dom/mobileconnection/tests/marionette/head.js

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:8ac59b0d332d
1 /* Any copyright is dedicated to the Public Domain.
2 * http://creativecommons.org/publicdomain/zero/1.0/ */
3
4 const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
5
6 const SETTINGS_KEY_DATA_ENABLED = "ril.data.enabled";
7 const SETTINGS_KEY_DATA_ROAMING_ENABLED = "ril.data.roaming_enabled";
8 const SETTINGS_KEY_DATA_APN_SETTINGS = "ril.data.apnSettings";
9
10 let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
11
12 let _pendingEmulatorCmdCount = 0;
13
14 /**
15 * Send emulator command with safe guard.
16 *
17 * We should only call |finish()| after all emulator command transactions
18 * end, so here comes with the pending counter. Resolve when the emulator
19 * gives positive response, and reject otherwise.
20 *
21 * Fulfill params:
22 * result -- an array of emulator response lines.
23 * Reject params:
24 * result -- an array of emulator response lines.
25 *
26 * @param aCommand
27 * A string command to be passed to emulator through its telnet console.
28 *
29 * @return A deferred promise.
30 */
31 function runEmulatorCmdSafe(aCommand) {
32 let deferred = Promise.defer();
33
34 ++_pendingEmulatorCmdCount;
35 runEmulatorCmd(aCommand, function(aResult) {
36 --_pendingEmulatorCmdCount;
37
38 ok(true, "Emulator response: " + JSON.stringify(aResult));
39 if (Array.isArray(aResult) &&
40 aResult[aResult.length - 1] === "OK") {
41 deferred.resolve(aResult);
42 } else {
43 deferred.reject(aResult);
44 }
45 });
46
47 return deferred.promise;
48 }
49
50 /**
51 * Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
52 *
53 * Fulfill params: A DOMEvent.
54 * Reject params: A DOMEvent.
55 *
56 * @param aRequest
57 * A DOMRequest instance.
58 *
59 * @return A deferred promise.
60 */
61 function wrapDomRequestAsPromise(aRequest) {
62 let deferred = Promise.defer();
63
64 ok(aRequest instanceof DOMRequest,
65 "aRequest is instanceof " + aRequest.constructor);
66
67 aRequest.addEventListener("success", function(aEvent) {
68 deferred.resolve(aEvent);
69 });
70 aRequest.addEventListener("error", function(aEvent) {
71 deferred.reject(aEvent);
72 });
73
74 return deferred.promise;
75 }
76
77 let workingFrame;
78
79 /**
80 * Get mozSettings value specified by @aKey.
81 *
82 * Resolve if that mozSettings value is retrieved successfully, reject
83 * otherwise.
84 *
85 * Fulfill params:
86 * The corresponding mozSettings value of the key.
87 * Reject params: (none)
88 *
89 * @param aKey
90 * A string.
91 * @param aAllowError [optional]
92 * A boolean value. If set to true, an error response won't be treated
93 * as test failure. Default: false.
94 *
95 * @return A deferred promise.
96 */
97 function getSettings(aKey, aAllowError) {
98 let request =
99 workingFrame.contentWindow.navigator.mozSettings.createLock().get(aKey);
100 return wrapDomRequestAsPromise(request)
101 .then(function resolve(aEvent) {
102 ok(true, "getSettings(" + aKey + ") - success");
103 return aEvent.target.result[aKey];
104 }, function reject(aEvent) {
105 ok(aAllowError, "getSettings(" + aKey + ") - error");
106 });
107 }
108
109 /**
110 * Set mozSettings values.
111 *
112 * Resolve if that mozSettings value is set successfully, reject otherwise.
113 *
114 * Fulfill params: (none)
115 * Reject params: (none)
116 *
117 * @param aSettings
118 * An object of format |{key1: value1, key2: value2, ...}|.
119 * @param aAllowError [optional]
120 * A boolean value. If set to true, an error response won't be treated
121 * as test failure. Default: false.
122 *
123 * @return A deferred promise.
124 */
125 function setSettings(aSettings, aAllowError) {
126 let request =
127 workingFrame.contentWindow.navigator.mozSettings.createLock().set(aSettings);
128 return wrapDomRequestAsPromise(request)
129 .then(function resolve() {
130 ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
131 }, function reject() {
132 ok(aAllowError, "setSettings(" + JSON.stringify(aSettings) + ")");
133 });
134 }
135
136 /**
137 * Set mozSettings value with only one key.
138 *
139 * Resolve if that mozSettings value is set successfully, reject otherwise.
140 *
141 * Fulfill params: (none)
142 * Reject params: (none)
143 *
144 * @param aKey
145 * A string key.
146 * @param aValue
147 * An object value.
148 * @param aAllowError [optional]
149 * A boolean value. If set to true, an error response won't be treated
150 * as test failure. Default: false.
151 *
152 * @return A deferred promise.
153 */
154 function setSettings1(aKey, aValue, aAllowError) {
155 let settings = {};
156 settings[aKey] = aValue;
157 return setSettings(settings, aAllowError);
158 }
159
160 /**
161 * Convenient MozSettings getter for SETTINGS_KEY_DATA_ENABLED.
162 */
163 function getDataEnabled(aAllowError) {
164 return getSettings(SETTINGS_KEY_DATA_ENABLED, aAllowError);
165 }
166
167 /**
168 * Convenient MozSettings setter for SETTINGS_KEY_DATA_ENABLED.
169 */
170 function setDataEnabled(aEnabled, aAllowError) {
171 return setSettings1(SETTINGS_KEY_DATA_ENABLED, aEnabled, aAllowError);
172 }
173
174 /**
175 * Convenient MozSettings getter for SETTINGS_KEY_DATA_ROAMING_ENABLED.
176 */
177 function getDataRoamingEnabled(aAllowError) {
178 return getSettings(SETTINGS_KEY_DATA_ROAMING_ENABLED, aAllowError);
179 }
180
181 /**
182 * Convenient MozSettings setter for SETTINGS_KEY_DATA_ROAMING_ENABLED.
183 */
184 function setDataRoamingEnabled(aEnabled, aAllowError) {
185 return setSettings1(SETTINGS_KEY_DATA_ROAMING_ENABLED, aEnabled, aAllowError);
186 }
187
188 /**
189 * Convenient MozSettings getter for SETTINGS_KEY_DATA_APN_SETTINGS.
190 */
191 function getDataApnSettings(aAllowError) {
192 return getSettings(SETTINGS_KEY_DATA_APN_SETTINGS, aAllowError);
193 }
194
195 /**
196 * Convenient MozSettings setter for SETTINGS_KEY_DATA_APN_SETTINGS.
197 */
198 function setDataApnSettings(aApnSettings, aAllowError) {
199 return setSettings1(SETTINGS_KEY_DATA_APN_SETTINGS, aApnSettings, aAllowError);
200 }
201
202 let mobileConnection;
203
204 /**
205 * Push required permissions and test if
206 * |navigator.mozMobileConnections[<aServiceId>]| exists. Resolve if it does,
207 * reject otherwise.
208 *
209 * Fulfill params:
210 * mobileConnection -- an reference to navigator.mozMobileMessage.
211 *
212 * Reject params: (none)
213 *
214 * @param aAdditonalPermissions [optional]
215 * An array of permission strings other than "mobileconnection" to be
216 * pushed. Default: empty string.
217 * @param aServiceId [optional]
218 * A numeric DSDS service id. Default: 0.
219 *
220 * @return A deferred promise.
221 */
222 function ensureMobileConnection(aAdditionalPermissions, aServiceId) {
223 let deferred = Promise.defer();
224
225 aAdditionalPermissions = aAdditionalPermissions || [];
226 aServiceId = aServiceId || 0;
227
228 if (aAdditionalPermissions.indexOf("mobileconnection") < 0) {
229 aAdditionalPermissions.push("mobileconnection");
230 }
231 let permissions = [];
232 for (let perm of aAdditionalPermissions) {
233 permissions.push({ "type": perm, "allow": 1, "context": document });
234 }
235
236 SpecialPowers.pushPermissions(permissions, function() {
237 ok(true, "permissions pushed: " + JSON.stringify(permissions));
238
239 // Permission changes can't change existing Navigator.prototype
240 // objects, so grab our objects from a new Navigator.
241 workingFrame = document.createElement("iframe");
242 workingFrame.addEventListener("load", function load() {
243 workingFrame.removeEventListener("load", load);
244
245 mobileConnection =
246 workingFrame.contentWindow.navigator.mozMobileConnections[aServiceId];
247
248 if (mobileConnection) {
249 log("navigator.mozMobileConnections[" + aServiceId + "] is instance of " +
250 mobileConnection.constructor);
251 } else {
252 log("navigator.mozMobileConnections[" + aServiceId + "] is undefined");
253 }
254
255 if (mobileConnection instanceof MozMobileConnection) {
256 deferred.resolve(mobileConnection);
257 } else {
258 deferred.reject();
259 }
260 });
261
262 document.body.appendChild(workingFrame);
263 });
264
265 return deferred.promise;
266 }
267
268 /**
269 * Wait for one named MobileConnection event.
270 *
271 * Resolve if that named event occurs. Never reject.
272 *
273 * Fulfill params: the DOMEvent passed.
274 *
275 * @param aEventName
276 * A string event name.
277 * @param aServiceId [optional]
278 * A numeric DSDS service id. Default: the one indicated in
279 * start*TestCommon() or 0 if not indicated.
280 *
281 * @return A deferred promise.
282 */
283 function waitForManagerEvent(aEventName, aServiceId) {
284 let deferred = Promise.defer();
285
286 let mobileConn = mobileConnection;
287 if (aServiceId !== undefined) {
288 mobileConn =
289 workingFrame.contentWindow.navigator.mozMobileConnections[aServiceId];
290 }
291
292 mobileConn.addEventListener(aEventName, function onevent(aEvent) {
293 mobileConn.removeEventListener(aEventName, onevent);
294
295 ok(true, "MobileConnection event '" + aEventName + "' got.");
296 deferred.resolve(aEvent);
297 });
298
299 return deferred.promise;
300 }
301
302 /**
303 * Get available networks.
304 *
305 * Fulfill params:
306 * An array of nsIDOMMozMobileNetworkInfo.
307 * Reject params:
308 * A DOMEvent.
309 *
310 * @return A deferred promise.
311 */
312 function getNetworks() {
313 let request = mobileConnection.getNetworks();
314 return wrapDomRequestAsPromise(request)
315 .then(() => request.result);
316 }
317
318 /**
319 * Manually select a network.
320 *
321 * Fulfill params: (none)
322 * Reject params:
323 * 'RadioNotAvailable', 'RequestNotSupported', or 'GenericFailure'
324 *
325 * @param aNetwork
326 * A nsIDOMMozMobileNetworkInfo.
327 *
328 * @return A deferred promise.
329 */
330 function selectNetwork(aNetwork) {
331 let request = mobileConnection.selectNetwork(aNetwork);
332 return wrapDomRequestAsPromise(request)
333 .then(null, () => { throw request.error });
334 }
335
336 /**
337 * Manually select a network and wait for a 'voicechange' event.
338 *
339 * Fulfill params: (none)
340 * Reject params:
341 * 'RadioNotAvailable', 'RequestNotSupported', or 'GenericFailure'
342 *
343 * @param aNetwork
344 * A nsIDOMMozMobileNetworkInfo.
345 *
346 * @return A deferred promise.
347 */
348 function selectNetworkAndWait(aNetwork) {
349 let promises = [];
350
351 promises.push(waitForManagerEvent("voicechange"));
352 promises.push(selectNetwork(aNetwork));
353
354 return Promise.all(promises);
355 }
356
357 /**
358 * Automatically select a network.
359 *
360 * Fulfill params: (none)
361 * Reject params:
362 * 'RadioNotAvailable', 'RequestNotSupported', or 'GenericFailure'
363 *
364 * @return A deferred promise.
365 */
366 function selectNetworkAutomatically() {
367 let request = mobileConnection.selectNetworkAutomatically();
368 return wrapDomRequestAsPromise(request)
369 .then(null, () => { throw request.error });
370 }
371
372 /**
373 * Automatically select a network and wait for a 'voicechange' event.
374 *
375 * Fulfill params: (none)
376 * Reject params:
377 * 'RadioNotAvailable', 'RequestNotSupported', or 'GenericFailure'
378 *
379 * @return A deferred promise.
380 */
381 function selectNetworkAutomaticallyAndWait() {
382 let promises = [];
383
384 promises.push(waitForManagerEvent("voicechange"));
385 promises.push(selectNetworkAutomatically());
386
387 return Promise.all(promises);
388 }
389
390 /**
391 * Set data connection enabling state and wait for "datachange" event.
392 *
393 * Resolve if data connection state changed to the expected one. Never reject.
394 *
395 * Fulfill params: (none)
396 *
397 * @param aEnabled
398 * A boolean state.
399 * @param aServiceId [optional]
400 * A numeric DSDS service id. Default: the one indicated in
401 * start*TestCommon() or 0 if not indicated.
402 *
403 * @return A deferred promise.
404 */
405 function setDataEnabledAndWait(aEnabled, aServiceId) {
406 let deferred = Promise.defer();
407
408 let promises = [];
409 promises.push(waitForManagerEvent("datachange", aServiceId));
410 promises.push(setDataEnabled(aEnabled));
411 Promise.all(promises).then(function keepWaiting() {
412 let mobileConn = mobileConnection;
413 if (aServiceId !== undefined) {
414 mobileConn =
415 workingFrame.contentWindow.navigator.mozMobileConnections[aServiceId];
416 }
417 // To ignore some transient states, we only resolve that deferred promise
418 // when the |connected| state equals to the expected one and never rejects.
419 let connected = mobileConn.data.connected;
420 if (connected == aEnabled) {
421 deferred.resolve();
422 return;
423 }
424
425 return waitForManagerEvent("datachange", aServiceId).then(keepWaiting);
426 });
427
428 return deferred.promise;
429 }
430
431 /**
432 * Set voice/data state and wait for state change.
433 *
434 * Fulfill params: (none)
435 *
436 * @param aWhich
437 * "voice" or "data".
438 * @param aState
439 * "unregistered", "searching", "denied", "roaming", or "home".
440 * @param aServiceId [optional]
441 * A numeric DSDS service id. Default: the one indicated in
442 * start*TestCommon() or 0 if not indicated.
443 *
444 * @return A deferred promise.
445 */
446 function setEmulatorVoiceDataStateAndWait(aWhich, aState, aServiceId) {
447 let promises = [];
448 promises.push(waitForManagerEvent(aWhich + "change", aServiceId));
449
450 let cmd = "gsm " + aWhich + " " + aState;
451 promises.push(runEmulatorCmdSafe(cmd));
452 return Promise.all(promises);
453 }
454
455 /**
456 * Set voice and data roaming emulation and wait for state change.
457 *
458 * Fulfill params: (none)
459 *
460 * @param aRoaming
461 * A boolean state.
462 * @param aServiceId [optional]
463 * A numeric DSDS service id. Default: the one indicated in
464 * start*TestCommon() or 0 if not indicated.
465 *
466 * @return A deferred promise.
467 */
468 function setEmulatorRoamingAndWait(aRoaming, aServiceId) {
469 function doSetAndWait(aWhich, aRoaming, aServiceId) {
470 let state = (aRoaming ? "roaming" : "home");
471 return setEmulatorVoiceDataStateAndWait(aWhich, state, aServiceId)
472 .then(() => {
473 let mobileConn = mobileConnection;
474 if (aServiceId !== undefined) {
475 mobileConn =
476 workingFrame.contentWindow.navigator.mozMobileConnections[aServiceId];
477 }
478 is(mobileConn[aWhich].roaming, aRoaming,
479 aWhich + ".roaming")
480 });
481 }
482
483 // Set voice registration state first and then data registration state.
484 return doSetAndWait("voice", aRoaming, aServiceId)
485 .then(() => doSetAndWait("data", aRoaming, aServiceId));
486 }
487
488 /**
489 * Get GSM location emulation.
490 *
491 * Fulfill params:
492 * { lac: <lac>, cid: <cid> }
493 * Reject params:
494 * result -- an array of emulator response lines.
495 *
496 * @return A deferred promise.
497 */
498 function getEmulatorGsmLocation() {
499 let cmd = "gsm location";
500 return runEmulatorCmdSafe(cmd)
501 .then(function(aResults) {
502 // lac: <lac>
503 // ci: <cid>
504 // OK
505 is(aResults[0].substring(0,3), "lac", "lac output");
506 is(aResults[1].substring(0,2), "ci", "ci output");
507
508 let lac = parseInt(aResults[0].substring(5));
509 lac = (lac < 0 ? 65535 : lac);
510 let cid = parseInt(aResults[1].substring(4));
511 cid = (cid < 0 ? 268435455 : cid);
512
513 return { lac: lac, cid: cid };
514 });
515 }
516
517 /**
518 * Set GSM location emulation.
519 *
520 * Fulfill params: (none)
521 * Reject params: (none)
522 *
523 * @param aLac
524 * @param aCid
525 *
526 * @return A deferred promise.
527 */
528 function setEmulatorGsmLocation(aLac, aCid) {
529 let cmd = "gsm location " + aLac + " " + aCid;
530 return runEmulatorCmdSafe(cmd);
531 }
532
533 /**
534 * Get emulator operators info.
535 *
536 * Fulfill params:
537 * An array of { longName: <string>, shortName: <string>, mccMnc: <string> }.
538 * Reject params:
539 * result -- an array of emulator response lines.
540 *
541 * @return A deferred promise.
542 */
543 function getEmulatorOperatorNames() {
544 let cmd = "operator dumpall";
545 return runEmulatorCmdSafe(cmd)
546 .then(function(aResults) {
547 let operators = [];
548
549 for (let i = 0; i < aResults.length - 1; i++) {
550 let names = aResults[i].split(',');
551 operators.push({
552 longName: names[0],
553 shortName: names[1],
554 mccMnc: names[2],
555 });
556 }
557
558 ok(true, "emulator operators list: " + JSON.stringify(operators));
559 return operators;
560 });
561 }
562
563 /**
564 * Set emulator operators info.
565 *
566 * Fulfill params: (none)
567 * Reject params:
568 * result -- an array of emulator response lines.
569 *
570 * @param aOperator
571 * "home" or "roaming".
572 * @param aLongName
573 * A string.
574 * @param aShortName
575 * A string.
576 * @param aMcc [optional]
577 * A string.
578 * @param aMnc [optional]
579 * A string.
580 *
581 * @return A deferred promise.
582 */
583 function setEmulatorOperatorNames(aOperator, aLongName, aShortName, aMcc, aMnc) {
584 const EMULATOR_OPERATORS = [ "home", "roaming" ];
585
586 let index = EMULATOR_OPERATORS.indexOf(aOperator);
587 if (index < 0) {
588 throw "invalid operator";
589 }
590
591 let cmd = "operator set " + index + " " + aLongName + "," + aShortName;
592 if (aMcc && aMnc) {
593 cmd = cmd + "," + aMcc + aMnc;
594 }
595 return runEmulatorCmdSafe(cmd)
596 .then(function(aResults) {
597 let exp = "^" + aLongName + "," + aShortName + ",";
598 if (aMcc && aMnc) {
599 cmd = cmd + aMcc + aMnc;
600 }
601
602 let re = new RegExp(exp);
603 ok(aResults[index].match(new RegExp(exp)),
604 "Long/short name and/or mcc/mnc should be changed.");
605 });
606 }
607
608 let _networkManager;
609
610 /**
611 * Get internal NetworkManager service.
612 */
613 function getNetworkManager() {
614 if (!_networkManager) {
615 _networkManager = Cc["@mozilla.org/network/manager;1"]
616 .getService(Ci.nsINetworkManager);
617 ok(_networkManager, "NetworkManager");
618 }
619
620 return _networkManager;
621 }
622
623 let _numOfRadioInterfaces;
624
625 /*
626 * Get number of radio interfaces. Default is 1 if preference is not set.
627 */
628 function getNumOfRadioInterfaces() {
629 if (!_numOfRadioInterfaces) {
630 try {
631 _numOfRadioInterfaces = SpecialPowers.getIntPref("ril.numRadioInterfaces");
632 } catch (ex) {
633 _numOfRadioInterfaces = 1; // Pref not set.
634 }
635 }
636
637 return _numOfRadioInterfaces;
638 }
639
640 /**
641 * Flush permission settings and call |finish()|.
642 */
643 function cleanUp() {
644 waitFor(function() {
645 SpecialPowers.flushPermissions(function() {
646 // Use ok here so that we have at least one test run.
647 ok(true, "permissions flushed");
648
649 finish();
650 });
651 }, function() {
652 return _pendingEmulatorCmdCount === 0;
653 });
654 }
655
656 /**
657 * Basic test routine helper for mobile connection tests.
658 *
659 * This helper does nothing but clean-ups.
660 *
661 * @param aTestCaseMain
662 * A function that takes no parameter.
663 */
664 function startTestBase(aTestCaseMain) {
665 Promise.resolve()
666 .then(aTestCaseMain)
667 .then(cleanUp, function() {
668 ok(false, 'promise rejects during test.');
669 cleanUp();
670 });
671 }
672
673 /**
674 * Common test routine helper for mobile connection tests.
675 *
676 * This function ensures global |mobileConnection| variable is available during
677 * the process and performs clean-ups as well.
678 *
679 * @param aTestCaseMain
680 * A function that takes one parameter -- mobileConnection.
681 * @param aAdditonalPermissions [optional]
682 * An array of permission strings other than "mobileconnection" to be
683 * pushed. Default: empty string.
684 * @param aServiceId [optional]
685 * A numeric DSDS service id. Default: 0.
686 */
687 function startTestCommon(aTestCaseMain, aAdditionalPermissions, aServiceId) {
688 startTestBase(function() {
689 return ensureMobileConnection(aAdditionalPermissions, aServiceId)
690 .then(aTestCaseMain);
691 });
692 }
693
694 /**
695 * Common test routine helper for multi-sim mobile connection tests. The test
696 * ends immediately if the device tested is not multi-sim.
697 *
698 * This function ensures global |mobileConnection| variable is available during
699 * the process and performs clean-ups as well.
700 *
701 * @param aTestCaseMain
702 * A function that takes one parameter -- mobileConnection.
703 * @param aAdditonalPermissions [optional]
704 * An array of permission strings other than "mobileconnection" to be
705 * pushed. Default: empty string.
706 * @param aServiceId [optional]
707 * A numeric DSDS service id. Default: 0.
708 */
709 function startDSDSTestCommon(aTestCaseMain, aAdditionalPermissions, aServiceId) {
710 if (getNumOfRadioInterfaces() > 1) {
711 startTestBase(function() {
712 return ensureMobileConnection(aAdditionalPermissions, aServiceId)
713 .then(aTestCaseMain);
714 });
715 } else {
716 log("Skipping DSDS tests on single SIM device.")
717 ok(true); // We should run at least one test.
718 cleanUp();
719 }
720 }

mercurial