diff options
Diffstat (limited to 'services/common/tests/unit/head_helpers.js')
-rw-r--r-- | services/common/tests/unit/head_helpers.js | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/services/common/tests/unit/head_helpers.js b/services/common/tests/unit/head_helpers.js new file mode 100644 index 0000000000..ab515fdd7e --- /dev/null +++ b/services/common/tests/unit/head_helpers.js @@ -0,0 +1,303 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* import-globals-from head_global.js */ + +var { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm"); +var { CommonUtils } = ChromeUtils.import("resource://services-common/utils.js"); +var { + HTTP_400, + HTTP_401, + HTTP_402, + HTTP_403, + HTTP_404, + HTTP_405, + HTTP_406, + HTTP_407, + HTTP_408, + HTTP_409, + HTTP_410, + HTTP_411, + HTTP_412, + HTTP_413, + HTTP_414, + HTTP_415, + HTTP_417, + HTTP_500, + HTTP_501, + HTTP_502, + HTTP_503, + HTTP_504, + HTTP_505, + HttpError, + HttpServer, +} = ChromeUtils.import("resource://testing-common/httpd.js"); +var { getTestLogger, initTestLogging } = ChromeUtils.import( + "resource://testing-common/services/common/logging.js" +); +var { MockRegistrar } = ChromeUtils.import( + "resource://testing-common/MockRegistrar.jsm" +); +var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); + +function do_check_empty(obj) { + do_check_attribute_count(obj, 0); +} + +function do_check_attribute_count(obj, c) { + Assert.equal(c, Object.keys(obj).length); +} + +function do_check_throws(aFunc, aResult) { + try { + aFunc(); + } catch (e) { + Assert.equal(e.result, aResult); + return; + } + do_throw("Expected result " + aResult + ", none thrown."); +} + +/** + * Test whether specified function throws exception with expected + * result. + * + * @param func + * Function to be tested. + * @param message + * Message of expected exception. <code>null</code> for no throws. + */ +function do_check_throws_message(aFunc, aResult) { + try { + aFunc(); + } catch (e) { + Assert.equal(e.message, aResult); + return; + } + do_throw("Expected an error, none thrown."); +} + +/** + * Print some debug message to the console. All arguments will be printed, + * separated by spaces. + * + * @param [arg0, arg1, arg2, ...] + * Any number of arguments to print out + * @usage _("Hello World") -> prints "Hello World" + * @usage _(1, 2, 3) -> prints "1 2 3" + */ +var _ = function(some, debug, text, to) { + print(Array.from(arguments).join(" ")); +}; + +function httpd_setup(handlers, port = -1) { + let server = new HttpServer(); + for (let path in handlers) { + server.registerPathHandler(path, handlers[path]); + } + try { + server.start(port); + } catch (ex) { + _("=========================================="); + _("Got exception starting HTTP server on port " + port); + _("Error: " + Log.exceptionStr(ex)); + _("Is there a process already listening on port " + port + "?"); + _("=========================================="); + do_throw(ex); + } + + // Set the base URI for convenience. + let i = server.identity; + server.baseURI = + i.primaryScheme + "://" + i.primaryHost + ":" + i.primaryPort; + + return server; +} + +function httpd_handler(statusCode, status, body) { + return function handler(request, response) { + _("Processing request"); + // Allow test functions to inspect the request. + request.body = readBytesFromInputStream(request.bodyInputStream); + handler.request = request; + + response.setStatusLine(request.httpVersion, statusCode, status); + if (body) { + response.bodyOutputStream.write(body, body.length); + } + }; +} + +function promiseStopServer(server) { + return new Promise(resolve => server.stop(resolve)); +} + +/* + * Read bytes string from an nsIInputStream. If 'count' is omitted, + * all available input is read. + */ +function readBytesFromInputStream(inputStream, count) { + if (!count) { + count = inputStream.available(); + } + if (!count) { + return ""; + } + return NetUtil.readInputStreamToString(inputStream, count, { + charset: "UTF-8", + }); +} + +function writeBytesToOutputStream(outputStream, string) { + if (!string) { + return; + } + let converter = Cc[ + "@mozilla.org/intl/converter-output-stream;1" + ].createInstance(Ci.nsIConverterOutputStream); + converter.init(outputStream, "UTF-8"); + converter.writeString(string); + converter.close(); +} + +/* + * Ensure exceptions from inside callbacks leads to test failures. + */ +function ensureThrows(func) { + return function() { + try { + func.apply(this, arguments); + } catch (ex) { + do_throw(ex); + } + }; +} + +/** + * Proxy auth helpers. + */ + +/** + * Fake a PAC to prompt a channel replacement. + */ +var PACSystemSettings = { + QueryInterface: ChromeUtils.generateQI(["nsISystemProxySettings"]), + + // Replace this URI for each test to avoid caching. We want to ensure that + // each test gets a completely fresh setup. + mainThreadOnly: true, + PACURI: null, + getProxyForURI: function getProxyForURI(aURI) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, +}; + +var fakePACCID; +function installFakePAC() { + _("Installing fake PAC."); + fakePACCID = MockRegistrar.register( + "@mozilla.org/system-proxy-settings;1", + PACSystemSettings + ); +} + +function uninstallFakePAC() { + _("Uninstalling fake PAC."); + MockRegistrar.unregister(fakePACCID); +} + +function _eventsTelemetrySnapshot(component, source) { + const { Services } = ChromeUtils.import( + "resource://gre/modules/Services.jsm" + ); + const TELEMETRY_CATEGORY_ID = "uptake.remotecontent.result"; + const snapshot = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_ALL_CHANNELS, + true + ); + const parentEvents = snapshot.parent || []; + return ( + parentEvents + // Transform raw event data to objects. + .map(([i, category, method, object, value, extras]) => { + return { category, method, object, value, extras }; + }) + // Keep only for the specified component and source. + .filter( + e => + e.category == TELEMETRY_CATEGORY_ID && + e.object == component && + e.extras.source == source + ) + // Return total number of events received by status, to mimic histograms snapshots. + .reduce((acc, e) => { + acc[e.value] = (acc[e.value] || 0) + 1; + return acc; + }, {}) + ); +} + +function getUptakeTelemetrySnapshot(key) { + const { Services } = ChromeUtils.import( + "resource://gre/modules/Services.jsm" + ); + const TELEMETRY_HISTOGRAM_ID = "UPTAKE_REMOTE_CONTENT_RESULT_1"; + const TELEMETRY_COMPONENT = "remotesettings"; + const histogram = Services.telemetry + .getKeyedHistogramById(TELEMETRY_HISTOGRAM_ID) + .snapshot()[key]; + const events = _eventsTelemetrySnapshot(TELEMETRY_COMPONENT, key); + return { histogram, events }; +} + +function checkUptakeTelemetry(snapshot1, snapshot2, expectedIncrements) { + const { UptakeTelemetry } = ChromeUtils.import( + "resource://services-common/uptake-telemetry.js" + ); + const STATUSES = Object.values(UptakeTelemetry.HISTOGRAM_LABELS); + + for (const status of STATUSES) { + const key = STATUSES.indexOf(status); + const expected = expectedIncrements[status] || 0; + // Check histogram increments. + let value1 = + (snapshot1 && snapshot1.histogram && snapshot1.histogram.values[key]) || + 0; + let value2 = + (snapshot2 && snapshot2.histogram && snapshot2.histogram.values[key]) || + 0; + let actual = value2 - value1; + equal(expected, actual, `check histogram values for ${status}`); + // Check events increments. + value1 = + (snapshot1 && snapshot1.histogram && snapshot1.histogram.values[key]) || + 0; + value2 = + (snapshot2 && snapshot2.histogram && snapshot2.histogram.values[key]) || + 0; + actual = value2 - value1; + equal(expected, actual, `check events for ${status}`); + } +} + +async function withFakeChannel(channel, f) { + const module = ChromeUtils.import( + "resource://services-common/uptake-telemetry.js", + null + ); + const oldPolicy = module.Policy; + module.Policy = { + ...oldPolicy, + getChannel: () => channel, + }; + try { + return await f(); + } finally { + module.Policy = oldPolicy; + } +} + +function arrayEqual(a, b) { + return JSON.stringify(a) == JSON.stringify(b); +} |