summaryrefslogtreecommitdiffstats
path: root/dom/network/tests
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/network/tests
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/network/tests')
-rw-r--r--dom/network/tests/add_task.js83
-rw-r--r--dom/network/tests/chrome.ini12
-rw-r--r--dom/network/tests/file_udpsocket_iframe.html23
-rw-r--r--dom/network/tests/marionette/head.js552
-rw-r--r--dom/network/tests/marionette/manifest.ini13
-rw-r--r--dom/network/tests/marionette/test_ethernet_add_interface.js16
-rw-r--r--dom/network/tests/marionette/test_ethernet_connect_with_dhcp.js26
-rw-r--r--dom/network/tests/marionette/test_ethernet_connect_with_static_ip.js33
-rw-r--r--dom/network/tests/marionette/test_ethernet_disable.js17
-rw-r--r--dom/network/tests/marionette/test_ethernet_disconnect.js25
-rw-r--r--dom/network/tests/marionette/test_ethernet_enable.js17
-rw-r--r--dom/network/tests/marionette/test_ethernet_ip_mode_change.js43
-rw-r--r--dom/network/tests/marionette/test_ethernet_reconnect_with_dhcp.js29
-rw-r--r--dom/network/tests/marionette/test_ethernet_reconnect_with_static_ip.js36
-rw-r--r--dom/network/tests/marionette/test_ethernet_remove_interface.js16
-rw-r--r--dom/network/tests/mochitest.ini8
-rw-r--r--dom/network/tests/tcpsocket_test.jsm20
-rw-r--r--dom/network/tests/test_network_basics.html38
-rw-r--r--dom/network/tests/test_tcpsocket_client_and_server_basics.html51
-rw-r--r--dom/network/tests/test_tcpsocket_client_and_server_basics.js423
-rw-r--r--dom/network/tests/test_tcpsocket_default_permissions.html38
-rw-r--r--dom/network/tests/test_tcpsocket_enabled_no_perm.html30
-rw-r--r--dom/network/tests/test_tcpsocket_enabled_with_perm.html35
-rw-r--r--dom/network/tests/test_tcpsocket_jsm.html25
-rw-r--r--dom/network/tests/test_tcpsocket_legacy.html59
-rw-r--r--dom/network/tests/test_udpsocket.html405
-rw-r--r--dom/network/tests/unit_stats/test_networkstats_db.js1093
-rw-r--r--dom/network/tests/unit_stats/test_networkstats_service.js290
-rw-r--r--dom/network/tests/unit_stats/test_networkstats_service_proxy.js233
-rw-r--r--dom/network/tests/unit_stats/xpcshell.ini7
30 files changed, 3696 insertions, 0 deletions
diff --git a/dom/network/tests/add_task.js b/dom/network/tests/add_task.js
new file mode 100644
index 000000000..3028afdb7
--- /dev/null
+++ b/dom/network/tests/add_task.js
@@ -0,0 +1,83 @@
+// Temporary implementation of add_task for mochitest-plain until bug 1078657 is
+// implemented.
+SimpleTest.waitForExplicitFinish();
+(function(scope) {
+ var pendingTasks = [];
+ var pendingPromise = null;
+
+ // Strict spawn function that takes a known generatorFunc and assumes that
+ // every yielded value will be a Promise. If nesting is desired, then yield*
+ // should be used!
+ function spawn(generatorFunc) {
+ return new Promise(function(resolve, reject) {
+ try {
+ var iterator = generatorFunc();
+ }
+ catch (ex) {
+ ok(false, 'Problem invoking generator func: ' + ex + ': ' + ex.stack);
+ return;
+ }
+ var stepResolved = function(result) {
+ try {
+ var iterStep = iterator.next(result);
+ }
+ catch (ex) {
+ ok(false, 'Problem invoking iterator step: ' + ex + ': ' + ex.stack);
+ return;
+ }
+ if (iterStep.done) {
+ resolve(iterStep.value);
+ return;
+ }
+ if (!iterStep.value || !iterStep.value.then) {
+ ok(false, 'Iterator step returned non-Promise: ' + iterStep.value);
+ }
+ iterStep.value.then(stepResolved, generalErrback);
+ };
+ stepResolved();
+ });
+ }
+
+ function maybeSpawn(promiseOrGenerator) {
+ if (promiseOrGenerator.then) {
+ return promiseOrGenerator;
+ }
+ return spawn(promiseOrGenerator);
+ }
+
+ scope.add_task = function(thing) {
+ pendingTasks.push(thing);
+ };
+
+ function generalErrback(ex) {
+ ok(false,
+ 'A rejection happened: ' +
+ (ex ? (ex + ': ' + ex.stack) : ''));
+ }
+
+ function runNextTask() {
+ if (pendingTasks.length) {
+ pendingPromise = maybeSpawn(pendingTasks.shift());
+ pendingPromise.then(runNextTask, generalErrback);
+ } else {
+ SimpleTest.finish();
+ }
+ }
+
+ // Trigger runNextTask after we think all JS files have been loaded.
+ // The primary goal is that we can call SimpleTest.finish() after all test
+ // code has been loaded and run. We gate this based on the document's
+ // readyState.
+ var running = false;
+ function maybeStartRunning() {
+ if (!running && document.readyState === 'complete') {
+ running = true;
+ document.removeEventListener('readystateChange', maybeStartRunning);
+ // Defer to a subsequent turn of the event loop to let micro-tasks and any
+ // other clever setTimeout(0) instances run first.
+ window.setTimeout(runNextTask, 0);
+ }
+ }
+ document.addEventListener('readystatechange', maybeStartRunning);
+ maybeStartRunning();
+})(this);
diff --git a/dom/network/tests/chrome.ini b/dom/network/tests/chrome.ini
new file mode 100644
index 000000000..ca1c3f79a
--- /dev/null
+++ b/dom/network/tests/chrome.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+support-files =
+ tcpsocket_test.jsm
+ test_tcpsocket_client_and_server_basics.js
+ add_task.js
+ file_udpsocket_iframe.html
+
+[test_tcpsocket_jsm.html]
+[test_tcpsocket_client_and_server_basics.html]
+[test_tcpsocket_enabled_with_perm.html]
+[test_tcpsocket_legacy.html]
+[test_udpsocket.html]
diff --git a/dom/network/tests/file_udpsocket_iframe.html b/dom/network/tests/file_udpsocket_iframe.html
new file mode 100644
index 000000000..1e124552d
--- /dev/null
+++ b/dom/network/tests/file_udpsocket_iframe.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test UDPSocket BFCache</title>
+</head>
+<body>
+<script type="application/javascript;version=1.8">
+'use strict';
+window.addEventListener('load', function onload() {
+ window.removeEventListener('load', onload);
+ let remotePort = parseInt(window.location.search.substring(1), 10);
+ let socket = new UDPSocket();
+ socket.addEventListener('message', function () {
+ socket.send('fail', '127.0.0.1', remotePort);
+ });
+
+ socket.opened.then(function() {
+ socket.send('ready', '127.0.0.1', remotePort);
+ });
+});
+</script>
+</body>
+</html>
diff --git a/dom/network/tests/marionette/head.js b/dom/network/tests/marionette/head.js
new file mode 100644
index 000000000..edbf2dd85
--- /dev/null
+++ b/dom/network/tests/marionette/head.js
@@ -0,0 +1,552 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let Promise = SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
+
+const ETHERNET_MANAGER_CONTRACT_ID = "@mozilla.org/ethernetManager;1";
+
+const INTERFACE_UP = "UP";
+const INTERFACE_DOWN = "DOWN";
+
+let gTestSuite = (function() {
+ let suite = {};
+
+ // Private member variables of the returned object |suite|.
+ let ethernetManager = SpecialPowers.Cc[ETHERNET_MANAGER_CONTRACT_ID]
+ .getService(SpecialPowers.Ci.nsIEthernetManager);
+ let pendingEmulatorShellCount = 0;
+
+ /**
+ * Send emulator shell 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: an array of emulator response lines.
+ * Reject params: an array of emulator response lines.
+ *
+ * @param command
+ * A string command to be passed to emulator through its telnet console.
+ *
+ * @return A deferred promise.
+ */
+ function runEmulatorShellSafe(command) {
+ let deferred = Promise.defer();
+
+ ++pendingEmulatorShellCount;
+ runEmulatorShell(command, function(aResult) {
+ --pendingEmulatorShellCount;
+
+ ok(true, "Emulator shell response: " + JSON.stringify(aResult));
+ if (Array.isArray(aResult)) {
+ deferred.resolve(aResult);
+ } else {
+ deferred.reject(aResult);
+ }
+ });
+
+ return deferred.promise;
+ }
+
+ /**
+ * Get the system network conifg by the given interface name.
+ *
+ * Use shell command 'netcfg' to get the list of network cofig.
+ *
+ * Fulfill params: An object of { name, flag, ip }
+ *
+ * @parm ifname
+ * Interface name.
+ *
+ * @return A deferred promise.
+ */
+ function getNetworkConfig(ifname) {
+ return runEmulatorShellSafe(['netcfg'])
+ .then(result => {
+ // Sample 'netcfg' output:
+ //
+ // lo UP 127.0.0.1/8 0x00000049 00:00:00:00:00:00
+ // eth0 UP 10.0.2.15/24 0x00001043 52:54:00:12:34:56
+ // eth1 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:57
+ // rmnet1 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:59
+
+ let config;
+
+ for (let i = 0; i < result.length; i++) {
+ let tokens = result[i].split(/\s+/);
+ let name = tokens[0];
+ let flag = tokens[1];
+ let ip = tokens[2].split(/\/+/)[0];
+ if (name == ifname) {
+ config = { name: name, flag: flag, ip: ip };
+ break;
+ }
+ }
+
+ return config;
+ });
+ }
+
+ /**
+ * Get the ip assigned by dhcp server of a given interface name.
+ *
+ * Get the ip from android property 'dhcp.[ifname].ipaddress'.
+ *
+ * Fulfill params: A string of ip address.
+ *
+ * @parm ifname
+ * Interface name.
+ *
+ * @return A deferred promise.
+ */
+ function getDhcpIpAddr(ifname) {
+ return runEmulatorShellSafe(['getprop', 'dhcp.' + ifname + '.ipaddress'])
+ .then(function(ipAddr) {
+ return ipAddr[0];
+ });
+ }
+
+ /**
+ * Get the gateway assigned by dhcp server of a given interface name.
+ *
+ * Get the ip from android property 'dhcp.[ifname].gateway'.
+ *
+ * Fulfill params: A string of gateway.
+ *
+ * @parm ifname
+ * Interface name.
+ *
+ * @return A deferred promise.
+ */
+ function getDhcpGateway(ifname) {
+ return runEmulatorShellSafe(['getprop', 'dhcp.' + ifname + '.gateway'])
+ .then(function(gateway) {
+ return gateway[0];
+ });
+ }
+
+ /**
+ * Get the default route.
+ *
+ * Use shell command 'ip route' to get the default of device.
+ *
+ * Fulfill params: An array of { name, gateway }
+ *
+ * @return A deferred promise.
+ */
+ function getDefaultRoute() {
+ return runEmulatorShellSafe(['ip', 'route'])
+ .then(result => {
+ // Sample 'ip route' output:
+ //
+ // 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15
+ // default via 10.0.2.2 dev eth0 metric 2
+
+ let routeInfo = [];
+
+ for (let i = 0; i < result.length; i++) {
+ if (!result[i].match('default')) {
+ continue;
+ }
+
+ let tokens = result[i].split(/\s+/);
+ let name = tokens[4];
+ let gateway = tokens[2];
+ routeInfo.push({ name: name, gateway: gateway });
+ }
+
+ return routeInfo;
+ });
+ }
+
+ /**
+ * Check a specific interface is enabled or not.
+ *
+ * @parm ifname
+ * Interface name.
+ * @parm enabled
+ * A boolean value used to check interface is disable or not.
+ *
+ * @return A deferred promise.
+ */
+ function checkInterfaceIsEnabled(ifname, enabled) {
+ return getNetworkConfig(ifname)
+ .then(function(config) {
+ if (enabled) {
+ is(config.flag, INTERFACE_UP, "Interface is enabled as expected.");
+ } else {
+ is(config.flag, INTERFACE_DOWN, "Interface is disabled as expected.");
+ }
+ });
+ }
+
+ /**
+ * Check the ip of a specific interface is equal to given ip or not.
+ *
+ * @parm ifname
+ * Interface name.
+ * @parm ip
+ * Given ip address.
+ *
+ * @return A deferred promise.
+ */
+ function checkInterfaceIpAddr(ifname, ip) {
+ return getNetworkConfig(ifname)
+ .then(function(config) {
+ is(config.ip, ip, "IP is right as expected.");
+ });
+ }
+
+ /**
+ * Check the default gateway of a specific interface is equal to given gateway
+ * or not.
+ *
+ * @parm ifname
+ * Interface name.
+ * @parm gateway
+ * Given gateway.
+ *
+ * @return A deferred promise.
+ */
+ function checkDefaultRoute(ifname, gateway) {
+ return getDefaultRoute()
+ .then(function(routeInfo) {
+ for (let i = 0; i < routeInfo.length; i++) {
+ if (routeInfo[i].name == ifname) {
+ is(routeInfo[i].gateway, gateway,
+ "Default gateway is right as expected.");
+ return true;
+ }
+ }
+
+ if (!gateway) {
+ ok(true, "Default route is cleared.");
+ return true;
+ }
+
+ // TODO: should we ok(false, ......) here?
+ return false;
+ });
+ }
+
+ /**
+ * Check the length of interface list in EthernetManager is equal to given
+ * length or not.
+ *
+ * @parm length
+ * Given length.
+ */
+ function checkInterfaceListLength(length) {
+ let list = ethernetManager.interfaceList;
+ is(length, list.length, "List length is equal as expected.");
+ }
+
+ /**
+ * Check the given interface exists on device or not.
+ *
+ * @parm ifname
+ * Interface name.
+ *
+ * @return A deferred promise.
+ */
+ function checkInterfaceExist(ifname) {
+ return scanInterfaces()
+ .then(list => {
+ let index = list.indexOf(ifname);
+ if (index < 0) {
+ throw "Interface " + ifname + " not found.";
+ }
+
+ ok(true, ifname + " exists.");
+ });
+ }
+
+ /**
+ * Scan for available ethernet interfaces.
+ *
+ * Fulfill params: A list of available interfaces found in device.
+ *
+ * @return A deferred promise.
+ */
+ function scanInterfaces() {
+ let deferred = Promise.defer();
+
+ ethernetManager.scan(function onScan(list) {
+ deferred.resolve(list);
+ });
+
+ return deferred.promise;
+ }
+
+ /**
+ * Add an interface into interface list.
+ *
+ * Fulfill params: A boolean value indicates success or not.
+ *
+ * @param ifname
+ * Interface name.
+ *
+ * @return A deferred promise.
+ */
+ function addInterface(ifname) {
+ let deferred = Promise.defer();
+
+ ethernetManager.addInterface(ifname, function onAdd(success, message) {
+ ok(success, "Add interface " + ifname + " succeeded.");
+ is(message, "ok", "Message is as expected.");
+
+ deferred.resolve(success);
+ });
+
+ return deferred.promise;
+ }
+
+ /**
+ * Remove an interface form the interface list.
+ *
+ * Fulfill params: A boolean value indicates success or not.
+ *
+ * @param ifname
+ * Interface name.
+ *
+ * @return A deferred promise.
+ */
+ function removeInterface(ifname) {
+ let deferred = Promise.defer();
+
+ ethernetManager.removeInterface(ifname, function onRemove(success, message) {
+ ok(success, "Remove interface " + ifname + " succeeded.");
+ is(message, "ok", "Message is as expected.");
+
+ deferred.resolve(success);
+ });
+
+ return deferred.promise;
+ }
+
+ /**
+ * Enable networking of an interface in the interface list.
+ *
+ * Fulfill params: A boolean value indicates success or not.
+ *
+ * @param ifname
+ * Interface name.
+ *
+ * @return A deferred promise.
+ */
+ function enableInterface(ifname) {
+ let deferred = Promise.defer();
+
+ ethernetManager.enable(ifname, function onEnable(success, message) {
+ ok(success, "Enable interface " + ifname + " succeeded.");
+ is(message, "ok", "Message is as expected.");
+
+ deferred.resolve(success);
+ });
+
+ return deferred.promise;
+ }
+
+ /**
+ * Disable networking of an interface in the interface list.
+ *
+ * Fulfill params: A boolean value indicates success or not.
+ *
+ * @param ifname
+ * Interface name.
+ *
+ * @return A deferred promise.
+ */
+ function disableInterface(ifname) {
+ let deferred = Promise.defer();
+
+ ethernetManager.disable(ifname, function onDisable(success, message) {
+ ok(success, "Disable interface " + ifname + " succeeded.");
+ is(message, "ok", "Message is as expected.");
+
+ deferred.resolve(success);
+ });
+
+ return deferred.promise;
+ }
+
+ /**
+ * Make an interface connect to network.
+ *
+ * Fulfill params: A boolean value indicates success or not.
+ *
+ * @param ifname
+ * Interface name.
+ *
+ * @return A deferred promise.
+ */
+ function makeInterfaceConnect(ifname) {
+ let deferred = Promise.defer();
+
+ ethernetManager.connect(ifname, function onConnect(success, message) {
+ ok(success, "Interface " + ifname + " is connected successfully.");
+ is(message, "ok", "Message is as expected.");
+
+ deferred.resolve(success);
+ });
+
+ return deferred.promise;
+ }
+
+ /**
+ * Make an interface disconnect to network.
+ *
+ * Fulfill params: A boolean value indicates success or not.
+ *
+ * @param ifname
+ * Interface name.
+ *
+ * @return A deferred promise.
+ */
+ function makeInterfaceDisconnect(ifname) {
+ let deferred = Promise.defer();
+
+ ethernetManager.disconnect(ifname, function onDisconnect(success, message) {
+ ok(success, "Interface " + ifname + " is disconnected successfully.");
+ is(message, "ok", "Message is as expected.");
+
+ deferred.resolve(success);
+ });
+
+ return deferred.promise;
+ }
+
+ /**
+ * Update the config the an interface in the interface list.
+ *
+ * @param ifname
+ * Interface name.
+ * @param config
+ * .ip: ip address.
+ * .prefixLength: mask length.
+ * .gateway: gateway.
+ * .dnses: dnses.
+ * .httpProxyHost: http proxy host.
+ * .httpProxyPort: http porxy port.
+ * .usingDhcp: an boolean value indicates using dhcp or not.
+ *
+ * @return A deferred promise.
+ */
+ function updateInterfaceConfig(ifname, config) {
+ let deferred = Promise.defer();
+
+ ethernetManager.updateInterfaceConfig(ifname, config,
+ function onUpdated(success, message) {
+ ok(success, "Interface " + ifname + " config is updated successfully " +
+ "with " + JSON.stringify(config));
+ is(message, "ok", "Message is as expected.");
+
+ deferred.resolve(success);
+ });
+
+ return deferred.promise;
+ }
+
+ /**
+ * Wait for timeout.
+ *
+ * @param timeout
+ * Time in ms.
+ *
+ * @return A deferred promise.
+ */
+ function waitForTimeout(timeout) {
+ let deferred = Promise.defer();
+
+ setTimeout(function() {
+ ok(true, "waitForTimeout " + timeout);
+ deferred.resolve();
+ }, timeout);
+
+ return deferred.promise;
+ }
+
+ /**
+ * Wait for default route of a specific interface being set and
+ * check.
+ *
+ * @param ifname
+ * Interface name.
+ * @param gateway
+ * Target gateway.
+ *
+ * @return A deferred promise.
+ */
+ function waitForDefaultRouteSet(ifname, gateway) {
+ return gTestSuite.waitForTimeout(500)
+ .then(() => gTestSuite.checkDefaultRoute(ifname, gateway))
+ .then(success => {
+ if (success) {
+ ok(true, "Default route is set as expected." + gateway);
+ return;
+ }
+
+ ok(true, "Default route is not set yet, check again. " + success);
+ return waitForDefaultRouteSet(ifname, gateway);
+ });
+ }
+
+ //---------------------------------------------------
+ // Public test suite functions
+ //---------------------------------------------------
+ suite.scanInterfaces = scanInterfaces;
+ suite.addInterface = addInterface;
+ suite.removeInterface = removeInterface;
+ suite.enableInterface = enableInterface;
+ suite.disableInterface = disableInterface;
+ suite.makeInterfaceConnect = makeInterfaceConnect;
+ suite.makeInterfaceDisconnect = makeInterfaceDisconnect;
+ suite.updateInterfaceConfig = updateInterfaceConfig;
+ suite.getDhcpIpAddr = getDhcpIpAddr;
+ suite.getDhcpGateway = getDhcpGateway;
+ suite.checkInterfaceExist = checkInterfaceExist;
+ suite.checkInterfaceIsEnabled = checkInterfaceIsEnabled;
+ suite.checkInterfaceIpAddr = checkInterfaceIpAddr;
+ suite.checkDefaultRoute = checkDefaultRoute;
+ suite.checkInterfaceListLength = checkInterfaceListLength;
+ suite.waitForTimeout = waitForTimeout;
+ suite.waitForDefaultRouteSet = waitForDefaultRouteSet;
+
+ /**
+ * End up the test run.
+ *
+ * Wait until all pending emulator shell commands are done and then |finish|
+ * will be called in the end.
+ */
+ function cleanUp() {
+ waitFor(finish, function() {
+ return pendingEmulatorShellCount === 0;
+ });
+ }
+
+ /**
+ * Common test routine.
+ *
+ * Start a test with the given test case chain. The test environment will be
+ * settled down before the test. After the test, all the affected things will
+ * be restored.
+ *
+ * @param aTestCaseChain
+ * The test case entry point, which can be a function or a promise.
+ *
+ * @return A deferred promise.
+ */
+ suite.doTest = function(aTestCaseChain) {
+ return Promise.resolve()
+ .then(aTestCaseChain)
+ .then(function onresolve() {
+ cleanUp();
+ }, function onreject(aReason) {
+ ok(false, 'Promise rejects during test' + (aReason ? '(' + aReason + ')' : ''));
+ cleanUp();
+ });
+ };
+
+ return suite;
+})();
diff --git a/dom/network/tests/marionette/manifest.ini b/dom/network/tests/marionette/manifest.ini
new file mode 100644
index 000000000..23f184bae
--- /dev/null
+++ b/dom/network/tests/marionette/manifest.ini
@@ -0,0 +1,13 @@
+[DEFAULT]
+run-if = buildapp == 'b2g'
+
+[test_ethernet_add_interface.js]
+[test_ethernet_remove_interface.js]
+[test_ethernet_enable.js]
+[test_ethernet_disable.js]
+[test_ethernet_connect_with_dhcp.js]
+[test_ethernet_connect_with_static_ip.js]
+[test_ethernet_reconnect_with_dhcp.js]
+[test_ethernet_reconnect_with_static_ip.js]
+[test_ethernet_ip_mode_change.js]
+[test_ethernet_disconnect.js]
diff --git a/dom/network/tests/marionette/test_ethernet_add_interface.js b/dom/network/tests/marionette/test_ethernet_add_interface.js
new file mode 100644
index 000000000..d628e7705
--- /dev/null
+++ b/dom/network/tests/marionette/test_ethernet_add_interface.js
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const ETHERNET_INTERFACE_NAME = "eth1";
+
+gTestSuite.doTest(function() {
+ return Promise.resolve()
+ .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.checkInterfaceListLength(0))
+ .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.checkInterfaceListLength(1))
+ .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
+}); \ No newline at end of file
diff --git a/dom/network/tests/marionette/test_ethernet_connect_with_dhcp.js b/dom/network/tests/marionette/test_ethernet_connect_with_dhcp.js
new file mode 100644
index 000000000..57c2df9c2
--- /dev/null
+++ b/dom/network/tests/marionette/test_ethernet_connect_with_dhcp.js
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const ETHERNET_INTERFACE_NAME = "eth1";
+
+function checkDhcpResult(ifname) {
+ return gTestSuite.getDhcpIpAddr(ifname)
+ .then(ip => gTestSuite.checkInterfaceIpAddr(ifname, ip))
+ .then(() => gTestSuite.getDhcpGateway(ifname))
+ .then(gateway => gTestSuite.waitForDefaultRouteSet(ifname, gateway));
+}
+
+gTestSuite.doTest(function() {
+ return Promise.resolve()
+ .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
+ .then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
+}); \ No newline at end of file
diff --git a/dom/network/tests/marionette/test_ethernet_connect_with_static_ip.js b/dom/network/tests/marionette/test_ethernet_connect_with_static_ip.js
new file mode 100644
index 000000000..3adc37b23
--- /dev/null
+++ b/dom/network/tests/marionette/test_ethernet_connect_with_static_ip.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const ETHERNET_INTERFACE_NAME = "eth1";
+
+let staticConfig = {
+ ip: "1.2.3.4",
+ gateway: "1.2.3.5",
+ prefixLength: 24,
+ dnses: ["1.2.3.6"],
+ ipMode: "static"
+};
+
+function checkStaticResult(ifname) {
+ return gTestSuite.checkInterfaceIpAddr(ifname, staticConfig.ip)
+ .then(() => gTestSuite.checkDefaultRoute(ifname, staticConfig.gateway));
+}
+
+gTestSuite.doTest(function() {
+ return Promise.resolve()
+ .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, staticConfig))
+ .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
+ .then(() => checkStaticResult(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
+}); \ No newline at end of file
diff --git a/dom/network/tests/marionette/test_ethernet_disable.js b/dom/network/tests/marionette/test_ethernet_disable.js
new file mode 100644
index 000000000..9c3525faa
--- /dev/null
+++ b/dom/network/tests/marionette/test_ethernet_disable.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const ETHERNET_INTERFACE_NAME = "eth1";
+
+gTestSuite.doTest(function() {
+ return Promise.resolve()
+ .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.checkInterfaceIsEnabled(ETHERNET_INTERFACE_NAME, false))
+ .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
+}); \ No newline at end of file
diff --git a/dom/network/tests/marionette/test_ethernet_disconnect.js b/dom/network/tests/marionette/test_ethernet_disconnect.js
new file mode 100644
index 000000000..73f6aa3c5
--- /dev/null
+++ b/dom/network/tests/marionette/test_ethernet_disconnect.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const ETHERNET_INTERFACE_NAME = "eth1";
+const INTERFACE_IP_NONE = "0.0.0.0";
+
+function checkIpAddrIsReset(ifname) {
+ return gTestSuite.checkInterfaceIpAddr(ifname, INTERFACE_IP_NONE)
+ .then(() => gTestSuite.checkDefaultRoute(ifname));
+}
+
+gTestSuite.doTest(function() {
+ return Promise.resolve()
+ .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
+ .then(() => checkIpAddrIsReset(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
+}); \ No newline at end of file
diff --git a/dom/network/tests/marionette/test_ethernet_enable.js b/dom/network/tests/marionette/test_ethernet_enable.js
new file mode 100644
index 000000000..f5578a44f
--- /dev/null
+++ b/dom/network/tests/marionette/test_ethernet_enable.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const ETHERNET_INTERFACE_NAME = "eth1";
+
+gTestSuite.doTest(function() {
+ return Promise.resolve()
+ .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.checkInterfaceIsEnabled(ETHERNET_INTERFACE_NAME, true))
+ .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
+}); \ No newline at end of file
diff --git a/dom/network/tests/marionette/test_ethernet_ip_mode_change.js b/dom/network/tests/marionette/test_ethernet_ip_mode_change.js
new file mode 100644
index 000000000..5db2049be
--- /dev/null
+++ b/dom/network/tests/marionette/test_ethernet_ip_mode_change.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const ETHERNET_INTERFACE_NAME = "eth1";
+
+let staticConfig = {
+ ip: "1.2.3.4",
+ gateway: "1.2.3.5",
+ prefixLength: 24,
+ dnses: ["1.2.3.6"],
+ ipMode: "static"
+};
+
+function checkStaticResult(ifname) {
+ return gTestSuite.checkInterfaceIpAddr(ifname, staticConfig.ip)
+ .then(() => gTestSuite.waitForDefaultRouteSet(ifname, staticConfig.gateway));
+}
+
+function checkDhcpResult(ifname) {
+ return gTestSuite.getDhcpIpAddr(ifname)
+ .then(ip => gTestSuite.checkInterfaceIpAddr(ifname, ip))
+ .then(() => gTestSuite.getDhcpGateway(ifname))
+ .then(gateway => gTestSuite.waitForDefaultRouteSet(ifname, gateway));
+}
+
+gTestSuite.doTest(function() {
+ return Promise.resolve()
+ .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
+ .then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, staticConfig))
+ .then(() => checkStaticResult(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, { ipMode: "dhcp"}))
+ .then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
+}); \ No newline at end of file
diff --git a/dom/network/tests/marionette/test_ethernet_reconnect_with_dhcp.js b/dom/network/tests/marionette/test_ethernet_reconnect_with_dhcp.js
new file mode 100644
index 000000000..96719c152
--- /dev/null
+++ b/dom/network/tests/marionette/test_ethernet_reconnect_with_dhcp.js
@@ -0,0 +1,29 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const ETHERNET_INTERFACE_NAME = "eth1";
+
+function checkDhcpResult(ifname) {
+ return gTestSuite.getDhcpIpAddr(ifname)
+ .then(ip => gTestSuite.checkInterfaceIpAddr(ifname, ip))
+ .then(() => gTestSuite.getDhcpGateway(ifname))
+ .then(gateway => gTestSuite.waitForDefaultRouteSet(ifname, gateway));
+}
+
+gTestSuite.doTest(function() {
+ return Promise.resolve()
+ .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
+ .then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
+ .then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
+});
diff --git a/dom/network/tests/marionette/test_ethernet_reconnect_with_static_ip.js b/dom/network/tests/marionette/test_ethernet_reconnect_with_static_ip.js
new file mode 100644
index 000000000..91f25a471
--- /dev/null
+++ b/dom/network/tests/marionette/test_ethernet_reconnect_with_static_ip.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const ETHERNET_INTERFACE_NAME = "eth1";
+
+let staticConfig = {
+ ip: "1.2.3.4",
+ gateway: "1.2.3.5",
+ prefixLength: 24,
+ dnses: ["1.2.3.6"],
+ ipMode: "static"
+};
+
+function checkStaticResult(ifname) {
+ return gTestSuite.checkInterfaceIpAddr(ifname, staticConfig.ip)
+ .then(() => gTestSuite.checkDefaultRoute(ifname, staticConfig.gateway));
+}
+
+gTestSuite.doTest(function() {
+ return Promise.resolve()
+ .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, staticConfig))
+ .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
+ .then(() => checkStaticResult(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME))
+ .then(() => checkStaticResult(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME));
+}); \ No newline at end of file
diff --git a/dom/network/tests/marionette/test_ethernet_remove_interface.js b/dom/network/tests/marionette/test_ethernet_remove_interface.js
new file mode 100644
index 000000000..c7fb0e81b
--- /dev/null
+++ b/dom/network/tests/marionette/test_ethernet_remove_interface.js
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const ETHERNET_INTERFACE_NAME = "eth1";
+
+gTestSuite.doTest(function() {
+ return Promise.resolve()
+ .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.checkInterfaceListLength(1))
+ .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME))
+ .then(() => gTestSuite.checkInterfaceListLength(0));
+}); \ No newline at end of file
diff --git a/dom/network/tests/mochitest.ini b/dom/network/tests/mochitest.ini
new file mode 100644
index 000000000..76fd55fe4
--- /dev/null
+++ b/dom/network/tests/mochitest.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+ add_task.js
+
+[test_network_basics.html]
+skip-if = toolkit == 'android'
+[test_tcpsocket_default_permissions.html]
+[test_tcpsocket_enabled_no_perm.html]
diff --git a/dom/network/tests/tcpsocket_test.jsm b/dom/network/tests/tcpsocket_test.jsm
new file mode 100644
index 000000000..837a52262
--- /dev/null
+++ b/dom/network/tests/tcpsocket_test.jsm
@@ -0,0 +1,20 @@
+this.EXPORTED_SYMBOLS = [
+ 'createSocket', 'createServer', 'enablePrefsAndPermissions',
+ 'socketCompartmentInstanceOfArrayBuffer'];
+
+this.createSocket = function(host, port, options) {
+ return new TCPSocket(host, port, options);
+}
+
+this.createServer = function(port, options, backlog) {
+ return new TCPServerSocket(port, options, backlog);
+}
+
+this.enablePrefsAndPermissions = function() {
+ return false;
+}
+
+// See test_tcpsocket_client_and_server_basics.html's version for rationale.
+this.socketCompartmentInstanceOfArrayBuffer = function(obj) {
+ return obj instanceof ArrayBuffer;
+}
diff --git a/dom/network/tests/test_network_basics.html b/dom/network/tests/test_network_basics.html
new file mode 100644
index 000000000..e3c3eb25d
--- /dev/null
+++ b/dom/network/tests/test_network_basics.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Network API</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Network Information API **/
+function test() {
+ ok('connection' in navigator, "navigator.connection should exist");
+
+ ok(navigator.connection, "navigator.connection returns an object");
+
+ ok(navigator.connection instanceof EventTarget,
+ "navigator.connection is a EventTarget object");
+
+ ok('type' in navigator.connection,
+ "type should be a Connection attribute");
+ is(navigator.connection.type, "none",
+ "By default connection.type equals to none");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["dom.netinfo.enabled", true]]}, test);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/network/tests/test_tcpsocket_client_and_server_basics.html b/dom/network/tests/test_tcpsocket_client_and_server_basics.html
new file mode 100644
index 000000000..4d304a978
--- /dev/null
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Core tests for TCPSocket and TCPServerSocket that replace their previous
+separate xpcshell incarnations. This migration and cleanup occurred as part
+of bug 1084245 in order to get coverage of the tests from content.
+
+https://bugzilla.mozilla.org/show_bug.cgi?id=1084245
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1084245</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="add_task.js"></script>
+ <script type="application/javascript">
+ function createServer(port, options, backlog) {
+ return new TCPServerSocket(port, options, backlog);
+ }
+
+ function createSocket(host, port, options) {
+ return new TCPSocket(host, port, options);
+ }
+
+ function enablePrefsAndPermissions() {
+ return true;
+ }
+
+ // In the JSM case, ArrayBuffers will be created in the compartment of the
+ // JSM with different globals than the
+ // test_tcpsocket_client_and_server_basics.js test logic sees, so we (and
+ // tcpsocket_test.jsm) need to do something. To avoid complexity relating
+ // to wrappers and the varying nuances of the module scope and global scope
+ // in JSM's (they differ on B2G), we hardcode ArrayBuffer rather than taking
+ // a string that we look up, etc.
+ function socketCompartmentInstanceOfArrayBuffer(obj) {
+ return obj instanceof ArrayBuffer;
+ }
+ </script>
+ <script type="application/javascript;version=1.7" src="test_tcpsocket_client_and_server_basics.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1084245">Mozilla Bug 1084245</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/network/tests/test_tcpsocket_client_and_server_basics.js b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
new file mode 100644
index 000000000..f47689b90
--- /dev/null
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
@@ -0,0 +1,423 @@
+'use strict';
+
+const SERVER_BACKLOG = -1;
+
+const SOCKET_EVENTS = ['open', 'data', 'drain', 'error', 'close'];
+
+function concatUint8Arrays(a, b) {
+ let newArr = new Uint8Array(a.length + b.length);
+ newArr.set(a, 0);
+ newArr.set(b, a.length);
+ return newArr;
+}
+
+function assertUint8ArraysEqual(a, b, comparingWhat) {
+ if (a.length !== b.length) {
+ ok(false, comparingWhat + ' arrays do not have the same length; ' +
+ a.length + ' versus ' + b.length);
+ return;
+ }
+ for (let i = 0; i < a.length; i++) {
+ if (a[i] !== b[i]) {
+ ok(false, comparingWhat + ' arrays differ at index ' + i +
+ a[i] + ' versus ' + b[i]);
+ return;
+ }
+ }
+ ok(true, comparingWhat + ' arrays were equivalent.');
+}
+
+/**
+ * Helper method to add event listeners to a socket and provide two Promise-returning
+ * helpers (see below for docs on them). This *must* be called during the turn of
+ * the event loop where TCPSocket's constructor is called or the onconnect method is being
+ * invoked.
+ */
+function listenForEventsOnSocket(socket, socketType) {
+ let wantDataLength = null;
+ let wantDataAndClose = false;
+ let pendingResolve = null;
+ let receivedEvents = [];
+ let receivedData = null;
+ let handleGenericEvent = function(event) {
+ dump('(' + socketType + ' event: ' + event.type + ')\n');
+ if (pendingResolve && wantDataLength === null) {
+ pendingResolve(event);
+ pendingResolve = null;
+ } else {
+ receivedEvents.push(event);
+ }
+ };
+
+ socket.onopen = handleGenericEvent;
+ socket.ondrain = handleGenericEvent;
+ socket.onerror = handleGenericEvent;
+ socket.onclose = function(event) {
+ if (!wantDataAndClose) {
+ handleGenericEvent(event);
+ } else if (pendingResolve) {
+ dump('(' + socketType + ' event: close)\n');
+ pendingResolve(receivedData);
+ pendingResolve = null;
+ wantDataAndClose = false;
+ }
+ }
+ socket.ondata = function(event) {
+ dump('(' + socketType + ' event: ' + event.type + ' length: ' +
+ event.data.byteLength + ')\n');
+ ok(socketCompartmentInstanceOfArrayBuffer(event.data),
+ 'payload is ArrayBuffer');
+ var arr = new Uint8Array(event.data);
+ if (receivedData === null) {
+ receivedData = arr;
+ } else {
+ receivedData = concatUint8Arrays(receivedData, arr);
+ }
+ if (wantDataLength !== null &&
+ receivedData.length >= wantDataLength) {
+ pendingResolve(receivedData);
+ pendingResolve = null;
+ receivedData = null;
+ wantDataLength = null;
+ }
+ };
+
+
+ return {
+ /**
+ * Return a Promise that will be resolved with the next (non-data) event
+ * received by the socket. If there are queued events, the Promise will
+ * be immediately resolved (but you won't see that until a future turn of
+ * the event loop).
+ */
+ waitForEvent: function() {
+ if (pendingResolve) {
+ throw new Error('only one wait allowed at a time.');
+ }
+
+ if (receivedEvents.length) {
+ return Promise.resolve(receivedEvents.shift());
+ }
+
+ dump('(' + socketType + ' waiting for event)\n');
+ return new Promise(function(resolve, reject) {
+ pendingResolve = resolve;
+ });
+ },
+ /**
+ * Return a Promise that will be resolved with a Uint8Array of at least the
+ * given length. We buffer / accumulate received data until we have enough
+ * data. Data is buffered even before you call this method, so be sure to
+ * explicitly wait for any and all data sent by the other side.
+ */
+ waitForDataWithAtLeastLength: function(length) {
+ if (pendingResolve) {
+ throw new Error('only one wait allowed at a time.');
+ }
+ if (receivedData && receivedData.length >= length) {
+ let promise = Promise.resolve(receivedData);
+ receivedData = null;
+ return promise;
+ }
+ dump('(' + socketType + ' waiting for ' + length + ' bytes)\n');
+ return new Promise(function(resolve, reject) {
+ pendingResolve = resolve;
+ wantDataLength = length;
+ });
+ },
+ waitForAnyDataAndClose: function() {
+ if (pendingResolve) {
+ throw new Error('only one wait allowed at a time.');
+ }
+
+ return new Promise(function(resolve, reject) {
+ pendingResolve = resolve;
+ // we may receive no data before getting close, in which case we want to
+ // return an empty array
+ receivedData = new Uint8Array();
+ wantDataAndClose = true;
+ });
+ }
+ };
+}
+
+/**
+ * Return a promise that is resolved when the server receives a connection. The
+ * promise is resolved with { socket, queue } where `queue` is the result of
+ * calling listenForEventsOnSocket(socket). This must be done because we need
+ * to add the event listener during the connection.
+ */
+function waitForConnection(listeningServer) {
+ return new Promise(function(resolve, reject) {
+ // Because of the event model of sockets, we can't use the
+ // listenForEventsOnSocket mechanism; we need to hook up listeners during
+ // the connect event.
+ listeningServer.onconnect = function(event) {
+ // Clobber the listener to get upset if it receives any more connections
+ // after this.
+ listeningServer.onconnect = function() {
+ ok(false, 'Received a connection when not expecting one.');
+ };
+ ok(true, 'Listening server accepted socket');
+ resolve({
+ socket: event.socket,
+ queue: listenForEventsOnSocket(event.socket, 'server')
+ });
+ };
+ });
+}
+
+function defer() {
+ var deferred = {};
+ deferred.promise = new Promise(function(resolve, reject) {
+ deferred.resolve = resolve;
+ deferred.reject = reject;
+ });
+ return deferred;
+}
+
+
+function* test_basics() {
+ // See bug 903830; in e10s mode we never get to find out the localPort if we
+ // let it pick a free port by choosing 0. This is the same port the xpcshell
+ // test was using.
+ let serverPort = 8085;
+
+ // - Start up a listening socket.
+ let listeningServer = createServer(serverPort,
+ { binaryType: 'arraybuffer' },
+ SERVER_BACKLOG);
+
+ let connectedPromise = waitForConnection(listeningServer);
+
+ // -- Open a connection to the server
+ let clientSocket = createSocket('127.0.0.1', serverPort,
+ { binaryType: 'arraybuffer' });
+ let clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+
+ // (the client connects)
+ is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
+ is(clientSocket.readyState, 'open', 'client readyState is open');
+
+ // (the server connected)
+ let { socket: serverSocket, queue: serverQueue } = yield connectedPromise;
+ is(serverSocket.readyState, 'open', 'server readyState is open');
+
+ // -- Simple send / receive
+ // - Send data from client to server
+ // (But not so much we cross the drain threshold.)
+ let smallUint8Array = new Uint8Array(256);
+ for (let i = 0; i < smallUint8Array.length; i++) {
+ smallUint8Array[i] = i;
+ }
+ is(clientSocket.send(smallUint8Array.buffer, 0, smallUint8Array.length), true,
+ 'Client sending less than 64k, buffer should not be full.');
+
+ let serverReceived = yield serverQueue.waitForDataWithAtLeastLength(256);
+ assertUint8ArraysEqual(serverReceived, smallUint8Array,
+ 'Server received/client sent');
+
+ // - Send data from server to client
+ // (But not so much we cross the drain threshold.)
+ is(serverSocket.send(smallUint8Array.buffer, 0, smallUint8Array.length), true,
+ 'Server sending less than 64k, buffer should not be full.');
+
+ let clientReceived = yield clientQueue.waitForDataWithAtLeastLength(256);
+ assertUint8ArraysEqual(clientReceived, smallUint8Array,
+ 'Client received/server sent');
+
+ // -- Perform sending multiple times with different buffer slices
+ // - Send data from client to server
+ // (But not so much we cross the drain threshold.)
+ is(clientSocket.send(smallUint8Array.buffer, 0, 7),
+ true, 'Client sending less than 64k, buffer should not be full.');
+ is(clientSocket.send(smallUint8Array.buffer, 7, smallUint8Array.length - 7),
+ true, 'Client sending less than 64k, buffer should not be full.');
+
+ serverReceived = yield serverQueue.waitForDataWithAtLeastLength(256);
+ assertUint8ArraysEqual(serverReceived, smallUint8Array,
+ 'Server received/client sent');
+
+ // - Send data from server to client
+ // (But not so much we cross the drain threshold.)
+ is(serverSocket.send(smallUint8Array.buffer, 0, 7),
+ true, 'Server sending less than 64k, buffer should not be full.');
+ is(serverSocket.send(smallUint8Array.buffer, 7, smallUint8Array.length - 7),
+ true, 'Server sending less than 64k, buffer should not be full.');
+
+ clientReceived = yield clientQueue.waitForDataWithAtLeastLength(256);
+ assertUint8ArraysEqual(clientReceived, smallUint8Array,
+ 'Client received/server sent');
+
+
+ // -- Send "big" data in both directions
+ // (Enough to cross the buffering/drain threshold; 64KiB)
+ let bigUint8Array = new Uint8Array(65536 + 3);
+ for (let i = 0; i < bigUint8Array.length; i++) {
+ bigUint8Array[i] = i % 256;
+ }
+ // Do this twice so we have confidence that the 'drain' event machinery
+ // doesn't break after the first use.
+ for (let iSend = 0; iSend < 2; iSend++) {
+ // - Send "big" data from the client to the server
+ is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
+ 'Client sending more than 64k should result in the buffer being full.');
+ is((yield clientQueue.waitForEvent()).type, 'drain',
+ 'The drain event should fire after a large send that indicated full.');
+
+ serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
+ bigUint8Array.length);
+ assertUint8ArraysEqual(serverReceived, bigUint8Array,
+ 'server received/client sent');
+
+ // - Send "big" data from the server to the client
+ is(serverSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
+ 'Server sending more than 64k should result in the buffer being full.');
+ is((yield serverQueue.waitForEvent()).type, 'drain',
+ 'The drain event should fire after a large send that indicated full.');
+
+ clientReceived = yield clientQueue.waitForDataWithAtLeastLength(
+ bigUint8Array.length);
+ assertUint8ArraysEqual(clientReceived, bigUint8Array,
+ 'client received/server sent');
+ }
+
+ // -- Server closes the connection
+ serverSocket.close();
+ is(serverSocket.readyState, 'closing',
+ 'readyState should be closing immediately after calling close');
+
+ is((yield clientQueue.waitForEvent()).type, 'close',
+ 'The client should get a close event when the server closes.');
+ is(clientSocket.readyState, 'closed',
+ 'client readyState should be closed after close event');
+ is((yield serverQueue.waitForEvent()).type, 'close',
+ 'The server should get a close event when it closes itself.');
+ is(serverSocket.readyState, 'closed',
+ 'server readyState should be closed after close event');
+
+ // -- Re-establish connection
+ connectedPromise = waitForConnection(listeningServer);
+ clientSocket = createSocket('127.0.0.1', serverPort,
+ { binaryType: 'arraybuffer' });
+ clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+ is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
+
+ let connectedResult = yield connectedPromise;
+ // destructuring assignment is not yet ES6 compliant, must manually unpack
+ serverSocket = connectedResult.socket;
+ serverQueue = connectedResult.queue;
+
+ // -- Client closes the connection
+ clientSocket.close();
+ is(clientSocket.readyState, 'closing',
+ 'client readyState should be losing immediately after calling close');
+
+ is((yield clientQueue.waitForEvent()).type, 'close',
+ 'The client should get a close event when it closes itself.');
+ is(clientSocket.readyState, 'closed',
+ 'client readyState should be closed after the close event is received');
+ is((yield serverQueue.waitForEvent()).type, 'close',
+ 'The server should get a close event when the client closes.');
+ is(serverSocket.readyState, 'closed',
+ 'server readyState should be closed after the close event is received');
+
+
+ // -- Re-establish connection
+ connectedPromise = waitForConnection(listeningServer);
+ clientSocket = createSocket('127.0.0.1', serverPort,
+ { binaryType: 'arraybuffer' });
+ clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+ is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
+
+ connectedResult = yield connectedPromise;
+ // destructuring assignment is not yet ES6 compliant, must manually unpack
+ serverSocket = connectedResult.socket;
+ serverQueue = connectedResult.queue;
+
+ // -- Call close after enqueueing a lot of data, make sure it goes through.
+ // We'll have the client send and close.
+ is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
+ 'Client sending more than 64k should result in the buffer being full.');
+ clientSocket.close();
+ // The drain will still fire
+ is((yield clientQueue.waitForEvent()).type, 'drain',
+ 'The drain event should fire after a large send that returned true.');
+ // Then we'll get a close
+ is((yield clientQueue.waitForEvent()).type, 'close',
+ 'The close event should fire after the drain event.');
+
+ // The server will get its data
+ serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
+ bigUint8Array.length);
+ assertUint8ArraysEqual(serverReceived, bigUint8Array,
+ 'server received/client sent');
+ // And a close.
+ is((yield serverQueue.waitForEvent()).type, 'close',
+ 'The drain event should fire after a large send that returned true.');
+
+
+ // -- Re-establish connection
+ connectedPromise = waitForConnection(listeningServer);
+ clientSocket = createSocket('127.0.0.1', serverPort,
+ { binaryType: 'string' });
+ clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+ is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
+
+ connectedResult = yield connectedPromise;
+ // destructuring assignment is not yet ES6 compliant, must manually unpack
+ serverSocket = connectedResult.socket;
+ serverQueue = connectedResult.queue;
+
+ // -- Attempt to send non-string data.
+ // Restore the original behavior by replacing toString with
+ // Object.prototype.toString. (bug 1121938)
+ bigUint8Array.toString = Object.prototype.toString;
+ is(clientSocket.send(bigUint8Array), true,
+ 'Client sending a large non-string should only send a small string.');
+ clientSocket.close();
+ // The server will get its data
+ serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
+ bigUint8Array.toString().length);
+ // Then we'll get a close
+ is((yield clientQueue.waitForEvent()).type, 'close',
+ 'The close event should fire after the drain event.');
+
+ // -- Re-establish connection (Test for Close Immediately)
+ connectedPromise = waitForConnection(listeningServer);
+ clientSocket = createSocket('127.0.0.1', serverPort,
+ { binaryType: 'arraybuffer' });
+ clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+ is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
+
+ connectedResult = yield connectedPromise;
+ // destructuring assignment is not yet ES6 compliant, must manually unpack
+ serverSocket = connectedResult.socket;
+ serverQueue = connectedResult.queue;
+
+ // -- Attempt to send two non-string data.
+ is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
+ 'Server sending more than 64k should result in the buffer being full.');
+ is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
+ 'Server sending more than 64k should result in the buffer being full.');
+ clientSocket.closeImmediately();
+
+ serverReceived = yield serverQueue.waitForAnyDataAndClose();
+
+ is(serverReceived.length < (2 * bigUint8Array.length), true, 'Received array length less than sent array length');
+
+ // -- Close the listening server (and try to connect)
+ // We want to verify that the server actually closes / stops listening when
+ // we tell it to.
+ listeningServer.close();
+
+ // - try and connect, get an error
+ clientSocket = createSocket('127.0.0.1', serverPort,
+ { binaryType: 'arraybuffer' });
+ clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+ is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect');
+ is(clientSocket.readyState, 'closed',
+ 'client readyState should be closed after the failure to connect');
+}
+
+add_task(test_basics);
diff --git a/dom/network/tests/test_tcpsocket_default_permissions.html b/dom/network/tests/test_tcpsocket_default_permissions.html
new file mode 100644
index 000000000..19c44b7b9
--- /dev/null
+++ b/dom/network/tests/test_tcpsocket_default_permissions.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test to ensure TCPSocket permission is disabled by default</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test to ensure TCPSocket permission is disabled by default **/
+
+var caught = false;
+try {
+ new TCPSocket("localhost", 80, {})
+} catch (e) {
+ caught = true;
+}
+
+ok(caught, "TCPSocket should not exist by default");
+
+var caught = false;
+try {
+ navigator.mozTCPSocket.open("localhost", 80, {})
+} catch (e) {
+ caught = true;
+}
+
+ok(caught, "navigator.mozTCPSocket.open should not exist by default");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/network/tests/test_tcpsocket_enabled_no_perm.html b/dom/network/tests/test_tcpsocket_enabled_no_perm.html
new file mode 100644
index 000000000..ae1313113
--- /dev/null
+++ b/dom/network/tests/test_tcpsocket_enabled_no_perm.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test to ensure TCPSocket permission enabled and no tcp-socket perm does not allow open</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test to ensure TCPSocket preference being turned on does not enable
+ navigator.mozTCPSocket.
+**/
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [['dom.mozTCPSocket.enabled', true]]}, runTest);
+function runTest() {
+ is('TCPSocket' in this, false, "TCPSocket should not be accessible if dom.mozTCPSocket.enabled is true");
+ is('TCPServerSocket' in this, false, "TCPServerSocket should not be accessible if dom.mozTCPSocket.enabled is true");
+ is('mozTCPSocket' in navigator, false, "mozTCPSocket should not be accessible if dom.mozTCPSocket.enabled is true");
+
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/network/tests/test_tcpsocket_enabled_with_perm.html b/dom/network/tests/test_tcpsocket_enabled_with_perm.html
new file mode 100644
index 000000000..44b85ee3e
--- /dev/null
+++ b/dom/network/tests/test_tcpsocket_enabled_with_perm.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test to ensure TCPSocket permission enabled and open works with tcp-socket perm</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test to ensure TCPSocket permission being turned on enables
+ navigator.mozTCPSocket, and mozTCPSocket.open works when
+ the tcp-socket permission has been granted.
+**/
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [['dom.mozTCPSocket.enabled', true]]}, runTest);
+
+function runTest() {
+ ok('TCPSocket' in this, "TCPSocket should be accessible if dom.mozTCPSocket.enabled is true");
+
+ ok(new TCPSocket('localhost', 80), "TCPSocket constructor should work for content that has the tcp-socket permission");
+ ok(navigator.mozTCPSocket.open('localhost', 80), "navigator.mozTCPSocket.open should work for content that has the tcp-socket permission");
+ // This just helps the test harness clean up quickly
+ SpecialPowers.forceCC();
+ SpecialPowers.forceGC();
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/network/tests/test_tcpsocket_jsm.html b/dom/network/tests/test_tcpsocket_jsm.html
new file mode 100644
index 000000000..508c3c956
--- /dev/null
+++ b/dom/network/tests/test_tcpsocket_jsm.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for 1207090</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ Components.utils.import("chrome://mochitests/content/chrome/dom/network/tests/tcpsocket_test.jsm");
+ </script>
+ <script type="application/javascript" src="add_task.js"></script>
+ <script type="application/javascript;version=1.7" src="test_tcpsocket_client_and_server_basics.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1207090">Mozilla Bug 1207090</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<div id="container"></div>
+</body>
+</html>
diff --git a/dom/network/tests/test_tcpsocket_legacy.html b/dom/network/tests/test_tcpsocket_legacy.html
new file mode 100644
index 000000000..3b35583ab
--- /dev/null
+++ b/dom/network/tests/test_tcpsocket_legacy.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test of legacy navigator interface for opening TCPSocket/TCPServerSocket.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 885982</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1084245">Mozilla Bug 1084245</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<script>
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv(
+ { set: [ ['dom.mozTCPSocket.enabled', true] ] },
+ runTest);
+
+ function runTest() {
+ // See bug 903830; in e10s mode we never get to find out the localPort if we
+ // let it pick a free port by choosing 0. This is the same port the xpcshell
+ // test was using.
+ var serverPort = 8085;
+
+ var listeningServer = navigator.mozTCPSocket.listen(serverPort,
+ { binaryType: 'arraybuffer' },
+ -1);
+ listeningServer.onconnect = function(ev) {
+ ok(true, "got server connect");
+ listeningServer.close();
+ listeningServer = null;
+ ev.socket.close()
+ }
+
+ var clientSocket = navigator.mozTCPSocket.open('127.0.0.1', serverPort,
+ { binaryType: 'arraybuffer' });
+ clientSocket.onopen = function() { ok(true, "got client open"); }
+ clientSocket.onclose = function() {
+ ok(true, "got client close");
+ clientSocket.close();
+ clientSocket = null;
+ setTimeout(function() {
+ // This just helps the test harness clean up quickly
+ SpecialPowers.forceCC();
+ SpecialPowers.forceGC();
+ SimpleTest.finish();
+ }, 0);
+ }
+ }
+</script>
+</body>
+</html>
diff --git a/dom/network/tests/test_udpsocket.html b/dom/network/tests/test_udpsocket.html
new file mode 100644
index 000000000..f1d03824a
--- /dev/null
+++ b/dom/network/tests/test_udpsocket.html
@@ -0,0 +1,405 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test UDPSocket API</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<iframe id="iframe"></iframe>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+'use strict';
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+const HELLO_WORLD = 'hlo wrld. ';
+const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0];
+const DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length);
+const TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER);
+const BIG_ARRAY = new Array(4096);
+const BIG_ARRAY_BUFFER = new ArrayBuffer(BIG_ARRAY.length);
+const BIG_TYPED_ARRAY = new Uint8Array(BIG_ARRAY_BUFFER);
+
+for (let i = 0; i < BIG_ARRAY.length; i++) {
+ BIG_ARRAY[i] = Math.floor(Math.random() * 256);
+}
+
+TYPED_DATA_ARRAY.set(DATA_ARRAY);
+BIG_TYPED_ARRAY.set(BIG_ARRAY);
+
+function is_same_buffer(recv_data, expect_data) {
+ let recv_dataview = new Uint8Array(recv_data);
+ let expected_dataview = new Uint8Array(expect_data);
+
+ if (recv_dataview.length !== expected_dataview.length) {
+ return false;
+ }
+
+ for (let i = 0; i < recv_dataview.length; i++) {
+ if (recv_dataview[i] != expected_dataview[i]) {
+ info('discover byte differenct at ' + i);
+ return false;
+ }
+ }
+ return true;
+}
+
+function testOpen() {
+ info('test for creating an UDP Socket');
+ let socket = new UDPSocket();
+ is(socket.localPort, null, 'expect no local port before socket opened');
+ is(socket.localAddress, null, 'expect no local address before socket opened');
+ is(socket.remotePort, null, 'expected no default remote port');
+ is(socket.remoteAddress, null, 'expected no default remote address');
+ is(socket.readyState, 'opening', 'expected ready state = opening');
+ is(socket.loopback, false, 'expected no loopback');
+ is(socket.addressReuse, true, 'expect to reuse address');
+
+ return socket.opened.then(function() {
+ ok(true, 'expect openedPromise to be resolved after successful socket binding');
+ ok(!(socket.localPort === 0), 'expect allocated a local port');
+ is(socket.localAddress, '0.0.0.0', 'expect assigned to default address');
+ is(socket.readyState, 'open', 'expected ready state = open');
+
+ return socket;
+ });
+}
+
+function testSendString(socket) {
+ info('test for sending string data');
+
+ socket.send(HELLO_WORLD, '127.0.0.1', socket.localPort);
+
+ return new Promise(function(resolve, reject) {
+ socket.addEventListener('message', function recv_callback(msg) {
+ socket.removeEventListener('message', recv_callback);
+ let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
+ is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
+ is(recvData, HELLO_WORLD, 'expected same string data');
+ resolve(socket);
+ });
+ });
+}
+
+function testSendArrayBuffer(socket) {
+ info('test for sending ArrayBuffer');
+
+ socket.send(DATA_ARRAY_BUFFER, '127.0.0.1', socket.localPort);
+
+ return new Promise(function(resolve, reject) {
+ socket.addEventListener('message', function recv_callback(msg) {
+ socket.removeEventListener('message', recv_callback);
+ is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
+ ok(is_same_buffer(msg.data, DATA_ARRAY_BUFFER), 'expected same buffer data');
+ resolve(socket);
+ });
+ });
+}
+
+function testSendArrayBufferView(socket) {
+ info('test for sending ArrayBufferView');
+
+ socket.send(TYPED_DATA_ARRAY, '127.0.0.1', socket.localPort);
+
+ return new Promise(function(resolve, reject) {
+ socket.addEventListener('message', function recv_callback(msg) {
+ socket.removeEventListener('message', recv_callback);
+ is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
+ ok(is_same_buffer(msg.data, TYPED_DATA_ARRAY), 'expected same buffer data');
+ resolve(socket);
+ });
+ });
+}
+
+function testSendBlob(socket) {
+ info('test for sending Blob');
+
+ let blob = new Blob([HELLO_WORLD], {type : 'text/plain'});
+ socket.send(blob, '127.0.0.1', socket.localPort);
+
+ return new Promise(function(resolve, reject) {
+ socket.addEventListener('message', function recv_callback(msg) {
+ socket.removeEventListener('message', recv_callback);
+ let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
+ is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
+ is(recvData, HELLO_WORLD, 'expected same string data');
+ resolve(socket);
+ });
+ });
+}
+
+function testSendBigArray(socket) {
+ info('test for sending Big ArrayBuffer');
+
+ socket.send(BIG_TYPED_ARRAY, '127.0.0.1', socket.localPort);
+
+ return new Promise(function(resolve, reject) {
+ let byteReceived = 0;
+ socket.addEventListener('message', function recv_callback(msg) {
+ let byteBegin = byteReceived;
+ byteReceived += msg.data.byteLength;
+ is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
+ ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']');
+ if (byteReceived >= BIG_TYPED_ARRAY.length) {
+ socket.removeEventListener('message', recv_callback);
+ resolve(socket);
+ }
+ });
+ });
+}
+
+function testSendBigBlob(socket) {
+ info('test for sending Big Blob');
+
+ let blob = new Blob([BIG_TYPED_ARRAY]);
+ socket.send(blob, '127.0.0.1', socket.localPort);
+
+ return new Promise(function(resolve, reject) {
+ let byteReceived = 0;
+ socket.addEventListener('message', function recv_callback(msg) {
+ let byteBegin = byteReceived;
+ byteReceived += msg.data.byteLength;
+ is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
+ ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']');
+ if (byteReceived >= BIG_TYPED_ARRAY.length) {
+ socket.removeEventListener('message', recv_callback);
+ resolve(socket);
+ }
+ });
+ });
+}
+
+function testUDPOptions(socket) {
+ info('test for UDP init options');
+
+ let remoteSocket = new UDPSocket({addressReuse: false,
+ loopback: true,
+ localAddress: '127.0.0.1',
+ remoteAddress: '127.0.0.1',
+ remotePort: socket.localPort});
+ is(remoteSocket.localAddress, '127.0.0.1', 'expected local address');
+ is(remoteSocket.remoteAddress, '127.0.0.1', 'expected remote address');
+ is(remoteSocket.remotePort, socket.localPort, 'expected remote port');
+ is(remoteSocket.addressReuse, false, 'expected address not reusable');
+ is(remoteSocket.loopback, true, 'expected loopback mode is on');
+
+ return remoteSocket.opened.then(function() {
+ remoteSocket.send(HELLO_WORLD);
+ return new Promise(function(resolve, reject) {
+ socket.addEventListener('message', function recv_callback(msg) {
+ socket.removeEventListener('message', recv_callback);
+ let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
+ is(msg.remotePort, remoteSocket.localPort, 'expected packet from ' + remoteSocket.localPort);
+ is(recvData, HELLO_WORLD, 'expected same string data');
+ resolve(socket);
+ });
+ });
+ });
+}
+
+function testClose(socket) {
+ info('test for close');
+
+ socket.close();
+ is(socket.readyState, 'closed', 'expect ready state to be "closed"');
+ try {
+ socket.send(HELLO_WORLD, '127.0.0.1', socket.localPort);
+ ok(false, 'unexpect to send successfully');
+ } catch (e) {
+ ok(true, 'expected send fail after socket closed');
+ }
+
+ return socket.closed.then(function() {
+ ok(true, 'expected closedPromise is resolved after socket.close()');
+ });
+}
+
+function testMulticast() {
+ info('test for multicast');
+
+ let socket = new UDPSocket({loopback: true});
+
+ const MCAST_ADDRESS = '224.0.0.255';
+ socket.joinMulticastGroup(MCAST_ADDRESS);
+
+ return socket.opened.then(function() {
+ socket.send(HELLO_WORLD, MCAST_ADDRESS, socket.localPort);
+
+ return new Promise(function(resolve, reject) {
+ socket.addEventListener('message', function recv_callback(msg) {
+ socket.removeEventListener('message', recv_callback);
+ let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
+ is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
+ is(recvData, HELLO_WORLD, 'expected same string data');
+ socket.leaveMulticastGroup(MCAST_ADDRESS);
+ resolve();
+ });
+ });
+ });
+}
+
+function testInvalidUDPOptions() {
+ info('test for invalid UDPOptions');
+ try {
+ let socket = new UDPSocket({localAddress: 'not-a-valid-address'});
+ ok(false, 'should not create an UDPSocket with an invalid localAddress');
+ } catch (e) {
+ is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localAddress is not a valid IPv4/6 address');
+ }
+
+ try {
+ let socket = new UDPSocket({localPort: 0});
+ ok(false, 'should not create an UDPSocket with an invalid localPort');
+ } catch (e) {
+ is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localPort is not a valid port number');
+ }
+
+ try {
+ let socket = new UDPSocket({remotePort: 0});
+ ok(false, 'should not create an UDPSocket with an invalid remotePort');
+ } catch (e) {
+ is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localPort is not a valid port number');
+ }
+}
+
+function testOpenFailed() {
+ info('test for falied on open');
+
+ //according to RFC5737, address block 192.0.2.0/24 should not be used in both local and public contexts
+ let socket = new UDPSocket({localAddress: '192.0.2.0'});
+
+ return socket.opened.then(function() {
+ ok(false, 'should not resolve openedPromise while fail to bind socket');
+ socket.close();
+ }).catch(function(reason) {
+ is(reason.name, 'NetworkError', 'expected openedPromise to be rejected while fail to bind socket');
+ });
+}
+
+function testSendBeforeOpen() {
+ info('test for send before open');
+
+ let socket = new UDPSocket();
+
+ try {
+ socket.send(HELLO_WORLD, '127.0.0.1', 9);
+ ok(false, 'unexpect to send successfully');
+ } catch (e) {
+ ok(true, 'expected send fail before openedPromise is resolved');
+ }
+
+ return socket.opened.then(function() {
+ socket.close();
+ });
+}
+
+function testCloseBeforeOpened() {
+ info('test for close socket before opened');
+
+ let socket = new UDPSocket();
+ socket.opened.then(function() {
+ ok(false, 'should not resolve openedPromise if it has already been closed');
+ }).catch(function(reason) {
+ is(reason.name, 'AbortError', 'expected openedPromise to be rejected while socket is closed during opening');
+ });
+
+ return socket.close().then(function() {
+ ok(true, 'expected closedPromise to be resolved');
+ }).then(socket.opened);
+}
+
+function testOpenWithoutClose() {
+ info('test for open without close');
+
+ let closed = [];
+ for (let i = 0; i < 50; i++) {
+ let socket = new UDPSocket();
+ closed.push(socket.closed);
+ }
+
+ SpecialPowers.gc();
+ info('all unrefereced socket should be closed right after GC');
+
+ return Promise.all(closed);
+}
+
+function testBFCache() {
+ info('test for bfcache behavior');
+
+ let socket = new UDPSocket();
+
+ return socket.opened.then(function() {
+ let iframe = document.getElementById('iframe');
+ SpecialPowers.wrap(iframe).mozbrowser = true;
+ iframe.src = 'file_udpsocket_iframe.html?' + socket.localPort;
+
+ return new Promise(function(resolve, reject) {
+ socket.addEventListener('message', function recv_callback(msg) {
+ socket.removeEventListener('message', recv_callback);
+ iframe.src = 'about:blank';
+ iframe.addEventListener('load', function onload() {
+ iframe.removeEventListener('load', onload);
+ socket.send(HELLO_WORLD, '127.0.0.1', msg.remotePort);
+
+ function recv_again_callback(msg) {
+ socket.removeEventListener('message', recv_again_callback);
+ ok(false, 'should not receive packet after page unload');
+ }
+
+ socket.addEventListener('message', recv_again_callback);
+
+ let timeout = setTimeout(function() {
+ socket.removeEventListener('message', recv_again_callback);
+ socket.close();
+ resolve();
+ }, 5000);
+ });
+ });
+ });
+ });
+}
+
+function runTest() {
+ testOpen()
+ .then(testSendString)
+ .then(testSendArrayBuffer)
+ .then(testSendArrayBufferView)
+ .then(testSendBlob)
+ .then(testSendBigArray)
+ .then(testSendBigBlob)
+ .then(testUDPOptions)
+ .then(testClose)
+ .then(testMulticast)
+ .then(testInvalidUDPOptions)
+ .then(testOpenFailed)
+ .then(testSendBeforeOpen)
+ .then(testCloseBeforeOpened)
+ .then(testOpenWithoutClose)
+ .then(testBFCache)
+ .then(function() {
+ info('test finished');
+ SimpleTest.finish();
+ })
+ .catch(function(err) {
+ ok(false, 'test failed due to: ' + err);
+ SimpleTest.finish();
+ });
+}
+
+window.addEventListener('load', function () {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.udpsocket.enabled', true],
+ ['browser.sessionhistory.max_total_viewers', 10]
+ ]
+ }, runTest);
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/network/tests/unit_stats/test_networkstats_db.js b/dom/network/tests/unit_stats/test_networkstats_db.js
new file mode 100644
index 000000000..50a5dc186
--- /dev/null
+++ b/dom/network/tests/unit_stats/test_networkstats_db.js
@@ -0,0 +1,1093 @@
+/* Any: copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
+
+const STATS_STORE_NAME = "net_stats_store_v3";
+const netStatsDb = new NetworkStatsDB();
+
+function clearStore(store, callback) {
+ netStatsDb.dbNewTxn(store, "readwrite", function(aTxn, aStore) {
+ aStore.openCursor().onsuccess = function (event) {
+ let cursor = event.target.result;
+ if (cursor){
+ cursor.delete();
+ cursor.continue();
+ }
+ };
+ }, callback);
+}
+
+function getNetworkId(aIccId, aNetworkType) {
+ return aIccId + '' + aNetworkType;
+}
+
+add_test(function prepareDatabase() {
+ // Clear whole database to avoid starting tests with unknown state
+ // due to the previous tests.
+ clearStore(STATS_STORE_NAME, function() {
+ clearStore('net_alarm', function() {
+ run_next_test();
+ });
+ });
+});
+
+function filterTimestamp(date) {
+ var sampleRate = netStatsDb.sampleRate;
+ var offset = date.getTimezoneOffset() * 60 * 1000;
+ return Math.floor((date.getTime() - offset) / sampleRate) * sampleRate;
+}
+
+function getNetworks() {
+ return [{ id: '0', type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI },
+ { id: '1234', type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE }];
+}
+
+function compareNetworks(networkA, networkB) {
+ return (networkA[0] == networkB[0] && networkA[1] == networkB[1]);
+}
+
+add_test(function test_sampleRate() {
+ var sampleRate = netStatsDb.sampleRate;
+ do_check_true(sampleRate > 0);
+ netStatsDb.sampleRate = 0;
+ sampleRate = netStatsDb.sampleRate;
+ do_check_true(sampleRate > 0);
+
+ run_next_test();
+});
+
+add_test(function test_maxStorageSamples() {
+ var maxStorageSamples = netStatsDb.maxStorageSamples;
+ do_check_true(maxStorageSamples > 0);
+ netStatsDb.maxStorageSamples = 0;
+ maxStorageSamples = netStatsDb.maxStorageSamples;
+ do_check_true(maxStorageSamples > 0);
+
+ run_next_test();
+});
+
+add_test(function test_fillResultSamples_emptyData() {
+ var samples = 3;
+ var data = [];
+ var start = filterTimestamp(new Date());
+ var sampleRate = netStatsDb.sampleRate;
+ var end = start + (sampleRate * samples);
+ netStatsDb.fillResultSamples(start, end, data);
+ do_check_eq(data.length, samples + 1);
+
+ var aux = start;
+ var success = true;
+ for (var i = 0; i <= samples; i++) {
+ if (data[i].date.getTime() != aux || data[i].rxBytes != undefined || data[i].txBytes != undefined) {
+ success = false;
+ break;
+ }
+ aux += sampleRate;
+ }
+ do_check_true(success);
+
+ run_next_test();
+});
+
+add_test(function test_fillResultSamples_noEmptyData() {
+ var samples = 3;
+ var sampleRate = netStatsDb.sampleRate;
+ var start = filterTimestamp(new Date());
+ var end = start + (sampleRate * samples);
+ var data = [{date: new Date(start + sampleRate),
+ rxBytes: 0,
+ txBytes: 0}];
+ netStatsDb.fillResultSamples(start, end, data);
+ do_check_eq(data.length, samples + 1);
+
+ var aux = start;
+ var success = true;
+ for (var i = 0; i <= samples; i++) {
+ if (i == 1) {
+ if (data[i].date.getTime() != aux || data[i].rxBytes != 0 || data[i].txBytes != 0) {
+ success = false;
+ break;
+ }
+ } else {
+ if (data[i].date.getTime() != aux || data[i].rxBytes != undefined || data[i].txBytes != undefined) {
+ success = false;
+ break;
+ }
+ }
+ aux += sampleRate;
+ }
+ do_check_true(success);
+
+ run_next_test();
+});
+
+add_test(function test_clear() {
+ var networks = getNetworks();
+ networks.forEach(function(network, index) {
+ networks[index] = {network: network, networkId: getNetworkId(network.id, network.type)};
+ }, this);
+
+ netStatsDb.clearStats(networks, function (error, result) {
+ do_check_eq(error, null);
+ run_next_test();
+ });
+});
+
+add_test(function test_clear_interface() {
+ var networks = getNetworks();
+ networks.forEach(function(network, index) {
+ networks[index] = {network: network, networkId: getNetworkId(network.id, network.type)};
+ }, this);
+
+ netStatsDb.clearInterfaceStats(networks[0], function (error, result) {
+ do_check_eq(error, null);
+ run_next_test();
+ });
+});
+
+add_test(function test_internalSaveStats_singleSample() {
+ var networks = getNetworks();
+
+ var stats = { appId: 0,
+ isInBrowser: 0,
+ serviceType: "",
+ network: [networks[0].id, networks[0].type],
+ timestamp: Date.now(),
+ rxBytes: 0,
+ txBytes: 0,
+ rxSystemBytes: 1234,
+ txSystemBytes: 1234,
+ rxTotalBytes: 1234,
+ txTotalBytes: 1234 };
+
+ netStatsDb.dbNewTxn(STATS_STORE_NAME, "readwrite", function(txn, store) {
+ netStatsDb._saveStats(txn, store, stats);
+ }, function(error, result) {
+ do_check_eq(error, null);
+
+ netStatsDb.logAllRecords(function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 1);
+ do_check_eq(result[0].appId, stats.appId);
+ do_check_eq(result[0].isInBrowser, stats.isInBrowser);
+ do_check_eq(result[0].serviceType, stats.serviceType);
+ do_check_true(compareNetworks(result[0].network, stats.network));
+ do_check_eq(result[0].timestamp, stats.timestamp);
+ do_check_eq(result[0].rxBytes, stats.rxBytes);
+ do_check_eq(result[0].txBytes, stats.txBytes);
+ do_check_eq(result[0].rxSystemBytes, stats.rxSystemBytes);
+ do_check_eq(result[0].txSystemBytes, stats.txSystemBytes);
+ do_check_eq(result[0].rxTotalBytes, stats.rxTotalBytes);
+ do_check_eq(result[0].txTotalBytes, stats.txTotalBytes);
+ run_next_test();
+ });
+ });
+});
+
+add_test(function test_internalSaveStats_arraySamples() {
+ clearStore(STATS_STORE_NAME, function() {
+ var networks = getNetworks();
+ var network = [networks[0].id, networks[0].type];
+
+ var samples = 2;
+ var stats = [];
+ for (var i = 0; i < samples; i++) {
+ stats.push({ appId: 0,
+ isInBrowser: 0,
+ serviceType: "",
+ network: network,
+ timestamp: Date.now() + (10 * i),
+ rxBytes: 0,
+ txBytes: 0,
+ rxSystemBytes: 1234,
+ txSystemBytes: 1234,
+ rxTotalBytes: 1234,
+ txTotalBytes: 1234 });
+ }
+
+ netStatsDb.dbNewTxn(STATS_STORE_NAME, "readwrite", function(txn, store) {
+ netStatsDb._saveStats(txn, store, stats);
+ }, function(error, result) {
+ do_check_eq(error, null);
+
+ netStatsDb.logAllRecords(function(error, result) {
+ do_check_eq(error, null);
+
+ do_check_eq(result.length, samples);
+ var success = true;
+ for (var i = 0; i < samples; i++) {
+ if (result[i].appId != stats[i].appId ||
+ result[i].isInBrowser != stats[i].isInBrowser ||
+ result[i].serviceType != stats[i].serviceType ||
+ !compareNetworks(result[i].network, stats[i].network) ||
+ result[i].timestamp != stats[i].timestamp ||
+ result[i].rxBytes != stats[i].rxBytes ||
+ result[i].txBytes != stats[i].txBytes ||
+ result[i].rxSystemBytes != stats[i].rxSystemBytes ||
+ result[i].txSystemBytes != stats[i].txSystemBytes ||
+ result[i].rxTotalBytes != stats[i].rxTotalBytes ||
+ result[i].txTotalBytes != stats[i].txTotalBytes) {
+ success = false;
+ break;
+ }
+ }
+ do_check_true(success);
+ run_next_test();
+ });
+ });
+ });
+});
+
+add_test(function test_internalRemoveOldStats() {
+ clearStore(STATS_STORE_NAME, function() {
+ var networks = getNetworks();
+ var network = [networks[0].id, networks[0].type];
+ var samples = 10;
+ var stats = [];
+ for (var i = 0; i < samples - 1; i++) {
+ stats.push({ appId: 0, isInBrowser: 0,
+ serviceType: "",
+ network: network, timestamp: Date.now() + (10 * i),
+ rxBytes: 0, txBytes: 0,
+ rxSystemBytes: 1234, txSystemBytes: 1234,
+ rxTotalBytes: 1234, txTotalBytes: 1234 });
+ }
+
+ stats.push({ appId: 0, isInBrowser: 0,
+ serviceType: "",
+ network: network, timestamp: Date.now() + (10 * samples),
+ rxBytes: 0, txBytes: 0,
+ rxSystemBytes: 1234, txSystemBytes: 1234,
+ rxTotalBytes: 1234, txTotalBytes: 1234 });
+
+ netStatsDb.dbNewTxn(STATS_STORE_NAME, "readwrite", function(txn, store) {
+ netStatsDb._saveStats(txn, store, stats);
+ var date = stats[stats.length - 1].timestamp
+ + (netStatsDb.sampleRate * netStatsDb.maxStorageSamples - 1) - 1;
+ netStatsDb._removeOldStats(txn, store, 0, 0, "", network, date);
+ }, function(error, result) {
+ do_check_eq(error, null);
+
+ netStatsDb.logAllRecords(function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 1);
+
+ run_next_test();
+ });
+ });
+ });
+});
+
+function processSamplesDiff(networks, lastStat, newStat, callback) {
+ clearStore(STATS_STORE_NAME, function() {
+ netStatsDb.dbNewTxn(STATS_STORE_NAME, "readwrite", function(txn, store) {
+ netStatsDb._saveStats(txn, store, lastStat);
+ }, function(error, result) {
+ netStatsDb.dbNewTxn(STATS_STORE_NAME, "readwrite", function(txn, store) {
+ let request = store.index("network").openCursor(newStat.network, "prev");
+ request.onsuccess = function onsuccess(event) {
+ let cursor = event.target.result;
+ do_check_neq(cursor, null);
+ netStatsDb._processSamplesDiff(txn, store, cursor, newStat, true);
+ };
+ }, function(error, result) {
+ do_check_eq(error, null);
+ netStatsDb.logAllRecords(function(error, result) {
+ do_check_eq(error, null);
+ callback(result);
+ });
+ });
+ });
+ });
+}
+
+add_test(function test_processSamplesDiffSameSample() {
+ var networks = getNetworks();
+ var network = [networks[0].id, networks[0].type];
+
+ var sampleRate = netStatsDb.sampleRate;
+ var date = filterTimestamp(new Date());
+
+ var lastStat = { appId: 0, isInBrowser: 0,
+ serviceType: "",
+ network: network, timestamp: date,
+ rxBytes: 0, txBytes: 0,
+ rxSystemBytes: 1234, txSystemBytes: 1234,
+ rxTotalBytes: 2234, txTotalBytes: 2234 };
+
+ var newStat = { appId: 0, isInBrowser: 0,
+ serviceType: "",
+ network: network, timestamp: date,
+ rxBytes: 0, txBytes: 0,
+ rxSystemBytes: 2234, txSystemBytes: 2234,
+ rxTotalBytes: 2234, txTotalBytes: 2234 };
+
+ processSamplesDiff(networks, lastStat, newStat, function(result) {
+ do_check_eq(result.length, 1);
+ do_check_eq(result[0].appId, newStat.appId);
+ do_check_eq(result[0].isInBrowser, newStat.isInBrowser);
+ do_check_eq(result[0].serviceType, newStat.serviceType);
+ do_check_true(compareNetworks(result[0].network, newStat.network));
+ do_check_eq(result[0].timestamp, newStat.timestamp);
+ do_check_eq(result[0].rxBytes, newStat.rxSystemBytes - lastStat.rxSystemBytes);
+ do_check_eq(result[0].txBytes, newStat.txSystemBytes - lastStat.txSystemBytes);
+ do_check_eq(result[0].rxTotalBytes, lastStat.rxTotalBytes + newStat.rxSystemBytes - lastStat.rxSystemBytes);
+ do_check_eq(result[0].txTotalBytes, lastStat.txTotalBytes + newStat.txSystemBytes - lastStat.txSystemBytes);
+ do_check_eq(result[0].rxSystemBytes, newStat.rxSystemBytes);
+ do_check_eq(result[0].txSystemBytes, newStat.txSystemBytes);
+ run_next_test();
+ });
+});
+
+add_test(function test_processSamplesDiffNextSample() {
+ var networks = getNetworks();
+ var network = [networks[0].id, networks[0].type];
+
+ var sampleRate = netStatsDb.sampleRate;
+ var date = filterTimestamp(new Date());
+
+ var lastStat = { appId: 0, isInBrowser: 0,
+ serviceType: "",
+ network: network, timestamp: date,
+ rxBytes: 0, txBytes: 0,
+ rxSystemBytes: 1234, txSystemBytes: 1234,
+ rxTotalBytes: 2234, txTotalBytes: 2234 };
+
+ var newStat = { appId: 0, isInBrowser: 0,
+ serviceType: "",
+ network: network, timestamp: date + sampleRate,
+ rxBytes: 0, txBytes: 0,
+ rxSystemBytes: 1734, txSystemBytes: 1734,
+ rxTotalBytes: 0, txTotalBytes: 0 };
+
+ processSamplesDiff(networks, lastStat, newStat, function(result) {
+ do_check_eq(result.length, 2);
+ do_check_eq(result[1].appId, newStat.appId);
+ do_check_eq(result[1].isInBrowser, newStat.isInBrowser);
+ do_check_eq(result[1].serviceType, newStat.serviceType);
+ do_check_true(compareNetworks(result[1].network, newStat.network));
+ do_check_eq(result[1].timestamp, newStat.timestamp);
+ do_check_eq(result[1].rxBytes, newStat.rxSystemBytes - lastStat.rxSystemBytes);
+ do_check_eq(result[1].txBytes, newStat.txSystemBytes - lastStat.txSystemBytes);
+ do_check_eq(result[1].rxSystemBytes, newStat.rxSystemBytes);
+ do_check_eq(result[1].txSystemBytes, newStat.txSystemBytes);
+ do_check_eq(result[1].rxTotalBytes, lastStat.rxTotalBytes + newStat.rxSystemBytes - lastStat.rxSystemBytes);
+ do_check_eq(result[1].txTotalBytes, lastStat.txTotalBytes + newStat.txSystemBytes - lastStat.txSystemBytes);
+ run_next_test();
+ });
+});
+
+add_test(function test_processSamplesDiffSamplesLost() {
+ var networks = getNetworks();
+ var network = [networks[0].id, networks[0].type];
+ var samples = 5;
+ var sampleRate = netStatsDb.sampleRate;
+ var date = filterTimestamp(new Date());
+ var lastStat = { appId: 0, isInBrowser: 0,
+ serviceType: "",
+ network: network, timestamp: date,
+ rxBytes: 0, txBytes: 0,
+ rxSystemBytes: 1234, txSystemBytes: 1234,
+ rxTotalBytes: 2234, txTotalBytes: 2234};
+
+ var newStat = { appId: 0, isInBrowser: 0,
+ serviceType: "",
+ network: network, timestamp: date + (sampleRate * samples),
+ rxBytes: 0, txBytes: 0,
+ rxSystemBytes: 2234, txSystemBytes: 2234,
+ rxTotalBytes: 0, txTotalBytes: 0 };
+
+ processSamplesDiff(networks, lastStat, newStat, function(result) {
+ do_check_eq(result.length, samples + 1);
+ do_check_eq(result[0].appId, newStat.appId);
+ do_check_eq(result[0].isInBrowser, newStat.isInBrowser);
+ do_check_eq(result[0].serviceType, newStat.serviceType);
+ do_check_true(compareNetworks(result[samples].network, newStat.network));
+ do_check_eq(result[samples].timestamp, newStat.timestamp);
+ do_check_eq(result[samples].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
+ do_check_eq(result[samples].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
+ do_check_eq(result[samples].rxSystemBytes, newStat.rxSystemBytes);
+ do_check_eq(result[samples].txSystemBytes, newStat.txSystemBytes);
+ do_check_eq(result[samples].rxTotalBytes, lastStat.rxTotalBytes + newStat.rxSystemBytes - lastStat.rxSystemBytes);
+ do_check_eq(result[samples].txTotalBytes, lastStat.txTotalBytes + newStat.txSystemBytes - lastStat.txSystemBytes);
+ run_next_test();
+ });
+});
+
+add_test(function test_saveStats() {
+ var networks = getNetworks();
+ var network = [networks[0].id, networks[0].type];
+
+ var stats = { appId: 0,
+ isInBrowser: false,
+ serviceType: "",
+ networkId: networks[0].id,
+ networkType: networks[0].type,
+ date: new Date(),
+ rxBytes: 2234,
+ txBytes: 2234,
+ isAccumulative: true };
+
+ clearStore(STATS_STORE_NAME, function() {
+ netStatsDb.saveStats(stats, function(error, result) {
+ do_check_eq(error, null);
+ netStatsDb.logAllRecords(function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 1);
+ do_check_eq(result[0].appId, stats.appId);
+ do_check_eq(result[0].isInBrowser, stats.isInBrowser);
+ do_check_eq(result[0].serviceType, stats.serviceType);
+ do_check_true(compareNetworks(result[0].network, network));
+ let timestamp = filterTimestamp(stats.date);
+ do_check_eq(result[0].timestamp, timestamp);
+ do_check_eq(result[0].rxBytes, stats.rxBytes);
+ do_check_eq(result[0].txBytes, stats.txBytes);
+ do_check_eq(result[0].rxSystemBytes, stats.rxBytes);
+ do_check_eq(result[0].txSystemBytes, stats.txBytes);
+ do_check_eq(result[0].rxTotalBytes, stats.rxBytes);
+ do_check_eq(result[0].txTotalBytes, stats.txBytes);
+ run_next_test();
+ });
+ });
+ });
+});
+
+add_test(function test_saveAppStats() {
+ var networks = getNetworks();
+ var network = [networks[0].id, networks[0].type];
+
+ var stats = { appId: 1,
+ isInBrowser: false,
+ serviceType: "",
+ networkId: networks[0].id,
+ networkType: networks[0].type,
+ date: new Date(),
+ rxBytes: 2234,
+ txBytes: 2234,
+ isAccumulative: false };
+
+ clearStore(STATS_STORE_NAME, function() {
+ netStatsDb.saveStats(stats, function(error, result) {
+ do_check_eq(error, null);
+ netStatsDb.logAllRecords(function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 1);
+ do_check_eq(result[0].appId, stats.appId);
+ do_check_eq(result[0].isInBrowser, 0);
+ do_check_eq(result[0].serviceType, stats.serviceType);
+ do_check_true(compareNetworks(result[0].network, network));
+ let timestamp = filterTimestamp(stats.date);
+ do_check_eq(result[0].timestamp, timestamp);
+ do_check_eq(result[0].rxBytes, stats.rxBytes);
+ do_check_eq(result[0].txBytes, stats.txBytes);
+ do_check_eq(result[0].rxSystemBytes, 0);
+ do_check_eq(result[0].txSystemBytes, 0);
+ do_check_eq(result[0].rxTotalBytes, 0);
+ do_check_eq(result[0].txTotalBytes, 0);
+ run_next_test();
+ });
+ });
+ });
+});
+
+add_test(function test_saveServiceStats() {
+ var networks = getNetworks();
+ var network = [networks[0].id, networks[0].type];
+
+ var stats = { appId: 0,
+ isInBrowser: false,
+ serviceType: "FakeType",
+ networkId: networks[0].id,
+ networkType: networks[0].type,
+ date: new Date(),
+ rxBytes: 2234,
+ txBytes: 2234,
+ isAccumulative: false };
+
+ clearStore(STATS_STORE_NAME, function() {
+ netStatsDb.saveStats(stats, function(error, result) {
+ do_check_eq(error, null);
+ netStatsDb.logAllRecords(function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 1);
+ do_check_eq(result[0].appId, stats.appId);
+ do_check_eq(result[0].isInBrowser, 0);
+ do_check_eq(result[0].serviceType, stats.serviceType);
+ do_check_true(compareNetworks(result[0].network, network));
+ let timestamp = filterTimestamp(stats.date);
+ do_check_eq(result[0].timestamp, timestamp);
+ do_check_eq(result[0].rxBytes, stats.rxBytes);
+ do_check_eq(result[0].txBytes, stats.txBytes);
+ do_check_eq(result[0].rxSystemBytes, 0);
+ do_check_eq(result[0].txSystemBytes, 0);
+ do_check_eq(result[0].rxTotalBytes, 0);
+ do_check_eq(result[0].txTotalBytes, 0);
+ run_next_test();
+ });
+ });
+ });
+});
+
+function prepareFind(stats, callback) {
+ clearStore(STATS_STORE_NAME, function() {
+ netStatsDb.dbNewTxn(STATS_STORE_NAME, "readwrite", function(txn, store) {
+ netStatsDb._saveStats(txn, store, stats);
+ }, function(error, result) {
+ callback(error, result);
+ });
+ });
+}
+
+add_test(function test_find () {
+ var networks = getNetworks();
+ var networkWifi = [networks[0].id, networks[0].type];
+ var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
+ var appId = 0;
+ var isInBrowser = 0;
+ var serviceType = "";
+
+ var samples = 5;
+ var sampleRate = netStatsDb.sampleRate;
+ var start = Date.now();
+ var saveDate = filterTimestamp(new Date());
+ var end = new Date(start + (sampleRate * (samples - 1)));
+ start = new Date(start - sampleRate);
+ var stats = [];
+ for (var i = 0; i < samples; i++) {
+ stats.push({ appId: appId, isInBrowser: isInBrowser,
+ serviceType: serviceType,
+ network: networkWifi, timestamp: saveDate + (sampleRate * i),
+ rxBytes: 0, txBytes: 10,
+ rxSystemBytes: 0, txSystemBytes: 0,
+ rxTotalBytes: 0, txTotalBytes: 0 });
+
+
+ stats.push({ appId: appId, isInBrowser: isInBrowser,
+ serviceType: serviceType,
+ network: networkMobile, timestamp: saveDate + (sampleRate * i),
+ rxBytes: 0, txBytes: 10,
+ rxSystemBytes: 0, txSystemBytes: 0,
+ rxTotalBytes: 0, txTotalBytes: 0 });
+ }
+
+ prepareFind(stats, function(error, result) {
+ do_check_eq(error, null);
+ netStatsDb.find(function (error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.browsingTrafficOnly, false);
+ do_check_eq(result.serviceType, serviceType);
+ do_check_eq(result.network.id, networks[0].id);
+ do_check_eq(result.network.type, networks[0].type);
+ do_check_eq(result.start.getTime(), start.getTime());
+ do_check_eq(result.end.getTime(), end.getTime());
+ do_check_eq(result.data.length, samples + 1);
+ do_check_eq(result.data[0].rxBytes, null);
+ do_check_eq(result.data[1].rxBytes, 0);
+ do_check_eq(result.data[samples].rxBytes, 0);
+ run_next_test();
+ }, appId, false, serviceType, networks[0], start, end);
+ });
+});
+
+add_test(function test_findAppStats () {
+ var networks = getNetworks();
+ var networkWifi = [networks[0].id, networks[0].type];
+ var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
+ var appId = 1;
+ var isInBrowser = 0;
+ var serviceType = "";
+
+ var samples = 5;
+ var sampleRate = netStatsDb.sampleRate;
+ var start = Date.now();
+ var saveDate = filterTimestamp(new Date());
+ var end = new Date(start + (sampleRate * (samples - 1)));
+ start = new Date(start - sampleRate);
+ var stats = [];
+ for (var i = 0; i < samples; i++) {
+ stats.push({ appId: appId, isInBrowser: isInBrowser,
+ serviceType: serviceType,
+ network: networkWifi, timestamp: saveDate + (sampleRate * i),
+ rxBytes: 0, txBytes: 10,
+ rxTotalBytes: 0, txTotalBytes: 0 });
+
+ stats.push({ appId: appId, isInBrowser: isInBrowser,
+ serviceType: serviceType,
+ network: networkMobile, timestamp: saveDate + (sampleRate * i),
+ rxBytes: 0, txBytes: 10,
+ rxTotalBytes: 0, txTotalBytes: 0 });
+ }
+
+ prepareFind(stats, function(error, result) {
+ do_check_eq(error, null);
+ netStatsDb.find(function (error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.browsingTrafficOnly, false);
+ do_check_eq(result.serviceType, serviceType);
+ do_check_eq(result.network.id, networks[0].id);
+ do_check_eq(result.network.type, networks[0].type);
+ do_check_eq(result.start.getTime(), start.getTime());
+ do_check_eq(result.end.getTime(), end.getTime());
+ do_check_eq(result.data.length, samples + 1);
+ do_check_eq(result.data[0].rxBytes, null);
+ do_check_eq(result.data[1].rxBytes, 0);
+ do_check_eq(result.data[samples].rxBytes, 0);
+ run_next_test();
+ }, appId, false, serviceType, networks[0], start, end);
+ });
+});
+
+add_test(function test_findServiceStats () {
+ var networks = getNetworks();
+ var networkWifi = [networks[0].id, networks[0].type];
+ var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
+ var appId = 0;
+ var isInBrowser = 0;
+ var serviceType = "FakeType";
+
+ var samples = 5;
+ var sampleRate = netStatsDb.sampleRate;
+ var start = Date.now();
+ var saveDate = filterTimestamp(new Date());
+ var end = new Date(start + (sampleRate * (samples - 1)));
+ start = new Date(start - sampleRate);
+ var stats = [];
+ for (var i = 0; i < samples; i++) {
+ stats.push({ appId: appId, isInBrowser: isInBrowser,
+ serviceType: serviceType,
+ network: networkWifi, timestamp: saveDate + (sampleRate * i),
+ rxBytes: 0, txBytes: 10,
+ rxTotalBytes: 0, txTotalBytes: 0 });
+
+ stats.push({ appId: appId, isInBrowser: isInBrowser,
+ serviceType: serviceType,
+ network: networkMobile, timestamp: saveDate + (sampleRate * i),
+ rxBytes: 0, txBytes: 10,
+ rxTotalBytes: 0, txTotalBytes: 0 });
+ }
+
+ prepareFind(stats, function(error, result) {
+ do_check_eq(error, null);
+ netStatsDb.find(function (error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.browsingTrafficOnly, false);
+ do_check_eq(result.serviceType, serviceType);
+ do_check_eq(result.network.id, networks[0].id);
+ do_check_eq(result.network.type, networks[0].type);
+ do_check_eq(result.start.getTime(), start.getTime());
+ do_check_eq(result.end.getTime(), end.getTime());
+ do_check_eq(result.data.length, samples + 1);
+ do_check_eq(result.data[0].rxBytes, null);
+ do_check_eq(result.data[1].rxBytes, 0);
+ do_check_eq(result.data[samples].rxBytes, 0);
+ run_next_test();
+ }, appId, false, serviceType, networks[0], start, end);
+ });
+});
+
+add_test(function test_saveMultipleAppStats () {
+ var networks = getNetworks();
+ var networkWifi = networks[0];
+ var networkMobile = networks[1]; // Fake mobile interface
+
+ var saveDate = filterTimestamp(new Date());
+ var cached = Object.create(null);
+ var serviceType = "FakeType";
+ var wifiNetId = networkWifi.id + '' + networkWifi.type;
+ var mobileNetId = networkMobile.id + '' + networkMobile.type;
+
+ cached[0 + '' + serviceType + wifiNetId] = {
+ appId: 0, date: new Date(),
+ networkId: networkWifi.id, networkType: networkWifi.type,
+ rxBytes: 0, txBytes: 10,
+ serviceType: serviceType, isAccumulative: false,
+ isInBrowser: false
+ };
+
+ cached[0 + '' + serviceType + mobileNetId] = {
+ appId: 0, date: new Date(),
+ networkId: networkMobile.id, networkType: networkMobile.type,
+ rxBytes: 0, txBytes: 10,
+ serviceType: serviceType, isAccumulative: false,
+ isInBrowser: false
+ };
+
+ cached[1 + '' + wifiNetId] = {
+ appId: 1, date: new Date(),
+ networkId: networkWifi.id, networkType: networkWifi.type,
+ rxBytes: 0, txBytes: 10,
+ serviceType: "", isAccumulative: false,
+ isInBrowser: false
+ };
+
+ cached[1 + '' + mobileNetId] = {
+ appId: 1, date: new Date(),
+ networkId: networkMobile.id, networkType: networkMobile.type,
+ rxBytes: 0, txBytes: 10,
+ serviceType: "", isAccumulative: false,
+ isInBrowser: false
+ };
+
+ cached[2 + '' + wifiNetId] = {
+ appId: 2, date: new Date(),
+ networkId: networkWifi.id, networkType: networkWifi.type,
+ rxBytes: 0, txBytes: 10,
+ serviceType: "", isAccumulative: false,
+ isInBrowser: false
+ };
+
+ cached[2 + '' + mobileNetId] = {
+ appId: 2, date: new Date(),
+ networkId: networkMobile.id, networkType: networkMobile.type,
+ rxBytes: 0, txBytes: 10,
+ serviceType: "", isAccumulative: false,
+ isInBrowser: false
+ };
+
+ let keys = Object.keys(cached);
+ let index = 0;
+
+ networks.push(networkMobile);
+
+ clearStore(STATS_STORE_NAME, function() {
+ netStatsDb.saveStats(cached[keys[index]],
+ function callback(error, result) {
+ do_check_eq(error, null);
+
+ if (index == keys.length - 1) {
+ netStatsDb.logAllRecords(function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 6);
+ do_check_eq(result[0].isInBrowser, 0);
+ do_check_eq(result[0].serviceType, serviceType);
+ do_check_eq(result[3].appId, 1);
+ do_check_true(compareNetworks(result[0].network, [networkWifi.id, networkWifi.type]));
+ do_check_eq(result[0].rxBytes, 0);
+ do_check_eq(result[0].txBytes, 10);
+ run_next_test();
+ });
+ return;
+ }
+
+ index += 1;
+ netStatsDb.saveStats(cached[keys[index]], callback);
+ });
+ });
+});
+
+// Test case for find samples with browsingTrafficOnly option.
+add_test(function test_findBrowsingTrafficStats() {
+ var networks = getNetworks();
+ var networkWifi = [networks[0].id, networks[0].type];
+ var networkMobile = [networks[1].id, networks[1].type];
+ var serviceType = "";
+ var samples = 5;
+ var sampleRate = netStatsDb.sampleRate;
+ var start = Date.now();
+ var end = new Date(start + (sampleRate * (samples - 1)));
+ var saveDate = filterTimestamp(new Date());
+ start = new Date(start - sampleRate);
+ var stats = [];
+
+ for (var i = 0; i < samples; i++) {
+ // System app.
+ stats.push({ appId: 1008, isInBrowser: 0,
+ serviceType: serviceType, network: networkMobile,
+ timestamp: saveDate + (sampleRate * i),
+ rxBytes: 200, txBytes: 100,
+ rxTotalBytes: 200, txTotalBytes: 100});
+ // Browser of system app.
+ stats.push({ appId: 1008, isInBrowser: 1,
+ serviceType: serviceType, network: networkMobile,
+ timestamp: saveDate + (sampleRate * i),
+ rxBytes: 1000, txBytes: 500,
+ rxTotalBytes: 1000, txTotalBytes: 500});
+ // Another app.
+ stats.push({ appId: 1021, isInBrowser: 0,
+ serviceType: serviceType, network: networkMobile,
+ timestamp: saveDate + (sampleRate * i),
+ rxBytes: 300, txBytes: 150,
+ rxTotalBytes: 300, txTotalBytes: 150});
+ // Browser of another app.
+ stats.push({ appId: 1021, isInBrowser: 1,
+ serviceType: serviceType, network: networkMobile,
+ timestamp: saveDate + (sampleRate * i),
+ rxBytes: 600, txBytes: 300,
+ rxTotalBytes: 600, txTotalBytes: 300});
+ }
+
+ prepareFind(stats, function(error, result) {
+ do_check_eq(error, null);
+ netStatsDb.find(function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.browsingTrafficOnly, true);
+ do_check_eq(result.serviceType, serviceType);
+ do_check_eq(result.network.id, networks[1].id);
+ do_check_eq(result.network.type, networks[1].type);
+ do_check_eq(result.start.getTime(), start.getTime());
+ do_check_eq(result.end.getTime(), end.getTime());
+ do_check_eq(result.data.length, samples + 1);
+ do_check_eq(result.data[0].rxBytes, null);
+ do_check_eq(result.data[1].txBytes, 500);
+ do_check_eq(result.data[2].rxBytes, 1000);
+ run_next_test();
+ }, 1008, true, serviceType, networks[1], start, end);
+ });
+});
+
+// Test case for find samples with browsingTrafficOnly option.
+add_test(function test_findAppTrafficStats() {
+ var networks = getNetworks();
+ var networkWifi = [networks[0].id, networks[0].type];
+ var networkMobile = [networks[1].id, networks[1].type];
+ var serviceType = "";
+ var samples = 5;
+ var sampleRate = netStatsDb.sampleRate;
+ var start = Date.now();
+ var end = new Date(start + (sampleRate * (samples - 1)));
+ var saveDate = filterTimestamp(new Date());
+ start = new Date(start - sampleRate);
+ var stats = [];
+
+ for (var i = 0; i < samples; i++) {
+ // System app.
+ stats.push({ appId: 1008, isInBrowser: 0,
+ serviceType: serviceType, network: networkMobile,
+ timestamp: saveDate + (sampleRate * i),
+ rxBytes: 200, txBytes: 100,
+ rxTotalBytes: 200, txTotalBytes: 100});
+ // Browser of system app.
+ stats.push({ appId: 1008, isInBrowser: 1,
+ serviceType: serviceType, network: networkMobile,
+ timestamp: saveDate + (sampleRate * i),
+ rxBytes: 1000, txBytes: 500,
+ rxTotalBytes: 1000, txTotalBytes: 500});
+ // Another app.
+ stats.push({ appId: 1021, isInBrowser: 0,
+ serviceType: serviceType, network: networkMobile,
+ timestamp: saveDate + (sampleRate * i),
+ rxBytes: 300, txBytes: 150,
+ rxTotalBytes: 300, txTotalBytes: 150});
+ // Browser of another app.
+ stats.push({ appId: 1021, isInBrowser: 1,
+ serviceType: serviceType, network: networkMobile,
+ timestamp: saveDate + (sampleRate * i),
+ rxBytes: 600, txBytes: 300,
+ rxTotalBytes: 600, txTotalBytes: 300});
+ }
+
+ prepareFind(stats, function(error, result) {
+ do_check_eq(error, null);
+ netStatsDb.find(function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.browsingTrafficOnly, false);
+ do_check_eq(result.serviceType, serviceType);
+ do_check_eq(result.network.id, networks[1].id);
+ do_check_eq(result.network.type, networks[1].type);
+ do_check_eq(result.start.getTime(), start.getTime());
+ do_check_eq(result.end.getTime(), end.getTime());
+ do_check_eq(result.data.length, samples + 1);
+ do_check_eq(result.data[0].rxBytes, null);
+ do_check_eq(result.data[1].txBytes, 600);
+ do_check_eq(result.data[2].rxBytes, 1200);
+ run_next_test();
+ }, 1008, false, serviceType, networks[1], start, end);
+ });
+});
+
+var networkWifi = '00';
+var networkMobile = '11';
+
+var examplePageURL = "http://example.com/index.html";
+var exampleManifestURL = "http://example.com/manifest.webapp";
+
+var testPageURL = "http://test.com/index.html";
+var testManifestURL = "http://test.com/manifest.webapp";
+
+var alarms = [{ id: null,
+ networkId: networkWifi,
+ absoluteThreshold: 10000,
+ relativeThreshold: 10000,
+ data: {foo: "something"},
+ pageURL: examplePageURL,
+ manifestURL: exampleManifestURL },
+ { id: null,
+ networkId: networkWifi,
+ absoluteThreshold: 1000,
+ relativeThreshold: 1000,
+ data: {foo: "else"},
+ pageURL: examplePageURL,
+ manifestURL: exampleManifestURL },
+ { id: null,
+ networkId: networkMobile,
+ absoluteThreshold: 100,
+ relativeThreshold: 100,
+ data: {foo: "to"},
+ pageURL: examplePageURL,
+ manifestURL: exampleManifestURL },
+ { id: null,
+ networkId: networkMobile,
+ absoluteThreshold: 10,
+ relativeThreshold: 10,
+ data: {foo: "test"},
+ pageURL: testPageURL,
+ manifestURL: testManifestURL }];
+
+var alarmsDbId = 1;
+
+add_test(function test_addAlarm() {
+ // Add alarms[0] -> DB: [ alarms[0] (id: 1) ]
+ // Check the insertion is OK.
+ netStatsDb.addAlarm(alarms[0], function(error, result) {
+ do_check_eq(error, null);
+ alarmsDbId = result;
+ netStatsDb.getAlarms(Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, exampleManifestURL, function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 1);
+ do_check_eq(result[0].id, alarmsDbId);
+ do_check_eq(result[0].networkId, alarms[0].networkId);
+ do_check_eq(result[0].absoluteThreshold, alarms[0].absoluteThreshold);
+ do_check_eq(result[0].relativeThreshold, alarms[0].relativeThreshold);
+ do_check_eq(result[0].data.foo, alarms[0].data.foo);
+ run_next_test();
+ });
+ });
+});
+
+add_test(function test_getFirstAlarm() {
+ // Add alarms[1] -> DB: [ alarms[0] (id: 1), alarms[1] (id: 2) ]
+ // Check first alarm is alarms[1] because threshold is lower.
+ alarmsDbId += 1;
+ netStatsDb.addAlarm(alarms[1], function (error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result, alarmsDbId);
+ netStatsDb.getFirstAlarm(networkWifi, function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.id, alarmsDbId);
+ do_check_eq(result.networkId, alarms[1].networkId);
+ do_check_eq(result.absoluteThreshold, alarms[1].absoluteThreshold);
+ do_check_eq(result.relativeThreshold, alarms[1].relativeThreshold);
+ do_check_eq(result.data.foo, alarms[1].data.foo);
+ do_check_eq(result.pageURL, alarms[1].pageURL);
+ do_check_eq(result.manifestURL, alarms[1].manifestURL);
+ run_next_test();
+ });
+ });
+});
+
+add_test(function test_removeAlarm() {
+ // Remove alarms[1] (id: 2) -> DB: [ alarms[0] (id: 1) ]
+ // Check get first return alarms[0].
+ netStatsDb.removeAlarm(alarmsDbId, alarms[0].manifestURL, function (error, result) {
+ do_check_eq(error, null);
+ netStatsDb.getFirstAlarm(networkWifi, function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.id, alarmsDbId - 1);
+ do_check_eq(result.networkId, alarms[0].networkId);
+ do_check_eq(result.absoluteThreshold, alarms[0].absoluteThreshold);
+ do_check_eq(result.relativeThreshold, alarms[0].relativeThreshold);
+ do_check_eq(result.data.foo, alarms[0].data.foo);
+ do_check_eq(result.pageURL, alarms[0].pageURL);
+ do_check_eq(result.manifestURL, alarms[0].manifestURL);
+ run_next_test();
+ });
+ });
+});
+
+add_test(function test_removeAppAlarm() {
+ // Remove alarms[0] (id: 1) -> DB: [ ]
+ netStatsDb.removeAlarm(alarmsDbId - 1, alarms[0].manifestURL, function (error, result) {
+ do_check_eq(error, null);
+ netStatsDb.getAlarms(networkWifi, exampleManifestURL, function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 0);
+ run_next_test();
+ });
+ });
+});
+
+add_test(function test_getAlarms() {
+ // Add all alarms -> DB: [ alarms[0] (id: 3),
+ // alarms[1] (id: 4),
+ // alarms[2] (id: 5),
+ // alarms[3] (id: 6) ]
+ // Check that getAlarms for wifi returns 2 alarms.
+ // Check that getAlarms for all connections returns 3 alarms.
+
+ var callback = function () {
+ netStatsDb.getAlarms(networkWifi, exampleManifestURL, function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 2);
+ netStatsDb.getAlarms(null, exampleManifestURL, function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 3);
+ run_next_test();
+ });
+ });
+ };
+
+ var index = 0;
+
+ var addFunction = function () {
+ alarmsDbId += 1;
+ netStatsDb.addAlarm(alarms[index], function (error, result) {
+ do_check_eq(error, null);
+ index += 1;
+ do_check_eq(result, alarmsDbId);
+ if (index >= alarms.length) {
+ callback();
+ return;
+ }
+ addFunction();
+ });
+ };
+
+ addFunction();
+});
+
+add_test(function test_removeAppAllAlarms() {
+ // Remove all alarms for exampleManifestURL -> DB: [ alarms[3] (id: 6) ]
+ netStatsDb.removeAlarms(exampleManifestURL, function (error, result) {
+ do_check_eq(error, null);
+ netStatsDb.getAlarms(null, exampleManifestURL, function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 0);
+ netStatsDb.getAlarms(null, testManifestURL, function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 1);
+ run_next_test();
+ });
+ });
+ });
+});
+
+add_test(function test_updateAlarm() {
+ // Update alarms[3] (id: 6) -> DB: [ alarms[3]* (id: 6) ]
+
+ var updatedAlarm = alarms[1];
+ updatedAlarm.id = alarmsDbId;
+ updatedAlarm.threshold = 10;
+
+ netStatsDb.updateAlarm(updatedAlarm, function (error, result) {
+ do_check_eq(error, null);
+ netStatsDb.getFirstAlarm(networkWifi, function(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.id, updatedAlarm.id);
+ do_check_eq(result.networkId, updatedAlarm.networkId);
+ do_check_eq(result.absoluteThreshold, updatedAlarm.absoluteThreshold);
+ do_check_eq(result.relativeThreshold, updatedAlarm.relativeThreshold);
+ do_check_eq(result.data.foo, updatedAlarm.data.foo);
+ do_check_eq(result.pageURL, updatedAlarm.pageURL);
+ do_check_eq(result.manifestURL, updatedAlarm.manifestURL);
+ run_next_test();
+ });
+ });
+});
+
+function run_test() {
+ do_get_profile();
+ run_next_test();
+}
diff --git a/dom/network/tests/unit_stats/test_networkstats_service.js b/dom/network/tests/unit_stats/test_networkstats_service.js
new file mode 100644
index 000000000..8c43a9b54
--- /dev/null
+++ b/dom/network/tests/unit_stats/test_networkstats_service.js
@@ -0,0 +1,290 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const NETWORK_STATUS_READY = 0;
+const NETWORK_STATUS_STANDBY = 1;
+const NETWORK_STATUS_AWAY = 2;
+
+const QUEUE_TYPE_UPDATE_STATS = 0;
+
+var wifiId = '00';
+
+function getNetworks(callback) {
+ NetworkStatsService._db.getAvailableNetworks(function onGetNetworks(aError, aResult) {
+ callback(aError, aResult);
+ });
+}
+
+add_test(function test_clearDB() {
+ getNetworks(function onGetNetworks(error, result) {
+ do_check_eq(error, null);
+ var networks = result;
+ networks.forEach(function(network, index) {
+ networks[index] = {network: network, networkId: NetworkStatsService.getNetworkId(network.id, network.type)};
+ }, this);
+
+ NetworkStatsService._db.clearStats(networks, function onDBCleared(error, result) {
+ do_check_eq(error, null);
+ run_next_test();
+ });
+ });
+});
+
+function getNetworkId(callback) {
+ getNetworks(function onGetNetworks(error, result) {
+ do_check_eq(error, null);
+ var netId = NetworkStatsService.getNetworkId(result[0].id, result[0].type);
+ callback(null, netId);
+ });
+}
+
+add_test(function test_networkStatsAvailable_ok() {
+ getNetworkId(function onGetId(error, result) {
+ do_check_eq(error, null);
+ var netId = result;
+ NetworkStatsService.networkStatsAvailable(function (success, msg) {
+ do_check_eq(success, true);
+ run_next_test();
+ }, netId, true, 1234, 4321, Date.now());
+ });
+});
+
+add_test(function test_networkStatsAvailable_failure() {
+ getNetworkId(function onGetId(error, result) {
+ do_check_eq(error, null);
+ var netId = result;
+ NetworkStatsService.networkStatsAvailable(function (success, msg) {
+ do_check_eq(success, false);
+ run_next_test();
+ }, netId, false, 1234, 4321, Date.now());
+ });
+});
+
+add_test(function test_update_invalidNetwork() {
+ NetworkStatsService.update(-1, function (success, msg) {
+ do_check_eq(success, false);
+ do_check_eq(msg, "Invalid network -1");
+ run_next_test();
+ });
+});
+
+add_test(function test_update() {
+ getNetworkId(function onGetId(error, result) {
+ do_check_eq(error, null);
+ var netId = result;
+ NetworkStatsService.update(netId, function (success, msg) {
+ do_check_eq(success, true);
+ run_next_test();
+ });
+ });
+});
+
+add_test(function test_updateQueueIndex() {
+ NetworkStatsService.updateQueue = [{netId: 0, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
+ {netId: 1, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
+ {netId: 2, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
+ {netId: 3, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
+ {netId: 4, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS}];
+ var index = NetworkStatsService.updateQueueIndex(3);
+ do_check_eq(index, 3);
+ index = NetworkStatsService.updateQueueIndex(10);
+ do_check_eq(index, -1);
+
+ NetworkStatsService.updateQueue = [];
+ run_next_test();
+});
+
+add_test(function test_updateAllStats() {
+ NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_READY;
+ NetworkStatsService.updateAllStats(function(success, msg) {
+ do_check_eq(success, true);
+ NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_STANDBY;
+ NetworkStatsService.updateAllStats(function(success, msg) {
+ do_check_eq(success, true);
+ NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_AWAY;
+ NetworkStatsService.updateAllStats(function(success, msg) {
+ do_check_eq(success, true);
+ run_next_test();
+ });
+ });
+ });
+});
+
+add_test(function test_updateStats_ok() {
+ getNetworkId(function onGetId(error, result) {
+ do_check_eq(error, null);
+ var netId = result;
+ NetworkStatsService.updateStats(netId, function(success, msg){
+ do_check_eq(success, true);
+ run_next_test();
+ });
+ });
+});
+
+add_test(function test_updateStats_failure() {
+ NetworkStatsService.updateStats(-1, function(success, msg){
+ do_check_eq(success, false);
+ run_next_test();
+ });
+});
+
+// Define Mockup function to simulate a request to netd
+function MockNetdRequest(aCallback) {
+ var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ var event = {
+ notify: function (timer) {
+ aCallback();
+ }
+ };
+
+ timer.initWithCallback(event, 100, Ci.nsITimer.TYPE_ONE_SHOT);
+}
+
+add_test(function test_queue() {
+
+ // Overwrite update function of NetworkStatsService to avoid netd errors due to use
+ // fake interfaces. First, original function is stored to restore it at the end of the
+ // test.
+ var updateFunctionBackup = NetworkStatsService.update;
+
+ NetworkStatsService.update = function update(aNetId, aCallback) {
+ MockNetdRequest(function () {
+ if (aCallback) {
+ aCallback(true, "ok");
+ }
+ });
+ };
+
+ // Fill networks with fake network interfaces to enable netd async requests.
+ var network = {id: "1234", type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE};
+ var netId1 = NetworkStatsService.getNetworkId(network.id, network.type);
+ NetworkStatsService._networks[netId1] = { network: network,
+ interfaceName: "net1" };
+
+ network = {id: "5678", type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE};
+ var netId2 = NetworkStatsService.getNetworkId(network.id, network.type);
+ NetworkStatsService._networks[netId2] = { network: network,
+ interfaceName: "net2" };
+
+ NetworkStatsService.updateStats(netId1);
+ NetworkStatsService.updateStats(netId2);
+ do_check_eq(NetworkStatsService.updateQueue.length, 2);
+ do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 1);
+
+ var i = 0;
+ var updateCount = 0;
+ var callback = function(success, msg) {
+ i++;
+ if (i >= updateCount) {
+ NetworkStatsService.update = updateFunctionBackup;
+ run_next_test();
+ }
+ };
+
+ NetworkStatsService.updateStats(netId1, callback);
+ updateCount++;
+ NetworkStatsService.updateStats(netId2, callback);
+ updateCount++;
+
+ do_check_eq(NetworkStatsService.updateQueue.length, 2);
+ do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 2);
+ do_check_eq(NetworkStatsService.updateQueue[0].callbacks[0], null);
+ do_check_neq(NetworkStatsService.updateQueue[0].callbacks[1], null);
+});
+
+add_test(function test_getAlarmQuota() {
+ let alarm = { networkId: wifiId, absoluteThreshold: 10000 };
+
+ NetworkStatsService._getAlarmQuota(alarm, function onSet(error, quota){
+ do_check_eq(error, null);
+ do_check_neq(quota, undefined);
+ do_check_eq(alarm.absoluteThreshold, alarm.relativeThreshold);
+ run_next_test();
+ });
+});
+
+var testPageURL = "http://test.com";
+var testManifestURL = "http://test.com/manifest.webapp";
+
+add_test(function test_setAlarm() {
+ let alarm = { id: null,
+ networkId: wifiId,
+ threshold: 10000,
+ absoluteThreshold: null,
+ alarmStart: null,
+ alarmEnd: null,
+ data: null,
+ pageURL: testPageURL,
+ manifestURL: testManifestURL };
+
+ NetworkStatsService._setAlarm(alarm, function onSet(error, result) {
+ do_check_eq(result, 1);
+ run_next_test();
+ });
+});
+
+add_test(function test_setAlarm_invalid_threshold() {
+ let alarm = { id: null,
+ networkId: wifiId,
+ threshold: -10000,
+ absoluteThreshold: null,
+ alarmStart: null,
+ alarmEnd: null,
+ data: null,
+ pageURL: testPageURL,
+ manifestURL: testManifestURL };
+
+ NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_READY;
+
+ NetworkStatsService._setAlarm(alarm, function onSet(error, result) {
+ do_check_eq(error, "InvalidStateError");
+ run_next_test();
+ });
+});
+
+add_test(function test_fireAlarm() {
+ // Add a fake alarm into database.
+ let alarm = { id: null,
+ networkId: wifiId,
+ threshold: 10000,
+ absoluteThreshold: null,
+ alarmStart: null,
+ alarmEnd: null,
+ data: null,
+ pageURL: testPageURL,
+ manifestURL: testManifestURL };
+
+ // Set wifi status to standby to avoid connecting to netd when adding an alarm.
+ NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_STANDBY;
+
+ NetworkStatsService._db.addAlarm(alarm, function addSuccessCb(error, newId) {
+ NetworkStatsService._db.getAlarms(Ci.nsINetworkInfo.NETWORK_TYPE_WIFI,
+ testManifestURL, function onGet(error, result) {
+ do_check_eq(error, null);
+ do_check_eq(result.length, 1);
+
+ // Result of getAlarms is based on expected child's data format, so
+ // some changes are needed to be able to use it.
+ result[0].networkId = wifiId;
+ result[0].pageURL = testPageURL;
+ result[0].manifestURL = testManifestURL;
+
+ NetworkStatsService._fireAlarm(result[0], false);
+ NetworkStatsService._db.getAlarms(Ci.nsINetworkInfo.NETWORK_TYPE_WIFI,
+ testManifestURL, function onGet(error, result) {
+ do_check_eq(error, undefined);
+ do_check_eq(result.length, 0);
+ run_next_test();
+ });
+ });
+ });
+});
+
+function run_test() {
+ do_get_profile();
+
+ Cu.import("resource://gre/modules/NetworkStatsService.jsm");
+ run_next_test();
+}
diff --git a/dom/network/tests/unit_stats/test_networkstats_service_proxy.js b/dom/network/tests/unit_stats/test_networkstats_service_proxy.js
new file mode 100644
index 000000000..131b886d0
--- /dev/null
+++ b/dom/network/tests/unit_stats/test_networkstats_service_proxy.js
@@ -0,0 +1,233 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "nssProxy",
+ "@mozilla.org/networkstatsServiceProxy;1",
+ "nsINetworkStatsServiceProxy");
+
+function mokConvertNetworkInfo() {
+ NetworkStatsService.convertNetworkInfo = function(aNetworkInfo) {
+ if (aNetworkInfo.type != Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE &&
+ aNetworkInfo.type != Ci.nsINetworkInfo.NETWORK_TYPE_WIFI) {
+ return null;
+ }
+
+ let id = '0';
+ if (aNetworkInfo.type == Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE) {
+ id = '1234'
+ }
+
+ let netId = this.getNetworkId(id, aNetworkInfo.type);
+
+ if (!this._networks[netId]) {
+ this._networks[netId] = Object.create(null);
+ this._networks[netId].network = { id: id,
+ type: aNetworkInfo.type };
+ }
+
+ return netId;
+ };
+}
+
+add_test(function test_saveAppStats() {
+ var cachedStats = NetworkStatsService.cachedStats;
+ var timestamp = NetworkStatsService.cachedStatsDate.getTime();
+
+ // Create to fake nsINetworkInfos. As nsINetworkInfo can not be instantiated,
+ // these two vars will emulate it by filling the properties that will be used.
+ var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
+ var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
+
+ // Insert fake mobile network info in NetworkStatsService
+ var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
+
+ do_check_eq(Object.keys(cachedStats).length, 0);
+
+ nssProxy.saveAppStats(1, false, wifi, timestamp, 10, 20, false,
+ function (success, message) {
+ do_check_eq(success, true);
+ nssProxy.saveAppStats(1, false, mobile, timestamp, 10, 20, false,
+ function (success, message) {
+ var key1 = 1 + "" + false + "" + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
+ var key2 = 1 + "" + false + "" + mobileNetId + "";
+
+ do_check_eq(Object.keys(cachedStats).length, 2);
+ do_check_eq(cachedStats[key1].appId, 1);
+ do_check_eq(cachedStats[key1].isInBrowser, false);
+ do_check_eq(cachedStats[key1].serviceType.length, 0);
+ do_check_eq(cachedStats[key1].networkId, wifi.id);
+ do_check_eq(cachedStats[key1].networkType, wifi.type);
+ do_check_eq(cachedStats[key1].date.getTime(), timestamp);
+ do_check_eq(cachedStats[key1].rxBytes, 10);
+ do_check_eq(cachedStats[key1].txBytes, 20);
+ do_check_eq(cachedStats[key2].appId, 1);
+ do_check_eq(cachedStats[key1].serviceType.length, 0);
+ do_check_eq(cachedStats[key2].networkId, mobile.id);
+ do_check_eq(cachedStats[key2].networkType, mobile.type);
+ do_check_eq(cachedStats[key2].date.getTime(), timestamp);
+ do_check_eq(cachedStats[key2].rxBytes, 10);
+ do_check_eq(cachedStats[key2].txBytes, 20);
+
+ run_next_test();
+ });
+ });
+});
+
+add_test(function test_saveServiceStats() {
+ var timestamp = NetworkStatsService.cachedStatsDate.getTime();
+
+ // Create to fake nsINetworkInfos. As nsINetworkInfo can not be instantiated,
+ // these two vars will emulate it by filling the properties that will be used.
+ var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
+ var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
+
+ // Insert fake mobile network info in NetworkStatsService
+ var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
+
+ NetworkStatsService.updateCachedStats(function (success, msg) {
+ do_check_eq(success, true);
+
+ var cachedStats = NetworkStatsService.cachedStats;
+ do_check_eq(Object.keys(cachedStats).length, 0);
+
+ var serviceType = 'FakeType';
+ nssProxy.saveServiceStats(serviceType, wifi, timestamp, 10, 20, false,
+ function (success, message) {
+ do_check_eq(success, true);
+ nssProxy.saveServiceStats(serviceType, mobile, timestamp, 10, 20, false,
+ function (success, message) {
+ do_check_eq(success, true);
+ var key1 = 0 + "" + false + "" + serviceType +
+ NetworkStatsService.getNetworkId(wifi.id, wifi.type);
+ var key2 = 0 + "" + false + "" + serviceType + mobileNetId + "";
+
+ do_check_eq(Object.keys(cachedStats).length, 2);
+ do_check_eq(cachedStats[key1].appId, 0);
+ do_check_eq(cachedStats[key1].isInBrowser, false);
+ do_check_eq(cachedStats[key1].serviceType, serviceType);
+ do_check_eq(cachedStats[key1].networkId, wifi.id);
+ do_check_eq(cachedStats[key1].networkType, wifi.type);
+ do_check_eq(cachedStats[key1].date.getTime(), timestamp);
+ do_check_eq(cachedStats[key1].rxBytes, 10);
+ do_check_eq(cachedStats[key1].txBytes, 20);
+ do_check_eq(cachedStats[key2].appId, 0);
+ do_check_eq(cachedStats[key1].serviceType, serviceType);
+ do_check_eq(cachedStats[key2].networkId, mobile.id);
+ do_check_eq(cachedStats[key2].networkType, mobile.type);
+ do_check_eq(cachedStats[key2].date.getTime(), timestamp);
+ do_check_eq(cachedStats[key2].rxBytes, 10);
+ do_check_eq(cachedStats[key2].txBytes, 20);
+
+ run_next_test();
+ });
+ });
+ });
+});
+
+add_test(function test_saveStatsWithDifferentDates() {
+ var today = NetworkStatsService.cachedStatsDate;
+ var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000));
+
+ var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
+
+ NetworkStatsService.updateCachedStats(function (success, message) {
+ do_check_eq(success, true);
+
+ do_check_eq(Object.keys(NetworkStatsService.cachedStats).length, 0);
+ nssProxy.saveAppStats(1, false, mobile, today.getTime(), 10, 20, false,
+ function (success, message) {
+ do_check_eq(success, true);
+ nssProxy.saveAppStats(2, false, mobile, tomorrow.getTime(), 30, 40, false,
+ function (success, message) {
+ do_check_eq(success, true);
+
+ var cachedStats = NetworkStatsService.cachedStats;
+ var key = 2 + "" + false + "" +
+ NetworkStatsService.getNetworkId(mobile.id, mobile.type);
+ do_check_eq(Object.keys(cachedStats).length, 1);
+ do_check_eq(cachedStats[key].appId, 2);
+ do_check_eq(cachedStats[key].isInBrowser, false);
+ do_check_eq(cachedStats[key].networkId, mobile.id);
+ do_check_eq(cachedStats[key].networkType, mobile.type);
+ do_check_eq(cachedStats[key].date.getTime(), tomorrow.getTime());
+ do_check_eq(cachedStats[key].rxBytes, 30);
+ do_check_eq(cachedStats[key].txBytes, 40);
+
+ run_next_test();
+ });
+ });
+ });
+});
+
+add_test(function test_saveStatsWithMaxCachedTraffic() {
+ var timestamp = NetworkStatsService.cachedStatsDate.getTime();
+ var maxtraffic = NetworkStatsService.maxCachedTraffic;
+ var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
+
+ NetworkStatsService.updateCachedStats(function (success, message) {
+ do_check_eq(success, true);
+
+ var cachedStats = NetworkStatsService.cachedStats;
+ do_check_eq(Object.keys(cachedStats).length, 0);
+ nssProxy.saveAppStats(1, false, wifi, timestamp, 10, 20, false,
+ function (success, message) {
+ do_check_eq(success, true);
+ do_check_eq(Object.keys(cachedStats).length, 1);
+ nssProxy.saveAppStats(1, false, wifi, timestamp, maxtraffic, 20, false,
+ function (success, message) {
+ do_check_eq(success, true);
+ do_check_eq(Object.keys(cachedStats).length, 0);
+
+ run_next_test();
+ });
+ });
+ });
+});
+
+add_test(function test_saveAppStats() {
+ var cachedStats = NetworkStatsService.cachedStats;
+ var timestamp = NetworkStatsService.cachedStatsDate.getTime();
+
+ // Create to fake nsINetworkInfo. As nsINetworkInfo can not
+ // be instantiated, these two vars will emulate it by filling the properties
+ // that will be used.
+ var wifi = {type: Ci.nsINetworkInfo.NETWORK_TYPE_WIFI, id: "0"};
+ var mobile = {type: Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, id: "1234"};
+
+ // Insert fake mobile network interface in NetworkStatsService
+ var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
+
+ do_check_eq(Object.keys(cachedStats).length, 0);
+
+ nssProxy.saveAppStats(1, false, wifi, timestamp, 10, 20, false, { notify:
+ function (success, message) {
+ do_check_eq(success, true);
+ var iterations = 10;
+ var counter = 0;
+ var callback = function (success, message) {
+ if (counter == iterations - 1)
+ run_next_test();
+ counter++;
+ };
+
+ for (var i = 0; i < iterations; i++) {
+ nssProxy.saveAppStats(1, false, mobile, timestamp, 10, 20, false, callback);
+ }
+ }});
+});
+
+function run_test() {
+ do_get_profile();
+
+ Cu.import("resource://gre/modules/NetworkStatsService.jsm");
+
+ // Function convertNetworkInfo of NetworkStatsService causes errors when dealing
+ // with RIL to get the iccid, so overwrite it.
+ mokConvertNetworkInfo();
+
+ run_next_test();
+}
diff --git a/dom/network/tests/unit_stats/xpcshell.ini b/dom/network/tests/unit_stats/xpcshell.ini
new file mode 100644
index 000000000..9b69ab755
--- /dev/null
+++ b/dom/network/tests/unit_stats/xpcshell.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+head =
+tail =
+
+[test_networkstats_service.js]
+[test_networkstats_service_proxy.js]
+[test_networkstats_db.js]