diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 14d2d071cf2a..fe293bb6e4e0 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 242f5c11cc40..b39c5e8a339b 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index d93d41600b3d..c57e9b88a604 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 14d2d071cf2a..fe293bb6e4e0 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index e043faa74b65..5b7cdb264724 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "branch": "", "revision": "" }, - "revision": "c5bd933fe99317a7e99822f2d9345ae67a3043df", + "revision": "93de9c5bcb90ae32bd3599b185f4bbf67e4529ff", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 8d741c0638ba..6eba97b45284 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 1693f0f93c75..1b28a400c2d0 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index 30f9065f1762..44d04d6ac832 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index 09de64da9f3b..d1fbcd541464 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index d1da73c588bf..6deb2c5cbb71 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 75b35f1e6a9b..fc9275484961 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/dom/bluetooth/tests/marionette/head.js b/dom/bluetooth/tests/marionette/head.js index 2cbb53f97ec1..cb63a9e8df9e 100644 --- a/dom/bluetooth/tests/marionette/head.js +++ b/dom/bluetooth/tests/marionette/head.js @@ -9,12 +9,13 @@ let Promise = let bluetoothManager; -/* Get mozSettings value specified by @aKey. +/** + * Get mozSettings value specified by @aKey. * * Resolve if that mozSettings value is retrieved successfully, reject * otherwise. * - * Forfill params: + * Fulfill params: * The corresponding mozSettings value of the key. * Reject params: (none) * @@ -39,11 +40,12 @@ function getSettings(aKey) { return deferred.promise; } -/* Set mozSettings values. +/** + * Set mozSettings values. * * Resolve if that mozSettings value is set successfully, reject otherwise. * - * Forfill params: (none) + * Fulfill params: (none) * Reject params: (none) * * @param aSettings @@ -67,12 +69,13 @@ function setSettings(aSettings) { return deferred.promise; } -/* Get mozSettings value of 'bluetooth.enabled'. +/** + * Get mozSettings value of 'bluetooth.enabled'. * * Resolve if that mozSettings value is retrieved successfully, reject * otherwise. * - * Forfill params: + * Fulfill params: * A boolean value. * Reject params: (none) * @@ -82,11 +85,12 @@ function getBluetoothEnabled() { return getSettings("bluetooth.enabled"); } -/* Set mozSettings value of 'bluetooth.enabled'. +/** + * Set mozSettings value of 'bluetooth.enabled'. * * Resolve if that mozSettings value is set successfully, reject otherwise. * - * Forfill params: (none) + * Fulfill params: (none) * Reject params: (none) * * @param aEnabled @@ -100,10 +104,11 @@ function setBluetoothEnabled(aEnabled) { return setSettings(obj); } -/* Push required permissions and test if |navigator.mozBluetooth| exists. +/** + * Push required permissions and test if |navigator.mozBluetooth| exists. * Resolve if it does, reject otherwise. * - * Forfill params: + * Fulfill params: * bluetoothManager -- an reference to navigator.mozBluetooth. * Reject params: (none) * @@ -151,11 +156,12 @@ function ensureBluetoothManager(aPermissions) { return deferred.promise; } -/* Wait for one named BluetoothManager event. +/** + * Wait for one named BluetoothManager event. * * Resolve if that named event occurs. Never reject. * - * Forfill params: the DOMEvent passed. + * Fulfill params: the DOMEvent passed. * * @return A deferred promise. */ @@ -172,12 +178,13 @@ function waitForManagerEvent(aEventName) { return deferred.promise; } -/* Convenient function for setBluetoothEnabled and waitForManagerEvent +/** + * Convenient function for setBluetoothEnabled and waitForManagerEvent * combined. * * Resolve if that named event occurs. Reject if we can't set settings. * - * Forfill params: the DOMEvent passed. + * Fulfill params: the DOMEvent passed. * Reject params: (none) * * @return A deferred promise. @@ -198,11 +205,12 @@ function setBluetoothEnabledAndWait(aEnabled) { return Promise.all(promises); } -/* Get default adapter. +/** + * Get default adapter. * * Resolve if that default adapter is got, reject otherwise. * - * Forfill params: a BluetoothAdapter instance. + * Fulfill params: a BluetoothAdapter instance. * Reject params: a DOMError, or null if if there is no adapter ready yet. * * @return A deferred promise. @@ -237,7 +245,8 @@ function getDefaultAdapter() { return deferred.promise; } -/* Flush permission settings and call |finish()|. +/** + * Flush permission settings and call |finish()|. */ function cleanUp() { SpecialPowers.flushPermissions(function() { diff --git a/dom/mobileconnection/tests/marionette/head.js b/dom/mobileconnection/tests/marionette/head.js new file mode 100644 index 000000000000..623d8047903a --- /dev/null +++ b/dom/mobileconnection/tests/marionette/head.js @@ -0,0 +1,399 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers; + +const SETTINGS_KEY_DATA_ENABLED = "ril.data.enabled"; +const SETTINGS_KEY_DATA_ROAMING_ENABLED = "ril.data.roaming_enabled"; +const SETTINGS_KEY_DATA_APN_SETTINGS = "ril.data.apnSettings"; + +let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise; + +let _pendingEmulatorCmdCount = 0; + +/** + * Send emulator command with safe guard. + * + * We should only call |finish()| after all emulator command transactions + * end, so here comes with the pending counter. Resolve when the emulator + * gives positive response, and reject otherwise. + * + * Fulfill params: + * result -- an array of emulator response lines. + * Reject params: + * result -- an array of emulator response lines. + * + * @param aCommand + * A string command to be passed to emulator through its telnet console. + * + * @return A deferred promise. + */ +function runEmulatorCmdSafe(aCommand) { + let deferred = Promise.defer(); + + ++_pendingEmulatorCmdCount; + runEmulatorCmd(aCommand, function(aResult) { + --_pendingEmulatorCmdCount; + + ok(true, "Emulator response: " + JSON.stringify(aResult)); + if (Array.isArray(aResult) && aResult[0] === "OK") { + deferred.resolve(aResult); + } else { + deferred.reject(aResult); + } + }); + + return deferred.promise; +} + +/** + * Get mozSettings value specified by @aKey. + * + * Resolve if that mozSettings value is retrieved successfully, reject + * otherwise. + * + * Fulfill params: + * The corresponding mozSettings value of the key. + * Reject params: (none) + * + * @param aKey + * A string. + * @param aAllowError [optional] + * A boolean value. If set to true, an error response won't be treated + * as test failure. Default: false. + * + * @return A deferred promise. + */ +function getSettings(aKey, aAllowError) { + let deferred = Promise.defer(); + + let request = navigator.mozSettings.createLock().get(aKey); + request.addEventListener("success", function(aEvent) { + ok(true, "getSettings(" + aKey + ") - success"); + deferred.resolve(aEvent.target.result[aKey]); + }); + request.addEventListener("error", function() { + ok(aAllowError, "getSettings(" + aKey + ") - error"); + deferred.reject(); + }); + + return deferred.promise; +} + +/** + * Set mozSettings values. + * + * Resolve if that mozSettings value is set successfully, reject otherwise. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @param aSettings + * An object of format |{key1: value1, key2: value2, ...}|. + * @param aAllowError [optional] + * A boolean value. If set to true, an error response won't be treated + * as test failure. Default: false. + * + * @return A deferred promise. + */ +function setSettings(aSettings, aAllowError) { + let deferred = Promise.defer(); + + let request = navigator.mozSettings.createLock().set(aSettings); + request.addEventListener("success", function() { + ok(true, "setSettings(" + JSON.stringify(aSettings) + ")"); + deferred.resolve(); + }); + request.addEventListener("error", function() { + ok(aAllowError, "setSettings(" + JSON.stringify(aSettings) + ")"); + deferred.reject(); + }); + + return deferred.promise; +} + +/** + * Set mozSettings value with only one key. + * + * Resolve if that mozSettings value is set successfully, reject otherwise. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @param aKey + * A string key. + * @param aValue + * An object value. + * @param aAllowError [optional] + * A boolean value. If set to true, an error response won't be treated + * as test failure. Default: false. + * + * @return A deferred promise. + */ +function setSettings1(aKey, aValue, aAllowError) { + let settings = {}; + settings[aKey] = aValue; + return setSettings(settings, aAllowError); +} + +/** + * Convenient MozSettings getter for SETTINGS_KEY_DATA_ENABLED. + */ +function getDataEnabled(aAllowError) { + return getSettings(SETTINGS_KEY_DATA_ENABLED, aAllowError); +} + +/** + * Convenient MozSettings setter for SETTINGS_KEY_DATA_ENABLED. + */ +function setDataEnabled(aEnabled, aAllowError) { + return setSettings1(SETTINGS_KEY_DATA_ENABLED, aEnabled, aAllowError); +} + +/** + * Convenient MozSettings getter for SETTINGS_KEY_DATA_ROAMING_ENABLED. + */ +function getDataRoamingEnabled(aAllowError) { + return getSettings(SETTINGS_KEY_DATA_ROAMING_ENABLED, aAllowError); +} + +/** + * Convenient MozSettings setter for SETTINGS_KEY_DATA_ROAMING_ENABLED. + */ +function setDataRoamingEnabled(aEnabled, aAllowError) { + return setSettings1(SETTINGS_KEY_DATA_ROAMING_ENABLED, aEnabled, aAllowError); +} + +/** + * Convenient MozSettings getter for SETTINGS_KEY_DATA_APN_SETTINGS. + */ +function getDataApnSettings(aAllowError) { + return getSettings(SETTINGS_KEY_DATA_APN_SETTINGS, aAllowError); +} + +/** + * Convenient MozSettings setter for SETTINGS_KEY_DATA_APN_SETTINGS. + */ +function setDataApnSettings(aApnSettings, aAllowError) { + return setSettings1(SETTINGS_KEY_DATA_APN_SETTINGS, aApnSettings, aAllowError); +} + +let mobileConnection; + +/** + * Push required permissions and test if + * |navigator.mozMobileConnections[]| exists. Resolve if it does, + * reject otherwise. + * + * Fulfill params: + * mobileConnection -- an reference to navigator.mozMobileMessage. + * + * Reject params: (none) + * + * @param aAdditonalPermissions [optional] + * An array of permission strings other than "mobileconnection" to be + * pushed. Default: empty string. + * @param aServiceId [optional] + * A numeric DSDS service id. Default: 0. + * + * @return A deferred promise. + */ +function ensureMobileConnection(aAdditionalPermissions, aServiceId) { + let deferred = Promise.defer(); + + aAdditionalPermissions = aAdditionalPermissions || []; + aServiceId = aServiceId || 0; + + if (aAdditionalPermissions.indexOf("mobileconnection") < 0) { + aAdditionalPermissions.push("mobileconnection"); + } + let permissions = []; + for (let perm of aAdditionalPermissions) { + permissions.push({ "type": perm, "allow": 1, "context": document }); + } + + SpecialPowers.pushPermissions(permissions, function() { + ok(true, "permissions pushed: " + JSON.stringify(permissions)); + + // Permission changes can't change existing Navigator.prototype + // objects, so grab our objects from a new Navigator. + let ifr = document.createElement("iframe"); + ifr.addEventListener("load", function load() { + ifr.removeEventListener("load", load); + + mobileConnection = + ifr.contentWindow.navigator.mozMobileConnections[aServiceId]; + + if (mobileConnection) { + log("navigator.mozMobileConnections[" + aServiceId + "] is instance of " + + mobileConnection.constructor); + } else { + log("navigator.mozMobileConnections[" + aServiceId + "] is undefined"); + } + + if (mobileConnection instanceof MozMobileConnection) { + deferred.resolve(mobileConnection); + } else { + deferred.reject(); + } + }); + + document.body.appendChild(ifr); + }); + + return deferred.promise; +} + +/** + * Wait for one named MobileConnection event. + * + * Resolve if that named event occurs. Never reject. + * + * Fulfill params: the DOMEvent passed. + * + * @param aEventName + * A string event name. + * + * @return A deferred promise. + */ +function waitForManagerEvent(aEventName) { + let deferred = Promise.defer(); + + mobileConnection.addEventListener(aEventName, function onevent(aEvent) { + mobileConnection.removeEventListener(aEventName, onevent); + + ok(true, "MobileConnection event '" + aEventName + "' got."); + deferred.resolve(aEvent); + }); + + return deferred.promise; +} + +/** + * Set data connection enabling state and wait for "datachange" event. + * + * Resolve if data connection state changed to the expected one. Never reject. + * + * Fulfill params: (none) + * + * @param aEnabled + * A boolean state. + * + * @return A deferred promise. + */ +function setDataEnabledAndWait(aEnabled) { + let deferred = Promise.defer(); + + let promises = []; + promises.push(waitForManagerEvent("datachange")); + promises.push(setDataEnabled(aEnabled)); + Promise.all(promises).then(function keepWaiting() { + // To ignore some transient states, we only resolve that deferred promise + // when the |connected| state equals to the expected one and never rejects. + let connected = mobileConnection.data.connected; + if (connected == aEnabled) { + deferred.resolve(); + return; + } + + return waitForManagerEvent("datachange").then(keepWaiting); + }); + + return deferred.promise; +} + +/** + * Set voice/data roaming emulation and wait for state change. + * + * Fulfill params: (none) + * + * @param aRoaming + * A boolean state. + * + * @return A deferred promise. + */ +function setEmulatorRoamingAndWait(aRoaming) { + function doSetAndWait(aWhich, aRoaming) { + let promises = []; + promises.push(waitForManagerEvent(aWhich + "change")); + + let cmd = "gsm " + aWhich + " " + (aRoaming ? "roaming" : "home"); + promises.push(runEmulatorCmdSafe(cmd)); + return Promise.all(promises) + .then(() => is(mobileConnection[aWhich].roaming, aRoaming, + aWhich + ".roaming")); + } + + // Set voice registration state first and then data registration state. + return doSetAndWait("voice", aRoaming) + .then(() => doSetAndWait("data", aRoaming)); +} + +let _networkManager; + +/** + * Get internal NetworkManager service. + */ +function getNetworkManager() { + if (!_networkManager) { + _networkManager = Cc["@mozilla.org/network/manager;1"] + .getService(Ci.nsINetworkManager); + ok(_networkManager, "NetworkManager"); + } + + return _networkManager; +} + +/** + * Flush permission settings and call |finish()|. + */ +function cleanUp() { + waitFor(function() { + SpecialPowers.flushPermissions(function() { + // Use ok here so that we have at least one test run. + ok(true, "permissions flushed"); + + finish(); + }); + }, function() { + return _pendingEmulatorCmdCount === 0; + }); +} + +/** + * Basic test routine helper for mobile connection tests. + * + * This helper does nothing but clean-ups. + * + * @param aTestCaseMain + * A function that takes no parameter. + */ +function startTestBase(aTestCaseMain) { + Promise.resolve() + .then(aTestCaseMain) + .then(cleanUp, function() { + ok(false, 'promise rejects during test.'); + cleanUp(); + }); +} + +/** + * Common test routine helper for mobile connection tests. + * + * This function ensures global |mobileConnection| variable is available during + * the process and performs clean-ups as well. + * + * @param aTestCaseMain + * A function that takes one parameter -- mobileConnection. + * @param aAdditonalPermissions [optional] + * An array of permission strings other than "mobileconnection" to be + * pushed. Default: empty string. + * @param aServiceId [optional] + * A numeric DSDS service id. Default: 0. + */ +function startTestCommon(aTestCaseMain, aAdditionalPermissions, aServiceId) { + startTestBase(function() { + return ensureMobileConnection(aAdditionalPermissions, aServiceId) + .then(aTestCaseMain); + }); +} diff --git a/dom/mobileconnection/tests/marionette/manifest.ini b/dom/mobileconnection/tests/marionette/manifest.ini index 88134a44c139..07ad02777d69 100644 --- a/dom/mobileconnection/tests/marionette/manifest.ini +++ b/dom/mobileconnection/tests/marionette/manifest.ini @@ -23,3 +23,5 @@ disabled = Bug 808783 [test_mobile_last_known_network.js] [test_mobile_icc_change.js] [test_mobile_connections_array_uninitialized.js] +[test_mobile_data_ipv6.js] +disabled = Bug 978071 diff --git a/dom/mobileconnection/tests/marionette/test_mobile_data_ipv6.js b/dom/mobileconnection/tests/marionette/test_mobile_data_ipv6.js new file mode 100644 index 000000000000..bf83dfa48c50 --- /dev/null +++ b/dom/mobileconnection/tests/marionette/test_mobile_data_ipv6.js @@ -0,0 +1,112 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +/** + * Test resulting IP address format with given APN settings. + * + * This test utility function performs following steps: + * + * 1) set "ril.data.apnSettings" to a given settings object, + * 2) enable data connection and wait for a "datachange" event, + * 3) check the IP address type of the active network interface, + * 4) disable data connection. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @param aApnSettings + * An APN settings value. + * @param aIsIPv6 + * A boolean value indicating whether we're expecting an IPv6 address. + * + * @return A deferred promise. + */ +function doTest(aApnSettings, aIsIPv6) { + return setDataApnSettings([]) + .then(() => setDataApnSettings(aApnSettings)) + .then(() => setDataEnabledAndWait(true)) + .then(function() { + let nm = getNetworkManager(); + let active = nm.active; + ok(active, "Active network interface"); + + log(" Interface: " + active.name); + log(" Address: " + active.ip); + if (aIsIPv6) { + ok(active.ip.indexOf(":") > 0, "IPv6 address"); + } else { + ok(active.ip.indexOf(":") < 0, "IPv4 address"); + } + }) + .then(() => setDataEnabledAndWait(false)); +} + +function doTestHome(aApnSettings, aProtocol) { + log("Testing \"" + aProtocol + "\"@HOME... "); + + // aApnSettings is a double-array of per PDP context settings. The first + // index is a DSDS service ID, and the second one is the index of pre-defined + // PDP context settings of a specified radio interface. We use 0 for both as + // default here. + aApnSettings[0][0].protocol = aProtocol; + delete aApnSettings[0][0].roaming_protocol; + + return doTest(aApnSettings, aProtocol === "IPV6"); +} + +function doTestRoaming(aApnSettings, aRoaminProtocol) { + log("Testing \"" + aRoaminProtocol + "\"@ROMAING... "); + + // aApnSettings is a double-array of per PDP context settings. The first + // index is a DSDS service ID, and the second one is the index of pre-defined + // PDP context settings of a specified radio interface. We use 0 for both as + // default here. + delete aApnSettings[0][0].protocol; + aApnSettings[0][0].roaming_protocol = aRoaminProtocol; + + return doTest(aApnSettings, aRoaminProtocol === "IPV6"); +} + +startTestCommon(function() { + let origApnSettings; + + return setDataRoamingEnabled(true) + .then(getDataApnSettings) + .then(function(aResult) { + // If already set, then save original APN settings. + origApnSettings = JSON.parse(JSON.stringify(aResult)); + return aResult; + }, function() { + // Return our own default value. + return [[{ "carrier": "T-Mobile US", + "apn": "epc.tmobile.com", + "mmsc": "http://mms.msg.eng.t-mobile.com/mms/wapenc", + "types": ["default", "supl", "mms"] }]]; + }) + + .then(function(aApnSettings) { + return Promise.resolve() + + .then(() => doTestHome(aApnSettings, "NoSuchProtocol")) + .then(() => doTestHome(aApnSettings, "IP")) + .then(() => doTestHome(aApnSettings, "IPV6")) + + .then(() => setEmulatorRoamingAndWait(true)) + + .then(() => doTestRoaming(aApnSettings, "NoSuchProtocol")) + .then(() => doTestRoaming(aApnSettings, "IP")) + .then(() => doTestRoaming(aApnSettings, "IPV6")) + + .then(() => setEmulatorRoamingAndWait(false)); + }) + + .then(() => setDataRoamingEnabled(false)) + .then(function() { + if (origApnSettings) { + return setDataApnSettings(origApnSettings); + } + }); +}, ["settings-read", "settings-write"]); diff --git a/dom/mobilemessage/tests/marionette/head.js b/dom/mobilemessage/tests/marionette/head.js index fd2572e36074..2c9bba498f73 100644 --- a/dom/mobilemessage/tests/marionette/head.js +++ b/dom/mobilemessage/tests/marionette/head.js @@ -5,7 +5,8 @@ const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers; let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise; -/* Push required permissions and test if |navigator.mozMobileMessage| exists. +/** + * Push required permissions and test if |navigator.mozMobileMessage| exists. * Resolve if it does, reject otherwise. * * Fulfill params: @@ -44,7 +45,8 @@ function ensureMobileMessage() { return deferred.promise; } -/* Send a SMS message to a single receiver. Resolve if it succeeds, reject +/** + * Send a SMS message to a single receiver. Resolve if it succeeds, reject * otherwise. * * Fulfill params: @@ -72,7 +74,8 @@ function sendSmsWithSuccess(aReceiver, aText) { return deferred.promise; } -/* Send a MMS message with specified parameters. Resolve if it fails, reject +/** + * Send a MMS message with specified parameters. Resolve if it fails, reject * otherwise. * * Fulfill params: @@ -100,7 +103,8 @@ function sendMmsWithFailure(aMmsParameters) { return deferred.promise; } -/* Retrieve messages from database. +/** + * Retrieve messages from database. * * Fulfill params: * messages -- an array of {Sms,Mms}Message instances. @@ -136,7 +140,8 @@ function getMessages(aFilter, aReverse) { return deferred.promise; } -/* Retrieve all messages from database. +/** + * Retrieve all messages from database. * * Fulfill params: * messages -- an array of {Sms,Mms}Message instances. @@ -150,7 +155,8 @@ function getAllMessages() { return getMessages(null, false); } -/* Retrieve all threads from database. +/** + * Retrieve all threads from database. * * Fulfill params: * threads -- an array of MozMobileMessageThread instances. @@ -179,7 +185,8 @@ function getAllThreads() { return deferred.promise; } -/* Retrieve a single specified thread from database. +/** + * Retrieve a single specified thread from database. * * Fulfill params: * thread -- a MozMobileMessageThread instance. @@ -204,7 +211,8 @@ function getThreadById(aThreadId) { }); } -/* Delete messages specified from database. +/** + * Delete messages specified from database. * * Fulfill params: * result -- an array of boolean values indicating whether delesion was @@ -234,7 +242,8 @@ function deleteMessagesById(aMessageIds) { return deferred.promise; } -/* Delete messages specified from database. +/** + * Delete messages specified from database. * * Fulfill params: * result -- an array of boolean values indicating whether delesion was @@ -252,7 +261,8 @@ function deleteMessages(aMessages) { return deleteMessagesById(ids); } -/* Delete all messages from database. +/** + * Delete all messages from database. * * Fulfill params: * ids -- an array of numeric values identifying those deleted @@ -269,7 +279,8 @@ function deleteAllMessages() { let pendingEmulatorCmdCount = 0; -/* Send emulator command with safe guard. +/** + * Send emulator command with safe guard. * * We should only call |finish()| after all emulator command transactions * end, so here comes with the pending counter. Resolve when the emulator @@ -301,7 +312,8 @@ function runEmulatorCmdSafe(aCommand) { return deferred.promise; } -/* Send simple text SMS to emulator. +/** + * Send simple text SMS to emulator. * * Fulfill params: * result -- an array of emulator response lines. @@ -316,7 +328,8 @@ function sendTextSmsToEmulator(aFrom, aText) { return runEmulatorCmdSafe(command); } -/* Send raw SMS TPDU to emulator. +/** + * Send raw SMS TPDU to emulator. * * Fulfill params: * result -- an array of emulator response lines. @@ -331,7 +344,8 @@ function sendRawSmsToEmulator(aPdu) { return runEmulatorCmdSafe(command); } -/* Name space for MobileMessageDB.jsm. Only initialized after first call to +/** + * Name space for MobileMessageDB.jsm. Only initialized after first call to * newMobileMessageDB. */ let MMDB; @@ -348,7 +362,8 @@ function newMobileMessageDB() { return mmdb; } -/* Initialize a MobileMessageDB. Resolve if initialized with success, reject +/** + * Initialize a MobileMessageDB. Resolve if initialized with success, reject * otherwise. * * Fulfill params: a MobileMessageDB instance. @@ -378,7 +393,8 @@ function initMobileMessageDB(aMmdb, aDbName, aDbVersion) { return deferred.promise; } -/* Close a MobileMessageDB. +/** + * Close a MobileMessageDB. * * @return The passed MobileMessageDB instance. */ @@ -387,7 +403,8 @@ function closeMobileMessageDB(aMmdb) { return aMmdb; } -/* Create a new array of id attribute of input messages. +/** + * Create a new array of id attribute of input messages. * * @param aMessages an array of {Sms,Mms}Message instances. * @@ -404,7 +421,8 @@ function messagesToIds(aMessages) { // A reference to a nsIUUIDGenerator service. let uuidGenerator; -/* Generate a new UUID. +/** + * Generate a new UUID. * * @return A UUID string. */ @@ -418,7 +436,8 @@ function newUUID() { return uuidGenerator.generateUUID().toString(); } -/* Flush permission settings and call |finish()|. +/** + * Flush permission settings and call |finish()|. */ function cleanUp() { waitFor(function() { @@ -433,6 +452,14 @@ function cleanUp() { }); } +/** + * Basic test routine helper for mobile message tests. + * + * This helper does nothing but clean-ups. + * + * @param aTestCaseMain + * A function that takes no parameter. + */ function startTestBase(aTestCaseMain) { Promise.resolve() .then(aTestCaseMain) @@ -442,6 +469,15 @@ function startTestBase(aTestCaseMain) { }); } +/** + * Common test routine helper for mobile message tests. + * + * This function ensures global |manager| variable is available during the + * process and performs clean-ups as well. + * + * @param aTestCaseMain + * A function that takes no parameter. + */ function startTestCommon(aTestCaseMain) { startTestBase(function() { return ensureMobileMessage() diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index b09a0a64533e..5150c22304c0 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -52,6 +52,10 @@ let RILQUIRKS_DATA_REGISTRATION_ON_DEMAND = let RILQUIRKS_RADIO_OFF_WO_CARD = libcutils.property_get("ro.moz.ril.radio_off_wo_card", "false") == "true"; +// Ril quirk to enable IPv6 protocol/roaming protocol in APN settings. +let RILQUIRKS_HAVE_IPV6 = + libcutils.property_get("ro.moz.ril.ipv6", "false") == "true"; + const RADIOINTERFACELAYER_CID = Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}"); const RADIOINTERFACE_CID = @@ -4525,12 +4529,25 @@ RILNetworkInterface.prototype = { } authType = RIL.RIL_DATACALL_AUTH_TO_GECKO.indexOf(RIL.GECKO_DATACALL_AUTH_DEFAULT); } + let pdpType = RIL.GECKO_DATACALL_PDP_TYPE_IP; + if (RILQUIRKS_HAVE_IPV6) { + pdpType = !radioInterface.rilContext.data.roaming + ? this.apnSetting.protocol + : this.apnSetting.roaming_protocol; + if (RIL.RIL_DATACALL_PDP_TYPES.indexOf(pdpType) < 0) { + if (DEBUG) { + this.debug("Invalid pdpType '" + pdpType + "', using '" + + RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT + "'"); + } + pdpType = RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT; + } + } radioInterface.setupDataCall(radioTechnology, this.apnSetting.apn, this.apnSetting.user, this.apnSetting.password, authType, - "IP"); + pdpType); this.connecting = true; }, diff --git a/dom/system/gonk/ril_consts.js b/dom/system/gonk/ril_consts.js index 81b44114a05b..f0d070ed2ce6 100644 --- a/dom/system/gonk/ril_consts.js +++ b/dom/system/gonk/ril_consts.js @@ -2361,6 +2361,15 @@ this.RIL_DATACALL_AUTH_TO_GECKO = [ GECKO_DATACALL_AUTH_PAP_OR_CHAP // DATACALL_AUTH_PAP_OR_CHAP ]; +this.GECKO_DATACALL_PDP_TYPE_IP = "IP"; +this.GECKO_DATACALL_PDP_TYPE_IPV6 = "IPV6"; +this.GECKO_DATACALL_PDP_TYPE_DEFAULT = GECKO_DATACALL_PDP_TYPE_IP; +this.RIL_DATACALL_PDP_TYPES = [ + GECKO_DATACALL_PDP_TYPE_IP, + GECKO_DATACALL_PDP_TYPE_IPV6, + // TODO: Bug 978711 - Support IPV4V6 +]; + this.DATACALL_PROFILE_DEFAULT = 0; this.DATACALL_PROFILE_TETHERED = 1; this.DATACALL_PROFILE_OEM_BASE = 1000; diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index 94024ef52fdc..cbe07ee855e3 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -3962,8 +3962,9 @@ RilObject.prototype = { } currentDataCall.gw = updatedDataCall.gw; if (updatedDataCall.dns) { - currentDataCall.dns[0] = updatedDataCall.dns[0]; - currentDataCall.dns[1] = updatedDataCall.dns[1]; + currentDataCall.dns = updatedDataCall.dns.slice(); + } else { + currentDataCall.dns = []; } currentDataCall.rilMessageType = "datacallstatechange"; this.sendChromeMessage(currentDataCall); diff --git a/testing/xpcshell/mach_commands.py b/testing/xpcshell/mach_commands.py index 0ebc0105f200..0d56f615faf8 100644 --- a/testing/xpcshell/mach_commands.py +++ b/testing/xpcshell/mach_commands.py @@ -11,6 +11,7 @@ import logging import os import shutil import sys +import urllib2 from StringIO import StringIO @@ -26,6 +27,15 @@ from mach.decorators import ( Command, ) +ADB_NOT_FOUND = ''' +The %s command requires the adb binary to be on your path. + +If you have a B2G build, this can be found in +'%s/out/host//bin'. +'''.lstrip() + +BUSYBOX_URL = 'http://www.busybox.net/downloads/binaries/latest/busybox-armv7l' + if sys.version_info[0] < 3: unicode_type = unicode @@ -274,9 +284,99 @@ class AndroidXPCShellRunner(MozbuildObject): return int(not result) +class B2GXPCShellRunner(MozbuildObject): + def __init__(self, *args, **kwargs): + MozbuildObject.__init__(self, *args, **kwargs) + + # TODO Bug 794506 remove once mach integrates with virtualenv. + build_path = os.path.join(self.topobjdir, 'build') + if build_path not in sys.path: + sys.path.append(build_path) + + build_path = os.path.join(self.topsrcdir, 'build') + if build_path not in sys.path: + sys.path.append(build_path) + + self.tests_dir = os.path.join(self.topobjdir, '_tests') + self.xpcshell_dir = os.path.join(self.tests_dir, 'xpcshell') + self.bin_dir = os.path.join(self.distdir, 'bin') + + def _download_busybox(self, b2g_home): + system_bin = os.path.join(b2g_home, 'out', 'target', 'product', 'generic', 'system', 'bin') + busybox_path = os.path.join(system_bin, 'busybox') + + if os.path.isfile(busybox_path): + return busybox_path + + if not os.path.isdir(system_bin): + os.makedirs(system_bin) + + try: + data = urllib2.urlopen(BUSYBOX_URL) + except urllib2.URLError: + print('There was a problem downloading busybox. Proceeding without it,' \ + 'initial setup will be slow.') + return + + with open(busybox_path, 'wb') as f: + f.write(data.read()) + return busybox_path + + def run_test(self, test_file, b2g_home=None, busybox=None, + # ignore parameters from other platforms' options + **kwargs): + try: + import which + which.which('adb') + except which.WhichError: + # TODO Find adb automatically if it isn't on the path + print(ADB_NOT_FOUND % ('mochitest-remote', b2g_home)) + sys.exit(1) + + test_path = None + if test_file: + test_path = self._wrap_path_argument(test_file).relpath() + + import runtestsb2g + parser = runtestsb2g.B2GOptions() + options, args = parser.parse_args([]) + + options.b2g_path = b2g_home + options.busybox = busybox or os.environ.get('BUSYBOX') + options.emulator = 'arm' + options.localLib = self.bin_dir + options.localBin = self.bin_dir + options.logcat_dir = self.xpcshell_dir + options.manifest = os.path.join(self.xpcshell_dir, 'xpcshell_b2g.ini') + options.mozInfo = os.path.join(self.topobjdir, 'mozinfo.json') + options.objdir = self.topobjdir + options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols'), + options.testingModulesDir = os.path.join(self.tests_dir, 'modules') + options.testsRootDir = self.xpcshell_dir + options.testPath = test_path + options.use_device_libs = True + + if not options.busybox: + options.busybox = self._download_busybox(b2g_home) + + return runtestsb2g.run_remote_xpcshell(parser, options, args) + +def is_platform_supported(cls): + """Must have a Firefox, Android or B2G build.""" + return conditions.is_android(cls) or \ + conditions.is_b2g(cls) or \ + conditions.is_firefox(cls) + @CommandProvider class MachCommands(MachCommandBase): + def __init__(self, context): + MachCommandBase.__init__(self, context) + + for attr in ('b2g_home', 'device_name'): + setattr(self, attr, getattr(context, attr, None)) + @Command('xpcshell-test', category='testing', + conditions=[is_platform_supported], description='Run XPCOM Shell tests.') @CommandArgument('test_file', default='all', nargs='?', metavar='TEST', help='Test to run. Can be specified as a single JS file, a directory, ' @@ -313,6 +413,8 @@ class MachCommands(MachCommandBase): help='(Android) Do not copy files to device') @CommandArgument('--local-apk', type=str, default=None, help='(Android) Use specified Fennec APK') + @CommandArgument('--busybox', type=str, default=None, + help='(B2G) Path to busybox binary (speeds up installation of tests).') def run_xpcshell_test(self, **params): from mozbuild.controller.building import BuildDriver @@ -326,6 +428,9 @@ class MachCommands(MachCommandBase): if conditions.is_android(self): xpcshell = self._spawn(AndroidXPCShellRunner) + elif conditions.is_b2g(self): + xpcshell = self._spawn(B2GXPCShellRunner) + params['b2g_home'] = self.b2g_home else: xpcshell = self._spawn(XPCShellRunner) xpcshell.cwd = self._mach_context.cwd diff --git a/testing/xpcshell/runtestsb2g.py b/testing/xpcshell/runtestsb2g.py index 55e417b70b02..872982d3ae7c 100644 --- a/testing/xpcshell/runtestsb2g.py +++ b/testing/xpcshell/runtestsb2g.py @@ -153,9 +153,7 @@ class B2GOptions(RemoteXPCShellOptions): self.error("You must specify --emulator if you specify --logcat-dir") return RemoteXPCShellOptions.verifyRemoteOptions(self, options) -def main(): - parser = B2GOptions() - options, args = parser.parse_args() +def run_remote_xpcshell(parser, options, args): options = parser.verifyRemoteOptions(options) # Create the Marionette instance @@ -212,6 +210,11 @@ def main(): traceback.print_exc() sys.exit(1) +def main(): + parser = B2GOptions() + options, args = parser.parse_args() + + run_remote_xpcshell(parser, options, args) # You usually run this like : # python runtestsb2g.py --emulator arm --b2gpath $B2GPATH --manifest $MANIFEST [--xre-path $MOZ_HOST_BIN