diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/network/tests | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-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')
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] |