/* 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. null 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); }