diff options
Diffstat (limited to 'toolkit/components/telemetry/tests/unit')
26 files changed, 0 insertions, 8968 deletions
diff --git a/toolkit/components/telemetry/tests/unit/.eslintrc.js b/toolkit/components/telemetry/tests/unit/.eslintrc.js deleted file mode 100644 index d35787cd2..000000000 --- a/toolkit/components/telemetry/tests/unit/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -module.exports = { - "extends": [ - "../../../../../testing/xpcshell/xpcshell.eslintrc.js" - ] -}; diff --git a/toolkit/components/telemetry/tests/unit/TelemetryArchiveTesting.jsm b/toolkit/components/telemetry/tests/unit/TelemetryArchiveTesting.jsm deleted file mode 100644 index 9be82c883..000000000 --- a/toolkit/components/telemetry/tests/unit/TelemetryArchiveTesting.jsm +++ /dev/null @@ -1,86 +0,0 @@ -const {utils: Cu} = Components; -Cu.import("resource://gre/modules/TelemetryArchive.jsm"); -Cu.import("resource://testing-common/Assert.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/TelemetryController.jsm"); - -this.EXPORTED_SYMBOLS = [ - "TelemetryArchiveTesting", -]; - -function checkForProperties(ping, expected) { - for (let [props, val] of expected) { - let test = ping; - for (let prop of props) { - test = test[prop]; - if (test === undefined) { - return false; - } - } - if (test !== val) { - return false; - } - } - return true; -} - -/** - * A helper object that allows test code to check whether a telemetry ping - * was properly saved. To use, first initialize to collect the starting pings - * and then check for new ping data. - */ -function Checker() { -} -Checker.prototype = { - promiseInit: function() { - this._pingMap = new Map(); - return TelemetryArchive.promiseArchivedPingList().then((plist) => { - for (let ping of plist) { - this._pingMap.set(ping.id, ping); - } - }); - }, - - /** - * Find and return a new ping with certain properties. - * - * @param expected: an array of [['prop'...], 'value'] to check - * For example: - * [ - * [['environment', 'build', 'applicationId'], '20150101010101'], - * [['version'], 1], - * [['metadata', 'OOMAllocationSize'], 123456789], - * ] - * @returns a matching ping if found, or null - */ - promiseFindPing: Task.async(function*(type, expected) { - let candidates = []; - let plist = yield TelemetryArchive.promiseArchivedPingList(); - for (let ping of plist) { - if (this._pingMap.has(ping.id)) { - continue; - } - if (ping.type == type) { - candidates.push(ping); - } - } - - for (let candidate of candidates) { - let ping = yield TelemetryArchive.promiseArchivedPingById(candidate.id); - if (checkForProperties(ping, expected)) { - return ping; - } - } - return null; - }), -}; - -const TelemetryArchiveTesting = { - setup: function() { - Services.prefs.setCharPref("toolkit.telemetry.log.level", "Trace"); - Services.prefs.setBoolPref("toolkit.telemetry.archive.enabled", true); - }, - - Checker: Checker, -}; diff --git a/toolkit/components/telemetry/tests/unit/engine.xml b/toolkit/components/telemetry/tests/unit/engine.xml deleted file mode 100644 index 2304fcdd7..000000000 --- a/toolkit/components/telemetry/tests/unit/engine.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/"> -<ShortName>engine-telemetry</ShortName> -<Url type="text/html" method="GET" template="http://www.example.com/search"> - <Param name="q" value="{searchTerms}"/> -</Url> -</SearchPlugin> diff --git a/toolkit/components/telemetry/tests/unit/head.js b/toolkit/components/telemetry/tests/unit/head.js deleted file mode 100644 index 87afd3617..000000000 --- a/toolkit/components/telemetry/tests/unit/head.js +++ /dev/null @@ -1,319 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -var { classes: Cc, utils: Cu, interfaces: Ci, results: Cr } = Components; - -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://gre/modules/PromiseUtils.jsm", this); -Cu.import("resource://gre/modules/Task.jsm", this); -Cu.import("resource://gre/modules/FileUtils.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); -Cu.import("resource://testing-common/httpd.js", this); -Cu.import("resource://gre/modules/AppConstants.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "AddonTestUtils", - "resource://testing-common/AddonTestUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", - "resource://gre/modules/osfile.jsm"); - -const gIsWindows = AppConstants.platform == "win"; -const gIsMac = AppConstants.platform == "macosx"; -const gIsAndroid = AppConstants.platform == "android"; -const gIsGonk = false; -const gIsLinux = AppConstants.platform == "linux"; - -const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); - -const MILLISECONDS_PER_MINUTE = 60 * 1000; -const MILLISECONDS_PER_HOUR = 60 * MILLISECONDS_PER_MINUTE; -const MILLISECONDS_PER_DAY = 24 * MILLISECONDS_PER_HOUR; - -const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled"; - -const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; - -var gGlobalScope = this; - -const PingServer = { - _httpServer: null, - _started: false, - _defers: [ PromiseUtils.defer() ], - _currentDeferred: 0, - - get port() { - return this._httpServer.identity.primaryPort; - }, - - get started() { - return this._started; - }, - - registerPingHandler: function(handler) { - const wrapped = wrapWithExceptionHandler(handler); - this._httpServer.registerPrefixHandler("/submit/telemetry/", wrapped); - }, - - resetPingHandler: function() { - this.registerPingHandler((request, response) => { - let deferred = this._defers[this._defers.length - 1]; - this._defers.push(PromiseUtils.defer()); - deferred.resolve(request); - }); - }, - - start: function() { - this._httpServer = new HttpServer(); - this._httpServer.start(-1); - this._started = true; - this.clearRequests(); - this.resetPingHandler(); - }, - - stop: function() { - return new Promise(resolve => { - this._httpServer.stop(resolve); - this._started = false; - }); - }, - - clearRequests: function() { - this._defers = [ PromiseUtils.defer() ]; - this._currentDeferred = 0; - }, - - promiseNextRequest: function() { - const deferred = this._defers[this._currentDeferred++]; - // Send the ping to the consumer on the next tick, so that the completion gets - // signaled to Telemetry. - return new Promise(r => Services.tm.currentThread.dispatch(() => r(deferred.promise), - Ci.nsIThread.DISPATCH_NORMAL)); - }, - - promiseNextPing: function() { - return this.promiseNextRequest().then(request => decodeRequestPayload(request)); - }, - - promiseNextRequests: Task.async(function*(count) { - let results = []; - for (let i=0; i<count; ++i) { - results.push(yield this.promiseNextRequest()); - } - - return results; - }), - - promiseNextPings: function(count) { - return this.promiseNextRequests(count).then(requests => { - return Array.from(requests, decodeRequestPayload); - }); - }, -}; - -/** - * Decode the payload of an HTTP request into a ping. - * @param {Object} request The data representing an HTTP request (nsIHttpRequest). - * @return {Object} The decoded ping payload. - */ -function decodeRequestPayload(request) { - let s = request.bodyInputStream; - let payload = null; - let decoder = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON) - - if (request.getHeader("content-encoding") == "gzip") { - let observer = { - buffer: "", - onStreamComplete: function(loader, context, status, length, result) { - this.buffer = String.fromCharCode.apply(this, result); - } - }; - - let scs = Cc["@mozilla.org/streamConverters;1"] - .getService(Ci.nsIStreamConverterService); - let listener = Cc["@mozilla.org/network/stream-loader;1"] - .createInstance(Ci.nsIStreamLoader); - listener.init(observer); - let converter = scs.asyncConvertData("gzip", "uncompressed", - listener, null); - converter.onStartRequest(null, null); - converter.onDataAvailable(null, null, s, 0, s.available()); - converter.onStopRequest(null, null, null); - let unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Ci.nsIScriptableUnicodeConverter); - unicodeConverter.charset = "UTF-8"; - let utf8string = unicodeConverter.ConvertToUnicode(observer.buffer); - utf8string += unicodeConverter.Finish(); - payload = JSON.parse(utf8string); - } else { - payload = decoder.decodeFromStream(s, s.available()); - } - - return payload; -} - -function wrapWithExceptionHandler(f) { - function wrapper(...args) { - try { - f(...args); - } catch (ex) { - if (typeof(ex) != 'object') { - throw ex; - } - dump("Caught exception: " + ex.message + "\n"); - dump(ex.stack); - do_test_finished(); - } - } - return wrapper; -} - -function loadAddonManager(...args) { - AddonTestUtils.init(gGlobalScope); - AddonTestUtils.overrideCertDB(); - createAppInfo(...args); - - // As we're not running in application, we need to setup the features directory - // used by system add-ons. - const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "app0"], true); - AddonTestUtils.registerDirectory("XREAppFeat", distroDir); - return AddonTestUtils.promiseStartupManager(); -} - -var gAppInfo = null; - -function createAppInfo(ID="xpcshell@tests.mozilla.org", name="XPCShell", - version="1.0", platformVersion="1.0") { - AddonTestUtils.createAppInfo(ID, name, version, platformVersion); - gAppInfo = AddonTestUtils.appInfo; -} - -// Fake the timeout functions for the TelemetryScheduler. -function fakeSchedulerTimer(set, clear) { - let session = Cu.import("resource://gre/modules/TelemetrySession.jsm"); - session.Policy.setSchedulerTickTimeout = set; - session.Policy.clearSchedulerTickTimeout = clear; -} - -/** - * Fake the current date. - * This passes all received arguments to a new Date constructor and - * uses the resulting date to fake the time in Telemetry modules. - * - * @return Date The new faked date. - */ -function fakeNow(...args) { - const date = new Date(...args); - const modules = [ - Cu.import("resource://gre/modules/TelemetrySession.jsm"), - Cu.import("resource://gre/modules/TelemetryEnvironment.jsm"), - Cu.import("resource://gre/modules/TelemetryController.jsm"), - Cu.import("resource://gre/modules/TelemetryStorage.jsm"), - Cu.import("resource://gre/modules/TelemetrySend.jsm"), - Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm"), - ]; - - for (let m of modules) { - m.Policy.now = () => date; - } - - return new Date(date); -} - -function fakeMonotonicNow(ms) { - const m = Cu.import("resource://gre/modules/TelemetrySession.jsm"); - m.Policy.monotonicNow = () => ms; - return ms; -} - -// Fake the timeout functions for TelemetryController sending. -function fakePingSendTimer(set, clear) { - let module = Cu.import("resource://gre/modules/TelemetrySend.jsm"); - let obj = Cu.cloneInto({set, clear}, module, {cloneFunctions:true}); - module.Policy.setSchedulerTickTimeout = obj.set; - module.Policy.clearSchedulerTickTimeout = obj.clear; -} - -function fakeMidnightPingFuzzingDelay(delayMs) { - let module = Cu.import("resource://gre/modules/TelemetrySend.jsm"); - module.Policy.midnightPingFuzzingDelay = () => delayMs; -} - -function fakeGeneratePingId(func) { - let module = Cu.import("resource://gre/modules/TelemetryController.jsm"); - module.Policy.generatePingId = func; -} - -function fakeCachedClientId(uuid) { - let module = Cu.import("resource://gre/modules/TelemetryController.jsm"); - module.Policy.getCachedClientID = () => uuid; -} - -// Return a date that is |offset| ms in the future from |date|. -function futureDate(date, offset) { - return new Date(date.getTime() + offset); -} - -function truncateToDays(aMsec) { - return Math.floor(aMsec / MILLISECONDS_PER_DAY); -} - -// Returns a promise that resolves to true when the passed promise rejects, -// false otherwise. -function promiseRejects(promise) { - return promise.then(() => false, () => true); -} - -// Generates a random string of at least a specific length. -function generateRandomString(length) { - let string = ""; - - while (string.length < length) { - string += Math.random().toString(36); - } - - return string.substring(0, length); -} - -// Short-hand for retrieving the histogram with that id. -function getHistogram(histogramId) { - return Telemetry.getHistogramById(histogramId); -} - -// Short-hand for retrieving the snapshot of the Histogram with that id. -function getSnapshot(histogramId) { - return Telemetry.getHistogramById(histogramId).snapshot(); -} - -// Helper for setting an empty list of Environment preferences to watch. -function setEmptyPrefWatchlist() { - let TelemetryEnvironment = - Cu.import("resource://gre/modules/TelemetryEnvironment.jsm").TelemetryEnvironment; - return TelemetryEnvironment.onInitialized().then(() => { - TelemetryEnvironment.testWatchPreferences(new Map()); - }); -} - -if (runningInParent) { - // Set logging preferences for all the tests. - Services.prefs.setCharPref("toolkit.telemetry.log.level", "Trace"); - // Telemetry archiving should be on. - Services.prefs.setBoolPref("toolkit.telemetry.archive.enabled", true); - // Telemetry xpcshell tests cannot show the infobar. - Services.prefs.setBoolPref("datareporting.policy.dataSubmissionPolicyBypassNotification", true); - // FHR uploads should be enabled. - Services.prefs.setBoolPref("datareporting.healthreport.uploadEnabled", true); - - fakePingSendTimer((callback, timeout) => { - Services.tm.mainThread.dispatch(() => callback(), Ci.nsIThread.DISPATCH_NORMAL); - }, - () => {}); - - do_register_cleanup(() => TelemetrySend.shutdown()); -} - -TelemetryController.testInitLogging(); - -// Avoid timers interrupting test behavior. -fakeSchedulerTimer(() => {}, () => {}); -// Make pind sending predictable. -fakeMidnightPingFuzzingDelay(0); diff --git a/toolkit/components/telemetry/tests/unit/test_ChildHistograms.js b/toolkit/components/telemetry/tests/unit/test_ChildHistograms.js deleted file mode 100644 index 11d730499..000000000 --- a/toolkit/components/telemetry/tests/unit/test_ChildHistograms.js +++ /dev/null @@ -1,107 +0,0 @@ - -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetrySession.jsm", this); -Cu.import("resource://gre/modules/PromiseUtils.jsm", this); -Cu.import("resource://testing-common/ContentTaskUtils.jsm", this); - -const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload"; -const MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD = "Telemetry:GetChildPayload"; -const MESSAGE_CHILD_TEST_DONE = "ChildTest:Done"; - -const PLATFORM_VERSION = "1.9.2"; -const APP_VERSION = "1"; -const APP_ID = "xpcshell@tests.mozilla.org"; -const APP_NAME = "XPCShell"; - -function run_child_test() { - // Setup histograms with some fixed values. - let flagHist = Telemetry.getHistogramById("TELEMETRY_TEST_FLAG"); - flagHist.add(1); - let countHist = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT"); - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_COUNT", false); - countHist.add(); - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_COUNT", true); - countHist.add(); - countHist.add(); - let categHist = Telemetry.getHistogramById("TELEMETRY_TEST_CATEGORICAL"); - categHist.add("Label2"); - categHist.add("Label3"); - - let flagKeyed = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_FLAG"); - flagKeyed.add("a", 1); - flagKeyed.add("b", 1); - let countKeyed = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT"); - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_KEYED_COUNT", false); - countKeyed.add("a"); - countKeyed.add("b"); - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_KEYED_COUNT", true); - countKeyed.add("a"); - countKeyed.add("b"); - countKeyed.add("b"); -} - -function check_histogram_values(payload) { - const hs = payload.histograms; - Assert.ok("TELEMETRY_TEST_COUNT" in hs, "Should have count test histogram."); - Assert.ok("TELEMETRY_TEST_FLAG" in hs, "Should have flag test histogram."); - Assert.ok("TELEMETRY_TEST_CATEGORICAL" in hs, "Should have categorical test histogram."); - Assert.equal(hs["TELEMETRY_TEST_COUNT"].sum, 2, - "Count test histogram should have the right value."); - Assert.equal(hs["TELEMETRY_TEST_FLAG"].sum, 1, - "Flag test histogram should have the right value."); - Assert.equal(hs["TELEMETRY_TEST_CATEGORICAL"].sum, 3, - "Categorical test histogram should have the right sum."); - - const kh = payload.keyedHistograms; - Assert.ok("TELEMETRY_TEST_KEYED_COUNT" in kh, "Should have keyed count test histogram."); - Assert.ok("TELEMETRY_TEST_KEYED_FLAG" in kh, "Should have keyed flag test histogram."); - Assert.equal(kh["TELEMETRY_TEST_KEYED_COUNT"]["a"].sum, 1, - "Keyed count test histogram should have the right value."); - Assert.equal(kh["TELEMETRY_TEST_KEYED_COUNT"]["b"].sum, 2, - "Keyed count test histogram should have the right value."); - Assert.equal(kh["TELEMETRY_TEST_KEYED_FLAG"]["a"].sum, 1, - "Keyed flag test histogram should have the right value."); - Assert.equal(kh["TELEMETRY_TEST_KEYED_FLAG"]["b"].sum, 1, - "Keyed flag test histogram should have the right value."); -} - -add_task(function*() { - if (!runningInParent) { - TelemetryController.testSetupContent(); - run_child_test(); - dump("... done with child test\n"); - do_send_remote_message(MESSAGE_CHILD_TEST_DONE); - return; - } - - // Setup. - do_get_profile(true); - loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION); - Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); - yield TelemetryController.testSetup(); - if (runningInParent) { - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - } - - // Run test in child, don't wait for it to finish. - run_test_in_child("test_ChildHistograms.js"); - yield do_await_remote_message(MESSAGE_CHILD_TEST_DONE); - - yield ContentTaskUtils.waitForCondition(() => { - let payload = TelemetrySession.getPayload("test-ping"); - return payload && - "processes" in payload && - "content" in payload.processes && - "histograms" in payload.processes.content && - "TELEMETRY_TEST_COUNT" in payload.processes.content.histograms; - }); - const payload = TelemetrySession.getPayload("test-ping"); - Assert.ok("processes" in payload, "Should have processes section"); - Assert.ok("content" in payload.processes, "Should have child process section"); - Assert.ok("histograms" in payload.processes.content, "Child process section should have histograms."); - Assert.ok("keyedHistograms" in payload.processes.content, "Child process section should have keyed histograms."); - check_histogram_values(payload.processes.content); - - do_test_finished(); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_PingAPI.js b/toolkit/components/telemetry/tests/unit/test_PingAPI.js deleted file mode 100644 index d4d79aad4..000000000 --- a/toolkit/components/telemetry/tests/unit/test_PingAPI.js +++ /dev/null @@ -1,502 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ - -// This tests the public Telemetry API for submitting pings. - -"use strict"; - -Cu.import("resource://gre/modules/ClientID.jsm", this); -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetryArchive.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); -Cu.import("resource://gre/modules/osfile.jsm", this); -Cu.import("resource://gre/modules/Task.jsm", this); -Cu.import("resource://gre/modules/Services.jsm", this); - -XPCOMUtils.defineLazyGetter(this, "gPingsArchivePath", function() { - return OS.Path.join(OS.Constants.Path.profileDir, "datareporting", "archived"); -}); - -/** - * Fakes the archive storage quota. - * @param {Integer} aArchiveQuota The new quota, in bytes. - */ -function fakeStorageQuota(aArchiveQuota) { - let storage = Cu.import("resource://gre/modules/TelemetryStorage.jsm"); - storage.Policy.getArchiveQuota = () => aArchiveQuota; -} - -/** - * Lists all the valid archived pings and their metadata, sorted by creation date. - * - * @param aFileName {String} The filename. - * @return {Object[]} A list of objects with the extracted data in the form: - * { timestamp: <number>, - * id: <string>, - * type: <string>, - * size: <integer> } - */ -var getArchivedPingsInfo = Task.async(function*() { - let dirIterator = new OS.File.DirectoryIterator(gPingsArchivePath); - let subdirs = (yield dirIterator.nextBatch()).filter(e => e.isDir); - let archivedPings = []; - - // Iterate through the subdirs of |gPingsArchivePath|. - for (let dir of subdirs) { - let fileIterator = new OS.File.DirectoryIterator(dir.path); - let files = (yield fileIterator.nextBatch()).filter(e => !e.isDir); - - // Then get a list of the files for the current subdir. - for (let f of files) { - let pingInfo = TelemetryStorage._testGetArchivedPingDataFromFileName(f.name); - if (!pingInfo) { - // This is not a valid archived ping, skip it. - continue; - } - // Find the size of the ping and then add the info to the array. - pingInfo.size = (yield OS.File.stat(f.path)).size; - archivedPings.push(pingInfo); - } - } - - // Sort the list by creation date and then return it. - archivedPings.sort((a, b) => b.timestamp - a.timestamp); - return archivedPings; -}); - -add_task(function* test_setup() { - do_get_profile(true); - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); -}); - -add_task(function* test_archivedPings() { - // TelemetryController should not be fully initialized at this point. - // Submitting pings should still work fine. - - const PINGS = [ - { - type: "test-ping-api-1", - payload: { foo: "bar"}, - dateCreated: new Date(2010, 1, 1, 10, 0, 0), - }, - { - type: "test-ping-api-2", - payload: { moo: "meh"}, - dateCreated: new Date(2010, 2, 1, 10, 0, 0), - }, - ]; - - // Submit pings and check the ping list. - let expectedPingList = []; - - for (let data of PINGS) { - fakeNow(data.dateCreated); - data.id = yield TelemetryController.submitExternalPing(data.type, data.payload); - let list = yield TelemetryArchive.promiseArchivedPingList(); - - expectedPingList.push({ - id: data.id, - type: data.type, - timestampCreated: data.dateCreated.getTime(), - }); - Assert.deepEqual(list, expectedPingList, "Archived ping list should contain submitted pings"); - } - - // Check loading the archived pings. - let checkLoadingPings = Task.async(function*() { - for (let data of PINGS) { - let ping = yield TelemetryArchive.promiseArchivedPingById(data.id); - Assert.equal(ping.id, data.id, "Archived ping should have matching id"); - Assert.equal(ping.type, data.type, "Archived ping should have matching type"); - Assert.equal(ping.creationDate, data.dateCreated.toISOString(), - "Archived ping should have matching creation date"); - } - }); - - yield checkLoadingPings(); - - // Check that we find the archived pings again by scanning after a restart. - yield TelemetryController.testReset(); - - let pingList = yield TelemetryArchive.promiseArchivedPingList(); - Assert.deepEqual(expectedPingList, pingList, - "Should have submitted pings in archive list after restart"); - yield checkLoadingPings(); - - // Write invalid pings into the archive with both valid and invalid names. - let writeToArchivedDir = Task.async(function*(dirname, filename, content, compressed) { - const dirPath = OS.Path.join(gPingsArchivePath, dirname); - yield OS.File.makeDir(dirPath, { ignoreExisting: true }); - const filePath = OS.Path.join(dirPath, filename); - const options = { tmpPath: filePath + ".tmp", noOverwrite: false }; - if (compressed) { - options.compression = "lz4"; - } - yield OS.File.writeAtomic(filePath, content, options); - }); - - const FAKE_ID1 = "10000000-0123-0123-0123-0123456789a1"; - const FAKE_ID2 = "20000000-0123-0123-0123-0123456789a2"; - const FAKE_ID3 = "20000000-0123-0123-0123-0123456789a3"; - const FAKE_TYPE = "foo"; - - // These should get rejected. - yield writeToArchivedDir("xx", "foo.json", "{}"); - yield writeToArchivedDir("2010-02", "xx.xx.xx.json", "{}"); - // This one should get picked up... - yield writeToArchivedDir("2010-02", "1." + FAKE_ID1 + "." + FAKE_TYPE + ".json", "{}"); - // ... but get overwritten by this one. - yield writeToArchivedDir("2010-02", "2." + FAKE_ID1 + "." + FAKE_TYPE + ".json", ""); - // This should get picked up fine. - yield writeToArchivedDir("2010-02", "3." + FAKE_ID2 + "." + FAKE_TYPE + ".json", ""); - // This compressed ping should get picked up fine as well. - yield writeToArchivedDir("2010-02", "4." + FAKE_ID3 + "." + FAKE_TYPE + ".jsonlz4", ""); - - expectedPingList.push({ - id: FAKE_ID1, - type: "foo", - timestampCreated: 2, - }); - expectedPingList.push({ - id: FAKE_ID2, - type: "foo", - timestampCreated: 3, - }); - expectedPingList.push({ - id: FAKE_ID3, - type: "foo", - timestampCreated: 4, - }); - expectedPingList.sort((a, b) => a.timestampCreated - b.timestampCreated); - - // Reset the TelemetryArchive so we scan the archived dir again. - yield TelemetryController.testReset(); - - // Check that we are still picking up the valid archived pings on disk, - // plus the valid ones above. - pingList = yield TelemetryArchive.promiseArchivedPingList(); - Assert.deepEqual(expectedPingList, pingList, "Should have picked up valid archived pings"); - yield checkLoadingPings(); - - // Now check that we fail to load the two invalid pings from above. - Assert.ok((yield promiseRejects(TelemetryArchive.promiseArchivedPingById(FAKE_ID1))), - "Should have rejected invalid ping"); - Assert.ok((yield promiseRejects(TelemetryArchive.promiseArchivedPingById(FAKE_ID2))), - "Should have rejected invalid ping"); -}); - -add_task(function* test_archiveCleanup() { - const PING_TYPE = "foo"; - - // Empty the archive. - yield OS.File.removeDir(gPingsArchivePath); - - Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SCAN_PING_COUNT").clear(); - Telemetry.getHistogramById("TELEMETRY_ARCHIVE_DIRECTORIES_COUNT").clear(); - // Also reset these histograms to make sure normal sized pings don't get counted. - Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_ARCHIVED").clear(); - Telemetry.getHistogramById("TELEMETRY_DISCARDED_ARCHIVED_PINGS_SIZE_MB").clear(); - - // Build the cache. Nothing should be evicted as there's no ping directory. - yield TelemetryController.testReset(); - yield TelemetryStorage.testCleanupTaskPromise(); - yield TelemetryArchive.promiseArchivedPingList(); - - let h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SCAN_PING_COUNT").snapshot(); - Assert.equal(h.sum, 0, "Telemetry must report 0 pings scanned if no archive dir exists."); - // One directory out of four was removed as well. - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTED_OLD_DIRS").snapshot(); - Assert.equal(h.sum, 0, "Telemetry must report 0 evicted dirs if no archive dir exists."); - - let expectedPrunedInfo = []; - let expectedNotPrunedInfo = []; - - let checkArchive = Task.async(function*() { - // Check that the pruned pings are not on disk anymore. - for (let prunedInfo of expectedPrunedInfo) { - yield Assert.rejects(TelemetryArchive.promiseArchivedPingById(prunedInfo.id), - "Ping " + prunedInfo.id + " should have been pruned."); - const pingPath = - TelemetryStorage._testGetArchivedPingPath(prunedInfo.id, prunedInfo.creationDate, PING_TYPE); - Assert.ok(!(yield OS.File.exists(pingPath)), "The ping should not be on the disk anymore."); - } - - // Check that the expected pings are there. - for (let expectedInfo of expectedNotPrunedInfo) { - Assert.ok((yield TelemetryArchive.promiseArchivedPingById(expectedInfo.id)), - "Ping" + expectedInfo.id + " should be in the archive."); - } - }); - - Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SESSION_PING_COUNT").clear(); - - // Create a ping which should be pruned because it is past the retention period. - let date = fakeNow(2010, 1, 1, 1, 0, 0); - let firstDate = date; - let pingId = yield TelemetryController.submitExternalPing(PING_TYPE, {}, {}); - expectedPrunedInfo.push({ id: pingId, creationDate: date }); - - // Create a ping which should be kept because it is within the retention period. - const oldestDirectoryDate = fakeNow(2010, 2, 1, 1, 0, 0); - pingId = yield TelemetryController.submitExternalPing(PING_TYPE, {}, {}); - expectedNotPrunedInfo.push({ id: pingId, creationDate: oldestDirectoryDate }); - - // Create 20 other pings which are within the retention period, but would be affected - // by the disk quota. - for (let month of [3, 4]) { - for (let minute = 0; minute < 10; minute++) { - date = fakeNow(2010, month, 1, 1, minute, 0); - pingId = yield TelemetryController.submitExternalPing(PING_TYPE, {}, {}); - expectedNotPrunedInfo.push({ id: pingId, creationDate: date }); - } - } - - // We expect all the pings we archived to be in this histogram. - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SESSION_PING_COUNT"); - Assert.equal(h.snapshot().sum, 22, "All the pings must be live-accumulated in the histogram."); - // Reset the histogram that will be populated by the archive scan. - Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTED_OLD_DIRS").clear(); - Telemetry.getHistogramById("TELEMETRY_ARCHIVE_OLDEST_DIRECTORY_AGE").clear(); - - // Move the current date 60 days ahead of the first ping. - fakeNow(futureDate(firstDate, 60 * MILLISECONDS_PER_DAY)); - // Reset TelemetryArchive and TelemetryController to start the startup cleanup. - yield TelemetryController.testReset(); - // Wait for the cleanup to finish. - yield TelemetryStorage.testCleanupTaskPromise(); - // Then scan the archived dir. - yield TelemetryArchive.promiseArchivedPingList(); - - // Check that the archive is in the correct state. - yield checkArchive(); - - // Make sure the ping count is correct after the scan (one ping was removed). - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SCAN_PING_COUNT").snapshot(); - Assert.equal(h.sum, 21, "The histogram must count all the pings in the archive."); - // One directory out of four was removed as well. - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTED_OLD_DIRS").snapshot(); - Assert.equal(h.sum, 1, "Telemetry must correctly report removed archive directories."); - // Check that the remaining directories are correctly counted. - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_DIRECTORIES_COUNT").snapshot(); - Assert.equal(h.sum, 3, "Telemetry must correctly report the remaining archive directories."); - // Check that the remaining directories are correctly counted. - const oldestAgeInMonths = 1; - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_OLDEST_DIRECTORY_AGE").snapshot(); - Assert.equal(h.sum, oldestAgeInMonths, - "Telemetry must correctly report age of the oldest directory in the archive."); - - // We need to test the archive size before we hit the quota, otherwise a special - // value is recorded. - Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SIZE_MB").clear(); - Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTED_OVER_QUOTA").clear(); - Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTING_OVER_QUOTA_MS").clear(); - - // Move the current date 60 days ahead of the second ping. - fakeNow(futureDate(oldestDirectoryDate, 60 * MILLISECONDS_PER_DAY)); - // Reset TelemetryController and TelemetryArchive. - yield TelemetryController.testReset(); - // Wait for the cleanup to finish. - yield TelemetryStorage.testCleanupTaskPromise(); - // Then scan the archived dir again. - yield TelemetryArchive.promiseArchivedPingList(); - - // Move the oldest ping to the unexpected pings list. - expectedPrunedInfo.push(expectedNotPrunedInfo.shift()); - // Check that the archive is in the correct state. - yield checkArchive(); - - // Find how much disk space the archive takes. - const archivedPingsInfo = yield getArchivedPingsInfo(); - let archiveSizeInBytes = - archivedPingsInfo.reduce((lastResult, element) => lastResult + element.size, 0); - - // Check that the correct values for quota probes are reported when no quota is hit. - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SIZE_MB").snapshot(); - Assert.equal(h.sum, Math.round(archiveSizeInBytes / 1024 / 1024), - "Telemetry must report the correct archive size."); - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTED_OVER_QUOTA").snapshot(); - Assert.equal(h.sum, 0, "Telemetry must report 0 evictions if quota is not hit."); - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTING_OVER_QUOTA_MS").snapshot(); - Assert.equal(h.sum, 0, "Telemetry must report a null elapsed time if quota is not hit."); - - // Set the quota to 80% of the space. - const testQuotaInBytes = archiveSizeInBytes * 0.8; - fakeStorageQuota(testQuotaInBytes); - - // The storage prunes archived pings until we reach 90% of the requested storage quota. - // Based on that, find how many pings should be kept. - const safeQuotaSize = testQuotaInBytes * 0.9; - let sizeInBytes = 0; - let pingsWithinQuota = []; - let pingsOutsideQuota = []; - - for (let pingInfo of archivedPingsInfo) { - sizeInBytes += pingInfo.size; - if (sizeInBytes >= safeQuotaSize) { - pingsOutsideQuota.push({ id: pingInfo.id, creationDate: new Date(pingInfo.timestamp) }); - continue; - } - pingsWithinQuota.push({ id: pingInfo.id, creationDate: new Date(pingInfo.timestamp) }); - } - - expectedNotPrunedInfo = pingsWithinQuota; - expectedPrunedInfo = expectedPrunedInfo.concat(pingsOutsideQuota); - - // Reset TelemetryArchive and TelemetryController to start the startup cleanup. - yield TelemetryController.testReset(); - yield TelemetryStorage.testCleanupTaskPromise(); - yield TelemetryArchive.promiseArchivedPingList(); - // Check that the archive is in the correct state. - yield checkArchive(); - - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_EVICTED_OVER_QUOTA").snapshot(); - Assert.equal(h.sum, pingsOutsideQuota.length, - "Telemetry must correctly report the over quota pings evicted from the archive."); - h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SIZE_MB").snapshot(); - Assert.equal(h.sum, 300, "Archive quota was hit, a special size must be reported."); - - // Trigger a cleanup again and make sure we're not removing anything. - yield TelemetryController.testReset(); - yield TelemetryStorage.testCleanupTaskPromise(); - yield TelemetryArchive.promiseArchivedPingList(); - yield checkArchive(); - - const OVERSIZED_PING_ID = "9b21ec8f-f762-4d28-a2c1-44e1c4694f24"; - // Create and archive an oversized, uncompressed, ping. - const OVERSIZED_PING = { - id: OVERSIZED_PING_ID, - type: PING_TYPE, - creationDate: (new Date()).toISOString(), - // Generate a ~2MB string to use as the payload. - payload: generateRandomString(2 * 1024 * 1024) - }; - yield TelemetryArchive.promiseArchivePing(OVERSIZED_PING); - - // Get the size of the archived ping. - const oversizedPingPath = - TelemetryStorage._testGetArchivedPingPath(OVERSIZED_PING.id, new Date(OVERSIZED_PING.creationDate), PING_TYPE) + "lz4"; - const archivedPingSizeMB = Math.floor((yield OS.File.stat(oversizedPingPath)).size / 1024 / 1024); - - // We expect the oversized ping to be pruned when scanning the archive. - expectedPrunedInfo.push({ id: OVERSIZED_PING_ID, creationDate: new Date(OVERSIZED_PING.creationDate) }); - - // Scan the archive. - yield TelemetryController.testReset(); - yield TelemetryStorage.testCleanupTaskPromise(); - yield TelemetryArchive.promiseArchivedPingList(); - // The following also checks that non oversized pings are not removed. - yield checkArchive(); - - // Make sure we're correctly updating the related histograms. - h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_ARCHIVED").snapshot(); - Assert.equal(h.sum, 1, "Telemetry must report 1 oversized ping in the archive."); - h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_ARCHIVED_PINGS_SIZE_MB").snapshot(); - Assert.equal(h.counts[archivedPingSizeMB], 1, - "Telemetry must report the correct size for the oversized ping."); -}); - -add_task(function* test_clientId() { - // Check that a ping submitted after the delayed telemetry initialization completed - // should get a valid client id. - yield TelemetryController.testReset(); - const clientId = yield ClientID.getClientID(); - - let id = yield TelemetryController.submitExternalPing("test-type", {}, {addClientId: true}); - let ping = yield TelemetryArchive.promiseArchivedPingById(id); - - Assert.ok(!!ping, "Should have loaded the ping."); - Assert.ok("clientId" in ping, "Ping should have a client id."); - Assert.ok(UUID_REGEX.test(ping.clientId), "Client id is in UUID format."); - Assert.equal(ping.clientId, clientId, "Ping client id should match the global client id."); - - // We should have cached the client id now. Lets confirm that by - // checking the client id on a ping submitted before the async - // controller setup is finished. - let promiseSetup = TelemetryController.testReset(); - id = yield TelemetryController.submitExternalPing("test-type", {}, {addClientId: true}); - ping = yield TelemetryArchive.promiseArchivedPingById(id); - Assert.equal(ping.clientId, clientId); - - // Finish setup. - yield promiseSetup; -}); - -add_task(function* test_InvalidPingType() { - const TYPES = [ - "a", - "-", - "¿€€€?", - "-foo-", - "-moo", - "zoo-", - ".bar", - "asfd.asdf", - ]; - - for (let type of TYPES) { - let histogram = Telemetry.getKeyedHistogramById("TELEMETRY_INVALID_PING_TYPE_SUBMITTED"); - Assert.equal(histogram.snapshot(type).sum, 0, - "Should not have counted this invalid ping yet: " + type); - Assert.ok(promiseRejects(TelemetryController.submitExternalPing(type, {})), - "Ping type should have been rejected."); - Assert.equal(histogram.snapshot(type).sum, 1, - "Should have counted this as an invalid ping type."); - } -}); - -add_task(function* test_InvalidPayloadType() { - const PAYLOAD_TYPES = [ - 19, - "string", - [1, 2, 3, 4], - null, - undefined, - ]; - - let histogram = Telemetry.getHistogramById("TELEMETRY_INVALID_PAYLOAD_SUBMITTED"); - for (let i = 0; i < PAYLOAD_TYPES.length; i++) { - histogram.clear(); - Assert.equal(histogram.snapshot().sum, 0, - "Should not have counted this invalid payload yet: " + JSON.stringify(PAYLOAD_TYPES[i])); - Assert.ok(yield promiseRejects(TelemetryController.submitExternalPing("payload-test", PAYLOAD_TYPES[i])), - "Payload type should have been rejected."); - Assert.equal(histogram.snapshot().sum, 1, - "Should have counted this as an invalid payload type."); - } -}); - -add_task(function* test_currentPingData() { - yield TelemetryController.testSetup(); - - // Setup test data. - let h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTOUT"); - h.clear(); - h.add(1); - let k = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT"); - k.clear(); - k.add("a", 1); - - // Get current ping data objects and check that their data is sane. - for (let subsession of [true, false]) { - let ping = TelemetryController.getCurrentPingData(subsession); - - Assert.ok(!!ping, "Should have gotten a ping."); - Assert.equal(ping.type, "main", "Ping should have correct type."); - const expectedReason = subsession ? "gather-subsession-payload" : "gather-payload"; - Assert.equal(ping.payload.info.reason, expectedReason, "Ping should have the correct reason."); - - let id = "TELEMETRY_TEST_RELEASE_OPTOUT"; - Assert.ok(id in ping.payload.histograms, "Payload should have test count histogram."); - Assert.equal(ping.payload.histograms[id].sum, 1, "Test count value should match."); - id = "TELEMETRY_TEST_KEYED_RELEASE_OPTOUT"; - Assert.ok(id in ping.payload.keyedHistograms, "Payload should have keyed test histogram."); - Assert.equal(ping.payload.keyedHistograms[id]["a"].sum, 1, "Keyed test value should match."); - } -}); - -add_task(function* test_shutdown() { - yield TelemetryController.testShutdown(); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_SubsessionChaining.js b/toolkit/components/telemetry/tests/unit/test_SubsessionChaining.js deleted file mode 100644 index c86fb0499..000000000 --- a/toolkit/components/telemetry/tests/unit/test_SubsessionChaining.js +++ /dev/null @@ -1,236 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ - -Cu.import("resource://gre/modules/Preferences.jsm", this); -Cu.import("resource://gre/modules/Promise.jsm", this); -Cu.import("resource://gre/modules/Task.jsm", this); -Cu.import("resource://gre/modules/TelemetryArchive.jsm", this); -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this); -Cu.import("resource://gre/modules/osfile.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); - -const MS_IN_ONE_HOUR = 60 * 60 * 1000; -const MS_IN_ONE_DAY = 24 * MS_IN_ONE_HOUR; - -const PREF_BRANCH = "toolkit.telemetry."; -const PREF_ARCHIVE_ENABLED = PREF_BRANCH + "archive.enabled"; - -const REASON_ABORTED_SESSION = "aborted-session"; -const REASON_DAILY = "daily"; -const REASON_ENVIRONMENT_CHANGE = "environment-change"; -const REASON_SHUTDOWN = "shutdown"; - -XPCOMUtils.defineLazyGetter(this, "DATAREPORTING_PATH", function() { - return OS.Path.join(OS.Constants.Path.profileDir, "datareporting"); -}); - -var promiseValidateArchivedPings = Task.async(function*(aExpectedReasons) { - // The list of ping reasons which mark the session end (and must reset the subsession - // count). - const SESSION_END_PING_REASONS = new Set([ REASON_ABORTED_SESSION, REASON_SHUTDOWN ]); - - let list = yield TelemetryArchive.promiseArchivedPingList(); - - // We're just interested in the "main" pings. - list = list.filter(p => p.type == "main"); - - Assert.equal(aExpectedReasons.length, list.length, "All the expected pings must be received."); - - let previousPing = yield TelemetryArchive.promiseArchivedPingById(list[0].id); - Assert.equal(aExpectedReasons.shift(), previousPing.payload.info.reason, - "Telemetry should only get pings with expected reasons."); - Assert.equal(previousPing.payload.info.previousSessionId, null, - "The first session must report a null previous session id."); - Assert.equal(previousPing.payload.info.previousSubsessionId, null, - "The first subsession must report a null previous subsession id."); - Assert.equal(previousPing.payload.info.profileSubsessionCounter, 1, - "profileSubsessionCounter must be 1 the first time."); - Assert.equal(previousPing.payload.info.subsessionCounter, 1, - "subsessionCounter must be 1 the first time."); - - let expectedSubsessionCounter = 1; - let expectedPreviousSessionId = previousPing.payload.info.sessionId; - - for (let i = 1; i < list.length; i++) { - let currentPing = yield TelemetryArchive.promiseArchivedPingById(list[i].id); - let currentInfo = currentPing.payload.info; - let previousInfo = previousPing.payload.info; - do_print("Archive entry " + i + " - id: " + currentPing.id + ", reason: " + currentInfo.reason); - - Assert.equal(aExpectedReasons.shift(), currentInfo.reason, - "Telemetry should only get pings with expected reasons."); - Assert.equal(currentInfo.previousSessionId, expectedPreviousSessionId, - "Telemetry must correctly chain session identifiers."); - Assert.equal(currentInfo.previousSubsessionId, previousInfo.subsessionId, - "Telemetry must correctly chain subsession identifiers."); - Assert.equal(currentInfo.profileSubsessionCounter, previousInfo.profileSubsessionCounter + 1, - "Telemetry must correctly track the profile subsessions count."); - Assert.equal(currentInfo.subsessionCounter, expectedSubsessionCounter, - "The subsession counter should be monotonically increasing."); - - // Store the current ping as previous. - previousPing = currentPing; - // Reset the expected subsession counter, if required. Otherwise increment the expected - // subsession counter. - // If this is the final subsession of a session we need to update expected values accordingly. - if (SESSION_END_PING_REASONS.has(currentInfo.reason)) { - expectedSubsessionCounter = 1; - expectedPreviousSessionId = currentInfo.sessionId; - } else { - expectedSubsessionCounter++; - } - } -}); - -add_task(function* test_setup() { - do_test_pending(); - - // Addon manager needs a profile directory - do_get_profile(); - loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - - Preferences.set(PREF_TELEMETRY_ENABLED, true); -}); - -add_task(function* test_subsessionsChaining() { - if (gIsAndroid) { - // We don't support subsessions yet on Android, so skip the next checks. - return; - } - - const PREF_TEST = PREF_BRANCH + "test.pref1"; - const PREFS_TO_WATCH = new Map([ - [PREF_TEST, {what: TelemetryEnvironment.RECORD_PREF_VALUE}], - ]); - Preferences.reset(PREF_TEST); - - // Fake the clock data to manually trigger an aborted-session ping and a daily ping. - // This is also helpful to make sure we get the archived pings in an expected order. - let now = fakeNow(2009, 9, 18, 0, 0, 0); - let monotonicNow = fakeMonotonicNow(1000); - - let moveClockForward = (minutes) => { - let ms = minutes * MILLISECONDS_PER_MINUTE; - now = fakeNow(futureDate(now, ms)); - monotonicNow = fakeMonotonicNow(monotonicNow + ms); - } - - // Keep track of the ping reasons we're expecting in this test. - let expectedReasons = []; - - // Start and shut down Telemetry. We expect a shutdown ping with profileSubsessionCounter: 1, - // subsessionCounter: 1, subsessionId: A, and previousSubsessionId: null to be archived. - yield TelemetryController.testSetup(); - yield TelemetryController.testShutdown(); - expectedReasons.push(REASON_SHUTDOWN); - - // Start Telemetry but don't wait for it to initialise before shutting down. We expect a - // shutdown ping with profileSubsessionCounter: 2, subsessionCounter: 1, subsessionId: B - // and previousSubsessionId: A to be archived. - moveClockForward(30); - TelemetryController.testReset(); - yield TelemetryController.testShutdown(); - expectedReasons.push(REASON_SHUTDOWN); - - // Start Telemetry and simulate an aborted-session ping. We expect an aborted-session ping - // with profileSubsessionCounter: 3, subsessionCounter: 1, subsessionId: C and - // previousSubsessionId: B to be archived. - let schedulerTickCallback = null; - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryController.testReset(); - moveClockForward(6); - // Trigger the an aborted session ping save. When testing,we are not saving the aborted-session - // ping as soon as Telemetry starts, otherwise we would end up with unexpected pings being - // sent when calling |TelemetryController.testReset()|, thus breaking some tests. - Assert.ok(!!schedulerTickCallback); - yield schedulerTickCallback(); - expectedReasons.push(REASON_ABORTED_SESSION); - - // Start Telemetry and trigger an environment change through a pref modification. We expect - // an environment-change ping with profileSubsessionCounter: 4, subsessionCounter: 1, - // subsessionId: D and previousSubsessionId: C to be archived. - moveClockForward(30); - yield TelemetryController.testReset(); - TelemetryEnvironment.testWatchPreferences(PREFS_TO_WATCH); - moveClockForward(30); - Preferences.set(PREF_TEST, 1); - expectedReasons.push(REASON_ENVIRONMENT_CHANGE); - - // Shut down Telemetry. We expect a shutdown ping with profileSubsessionCounter: 5, - // subsessionCounter: 2, subsessionId: E and previousSubsessionId: D to be archived. - moveClockForward(30); - yield TelemetryController.testShutdown(); - expectedReasons.push(REASON_SHUTDOWN); - - // Start Telemetry and trigger a daily ping. We expect a daily ping with - // profileSubsessionCounter: 6, subsessionCounter: 1, subsessionId: F and - // previousSubsessionId: E to be archived. - moveClockForward(30); - yield TelemetryController.testReset(); - - // Delay the callback around midnight. - now = fakeNow(futureDate(now, MS_IN_ONE_DAY)); - // Trigger the daily ping. - yield schedulerTickCallback(); - expectedReasons.push(REASON_DAILY); - - // Trigger an environment change ping. We expect an environment-changed ping with - // profileSubsessionCounter: 7, subsessionCounter: 2, subsessionId: G and - // previousSubsessionId: F to be archived. - moveClockForward(30); - Preferences.set(PREF_TEST, 0); - expectedReasons.push(REASON_ENVIRONMENT_CHANGE); - - // Shut down Telemetry and trigger a shutdown ping. - moveClockForward(30); - yield TelemetryController.testShutdown(); - expectedReasons.push(REASON_SHUTDOWN); - - // Start Telemetry and trigger an environment change. - yield TelemetryController.testReset(); - TelemetryEnvironment.testWatchPreferences(PREFS_TO_WATCH); - moveClockForward(30); - Preferences.set(PREF_TEST, 1); - expectedReasons.push(REASON_ENVIRONMENT_CHANGE); - - // Don't shut down, instead trigger an aborted-session ping. - moveClockForward(6); - // Trigger the an aborted session ping save. - yield schedulerTickCallback(); - expectedReasons.push(REASON_ABORTED_SESSION); - - // Start Telemetry and trigger a daily ping. - moveClockForward(30); - yield TelemetryController.testReset(); - // Delay the callback around midnight. - now = futureDate(now, MS_IN_ONE_DAY); - fakeNow(now); - // Trigger the daily ping. - yield schedulerTickCallback(); - expectedReasons.push(REASON_DAILY); - - // Trigger an environment change. - moveClockForward(30); - Preferences.set(PREF_TEST, 0); - expectedReasons.push(REASON_ENVIRONMENT_CHANGE); - - // And an aborted-session ping again. - moveClockForward(6); - // Trigger the an aborted session ping save. - yield schedulerTickCallback(); - expectedReasons.push(REASON_ABORTED_SESSION); - - // Make sure the aborted-session ping gets archived. - yield TelemetryController.testReset(); - - yield promiseValidateArchivedPings(expectedReasons); -}); - -add_task(function* () { - yield TelemetryController.testShutdown(); - do_test_finished(); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js b/toolkit/components/telemetry/tests/unit/test_TelemetryController.js deleted file mode 100644 index b383de6bf..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js +++ /dev/null @@ -1,507 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ -/* This testcase triggers two telemetry pings. - * - * Telemetry code keeps histograms of past telemetry pings. The first - * ping populates these histograms. One of those histograms is then - * checked in the second request. - */ - -Cu.import("resource://gre/modules/ClientID.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetryStorage.jsm", this); -Cu.import("resource://gre/modules/TelemetrySend.jsm", this); -Cu.import("resource://gre/modules/TelemetryArchive.jsm", this); -Cu.import("resource://gre/modules/Task.jsm", this); -Cu.import("resource://gre/modules/Promise.jsm", this); -Cu.import("resource://gre/modules/Preferences.jsm"); - -const PING_FORMAT_VERSION = 4; -const DELETION_PING_TYPE = "deletion"; -const TEST_PING_TYPE = "test-ping-type"; - -const PLATFORM_VERSION = "1.9.2"; -const APP_VERSION = "1"; -const APP_NAME = "XPCShell"; - -const PREF_BRANCH = "toolkit.telemetry."; -const PREF_ENABLED = PREF_BRANCH + "enabled"; -const PREF_ARCHIVE_ENABLED = PREF_BRANCH + "archive.enabled"; -const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; -const PREF_UNIFIED = PREF_BRANCH + "unified"; - -var gClientID = null; - -function sendPing(aSendClientId, aSendEnvironment) { - if (PingServer.started) { - TelemetrySend.setServer("http://localhost:" + PingServer.port); - } else { - TelemetrySend.setServer("http://doesnotexist"); - } - - let options = { - addClientId: aSendClientId, - addEnvironment: aSendEnvironment, - }; - return TelemetryController.submitExternalPing(TEST_PING_TYPE, {}, options); -} - -function checkPingFormat(aPing, aType, aHasClientId, aHasEnvironment) { - const MANDATORY_PING_FIELDS = [ - "type", "id", "creationDate", "version", "application", "payload" - ]; - - const APPLICATION_TEST_DATA = { - buildId: gAppInfo.appBuildID, - name: APP_NAME, - version: APP_VERSION, - displayVersion: AppConstants.MOZ_APP_VERSION_DISPLAY, - vendor: "Mozilla", - platformVersion: PLATFORM_VERSION, - xpcomAbi: "noarch-spidermonkey", - }; - - // Check that the ping contains all the mandatory fields. - for (let f of MANDATORY_PING_FIELDS) { - Assert.ok(f in aPing, f + " must be available."); - } - - Assert.equal(aPing.type, aType, "The ping must have the correct type."); - Assert.equal(aPing.version, PING_FORMAT_VERSION, "The ping must have the correct version."); - - // Test the application section. - for (let f in APPLICATION_TEST_DATA) { - Assert.equal(aPing.application[f], APPLICATION_TEST_DATA[f], - f + " must have the correct value."); - } - - // We can't check the values for channel and architecture. Just make - // sure they are in. - Assert.ok("architecture" in aPing.application, - "The application section must have an architecture field."); - Assert.ok("channel" in aPing.application, - "The application section must have a channel field."); - - // Check the clientId and environment fields, as needed. - Assert.equal("clientId" in aPing, aHasClientId); - Assert.equal("environment" in aPing, aHasEnvironment); -} - -add_task(function* test_setup() { - // Addon manager needs a profile directory - do_get_profile(); - loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - - Services.prefs.setBoolPref(PREF_ENABLED, true); - Services.prefs.setBoolPref(PREF_FHR_UPLOAD_ENABLED, true); - - yield new Promise(resolve => - Telemetry.asyncFetchTelemetryData(wrapWithExceptionHandler(resolve))); -}); - -add_task(function* asyncSetup() { - yield TelemetryController.testSetup(); -}); - -// Ensure that not overwriting an existing file fails silently -add_task(function* test_overwritePing() { - let ping = {id: "foo"}; - yield TelemetryStorage.savePing(ping, true); - yield TelemetryStorage.savePing(ping, false); - yield TelemetryStorage.cleanupPingFile(ping); -}); - -// Checks that a sent ping is correctly received by a dummy http server. -add_task(function* test_simplePing() { - PingServer.start(); - // Update the Telemetry Server preference with the address of the local server. - // Otherwise we might end up sending stuff to a non-existing server after - // |TelemetryController.testReset| is called. - Preferences.set(TelemetryController.Constants.PREF_SERVER, "http://localhost:" + PingServer.port); - - yield sendPing(false, false); - let request = yield PingServer.promiseNextRequest(); - - // Check that we have a version query parameter in the URL. - Assert.notEqual(request.queryString, ""); - - // Make sure the version in the query string matches the new ping format version. - let params = request.queryString.split("&"); - Assert.ok(params.find(p => p == ("v=" + PING_FORMAT_VERSION))); - - let ping = decodeRequestPayload(request); - checkPingFormat(ping, TEST_PING_TYPE, false, false); -}); - -add_task(function* test_disableDataUpload() { - const isUnified = Preferences.get(PREF_UNIFIED, false); - if (!isUnified) { - // Skipping the test if unified telemetry is off, as no deletion ping will - // be generated. - return; - } - - // Disable FHR upload: this should trigger a deletion ping. - Preferences.set(PREF_FHR_UPLOAD_ENABLED, false); - - let ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, DELETION_PING_TYPE, true, false); - // Wait on ping activity to settle. - yield TelemetrySend.testWaitOnOutgoingPings(); - - // Restore FHR Upload. - Preferences.set(PREF_FHR_UPLOAD_ENABLED, true); - - // Simulate a failure in sending the deletion ping by disabling the HTTP server. - yield PingServer.stop(); - - // Try to send a ping. It will be saved as pending and get deleted when disabling upload. - TelemetryController.submitExternalPing(TEST_PING_TYPE, {}); - - // Disable FHR upload to send a deletion ping again. - Preferences.set(PREF_FHR_UPLOAD_ENABLED, false); - - // Wait on sending activity to settle, as |TelemetryController.testReset()| doesn't do that. - yield TelemetrySend.testWaitOnOutgoingPings(); - // Wait for the pending pings to be deleted. Resetting TelemetryController doesn't - // trigger the shutdown, so we need to call it ourselves. - yield TelemetryStorage.shutdown(); - // Simulate a restart, and spin the send task. - yield TelemetryController.testReset(); - - // Disabling Telemetry upload must clear out all the pending pings. - let pendingPings = yield TelemetryStorage.loadPendingPingList(); - Assert.equal(pendingPings.length, 1, - "All the pending pings but the deletion ping should have been deleted"); - - // Enable the ping server again. - PingServer.start(); - // We set the new server using the pref, otherwise it would get reset with - // |TelemetryController.testReset|. - Preferences.set(TelemetryController.Constants.PREF_SERVER, "http://localhost:" + PingServer.port); - - // Stop the sending task and then start it again. - yield TelemetrySend.shutdown(); - // Reset the controller to spin the ping sending task. - yield TelemetryController.testReset(); - ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, DELETION_PING_TYPE, true, false); - - // Wait on ping activity to settle before moving on to the next test. If we were - // to shut down telemetry, even though the PingServer caught the expected pings, - // TelemetrySend could still be processing them (clearing pings would happen in - // a couple of ticks). Shutting down would cancel the request and save them as - // pending pings. - yield TelemetrySend.testWaitOnOutgoingPings(); - // Restore FHR Upload. - Preferences.set(PREF_FHR_UPLOAD_ENABLED, true); -}); - -add_task(function* test_pingHasClientId() { - const PREF_CACHED_CLIENTID = "toolkit.telemetry.cachedClientID"; - - // Make sure we have no cached client ID for this test: we'll try to send - // a ping with it while Telemetry is being initialized. - Preferences.reset(PREF_CACHED_CLIENTID); - yield TelemetryController.testShutdown(); - yield ClientID._reset(); - yield TelemetryStorage.testClearPendingPings(); - // And also clear the counter histogram since we're here. - let h = Telemetry.getHistogramById("TELEMETRY_PING_SUBMISSION_WAITING_CLIENTID"); - h.clear(); - - // Init telemetry and try to send a ping with a client ID. - let promisePingSetup = TelemetryController.testReset(); - yield sendPing(true, false); - Assert.equal(h.snapshot().sum, 1, - "We must have a ping waiting for the clientId early during startup."); - // Wait until we are fully initialized. Pings will be assembled but won't get - // sent before then. - yield promisePingSetup; - - let ping = yield PingServer.promiseNextPing(); - // Fetch the client ID after initializing and fetching the the ping, so we - // don't unintentionally trigger its loading. We'll still need the client ID - // to see if the ping looks sane. - gClientID = yield ClientID.getClientID(); - - checkPingFormat(ping, TEST_PING_TYPE, true, false); - Assert.equal(ping.clientId, gClientID, "The correct clientId must be reported."); - - // Shutdown Telemetry so we can safely restart it. - yield TelemetryController.testShutdown(); - yield TelemetryStorage.testClearPendingPings(); - - // We should have cached the client ID now. Lets confirm that by checking it before - // the async ping setup is finished. - h.clear(); - promisePingSetup = TelemetryController.testReset(); - yield sendPing(true, false); - yield promisePingSetup; - - // Check that we received the cached client id. - Assert.equal(h.snapshot().sum, 0, "We must have used the cached clientId."); - ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, TEST_PING_TYPE, true, false); - Assert.equal(ping.clientId, gClientID, - "Telemetry should report the correct cached clientId."); - - // Check that sending a ping without relying on the cache, after the - // initialization, still works. - Preferences.reset(PREF_CACHED_CLIENTID); - yield TelemetryController.testShutdown(); - yield TelemetryStorage.testClearPendingPings(); - yield TelemetryController.testReset(); - yield sendPing(true, false); - ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, TEST_PING_TYPE, true, false); - Assert.equal(ping.clientId, gClientID, "The correct clientId must be reported."); - Assert.equal(h.snapshot().sum, 0, "No ping should have been waiting for a clientId."); -}); - -add_task(function* test_pingHasEnvironment() { - // Send a ping with the environment data. - yield sendPing(false, true); - let ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, TEST_PING_TYPE, false, true); - - // Test a field in the environment build section. - Assert.equal(ping.application.buildId, ping.environment.build.buildId); -}); - -add_task(function* test_pingHasEnvironmentAndClientId() { - // Send a ping with the environment data and client id. - yield sendPing(true, true); - let ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, TEST_PING_TYPE, true, true); - - // Test a field in the environment build section. - Assert.equal(ping.application.buildId, ping.environment.build.buildId); - // Test that we have the correct clientId. - Assert.equal(ping.clientId, gClientID, "The correct clientId must be reported."); -}); - -add_task(function* test_archivePings() { - let now = new Date(2009, 10, 18, 12, 0, 0); - fakeNow(now); - - // Disable ping upload so that pings don't get sent. - // With unified telemetry the FHR upload pref controls this, - // with non-unified telemetry the Telemetry enabled pref. - const isUnified = Preferences.get(PREF_UNIFIED, false); - const uploadPref = isUnified ? PREF_FHR_UPLOAD_ENABLED : PREF_ENABLED; - Preferences.set(uploadPref, false); - - // If we're using unified telemetry, disabling ping upload will generate a "deletion" - // ping. Catch it. - if (isUnified) { - let ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, DELETION_PING_TYPE, true, false); - } - - // Register a new Ping Handler that asserts if a ping is received, then send a ping. - PingServer.registerPingHandler(() => Assert.ok(false, "Telemetry must not send pings if not allowed to.")); - let pingId = yield sendPing(true, true); - - // Check that the ping was archived, even with upload disabled. - let ping = yield TelemetryArchive.promiseArchivedPingById(pingId); - Assert.equal(ping.id, pingId, "TelemetryController should still archive pings."); - - // Check that pings don't get archived if not allowed to. - now = new Date(2010, 10, 18, 12, 0, 0); - fakeNow(now); - Preferences.set(PREF_ARCHIVE_ENABLED, false); - pingId = yield sendPing(true, true); - let promise = TelemetryArchive.promiseArchivedPingById(pingId); - Assert.ok((yield promiseRejects(promise)), - "TelemetryController should not archive pings if the archive pref is disabled."); - - // Enable archiving and the upload so that pings get sent and archived again. - Preferences.set(uploadPref, true); - Preferences.set(PREF_ARCHIVE_ENABLED, true); - - now = new Date(2014, 6, 18, 22, 0, 0); - fakeNow(now); - // Restore the non asserting ping handler. - PingServer.resetPingHandler(); - pingId = yield sendPing(true, true); - - // Check that we archive pings when successfully sending them. - yield PingServer.promiseNextPing(); - ping = yield TelemetryArchive.promiseArchivedPingById(pingId); - Assert.equal(ping.id, pingId, - "TelemetryController should still archive pings if ping upload is enabled."); -}); - -// Test that we fuzz the submission time around midnight properly -// to avoid overloading the telemetry servers. -add_task(function* test_midnightPingSendFuzzing() { - const fuzzingDelay = 60 * 60 * 1000; - fakeMidnightPingFuzzingDelay(fuzzingDelay); - let now = new Date(2030, 5, 1, 11, 0, 0); - fakeNow(now); - - let waitForTimer = () => new Promise(resolve => { - fakePingSendTimer((callback, timeout) => { - resolve([callback, timeout]); - }, () => {}); - }); - - PingServer.clearRequests(); - yield TelemetryController.testReset(); - - // A ping after midnight within the fuzzing delay should not get sent. - now = new Date(2030, 5, 2, 0, 40, 0); - fakeNow(now); - PingServer.registerPingHandler((req, res) => { - Assert.ok(false, "No ping should be received yet."); - }); - let timerPromise = waitForTimer(); - yield sendPing(true, true); - let [timerCallback, timerTimeout] = yield timerPromise; - Assert.ok(!!timerCallback); - Assert.deepEqual(futureDate(now, timerTimeout), new Date(2030, 5, 2, 1, 0, 0)); - - // A ping just before the end of the fuzzing delay should not get sent. - now = new Date(2030, 5, 2, 0, 59, 59); - fakeNow(now); - timerPromise = waitForTimer(); - yield sendPing(true, true); - [timerCallback, timerTimeout] = yield timerPromise; - Assert.deepEqual(timerTimeout, 1 * 1000); - - // Restore the previous ping handler. - PingServer.resetPingHandler(); - - // Setting the clock to after the fuzzing delay, we should trigger the two ping sends - // with the timer callback. - now = futureDate(now, timerTimeout); - fakeNow(now); - yield timerCallback(); - const pings = yield PingServer.promiseNextPings(2); - for (let ping of pings) { - checkPingFormat(ping, TEST_PING_TYPE, true, true); - } - yield TelemetrySend.testWaitOnOutgoingPings(); - - // Moving the clock further we should still send pings immediately. - now = futureDate(now, 5 * 60 * 1000); - yield sendPing(true, true); - let ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, TEST_PING_TYPE, true, true); - yield TelemetrySend.testWaitOnOutgoingPings(); - - // Check that pings shortly before midnight are immediately sent. - now = fakeNow(2030, 5, 3, 23, 59, 0); - yield sendPing(true, true); - ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, TEST_PING_TYPE, true, true); - yield TelemetrySend.testWaitOnOutgoingPings(); - - // Clean-up. - fakeMidnightPingFuzzingDelay(0); - fakePingSendTimer(() => {}, () => {}); -}); - -add_task(function* test_changePingAfterSubmission() { - // Submit a ping with a custom payload. - let payload = { canary: "test" }; - let pingPromise = TelemetryController.submitExternalPing(TEST_PING_TYPE, payload, options); - - // Change the payload with a predefined value. - payload.canary = "changed"; - - // Wait for the ping to be archived. - const pingId = yield pingPromise; - - // Make sure our changes didn't affect the submitted payload. - let archivedCopy = yield TelemetryArchive.promiseArchivedPingById(pingId); - Assert.equal(archivedCopy.payload.canary, "test", - "The payload must not be changed after being submitted."); -}); - -add_task(function* test_telemetryEnabledUnexpectedValue() { - // Remove the default value for toolkit.telemetry.enabled from the default prefs. - // Otherwise, we wouldn't be able to set the pref to a string. - let defaultPrefBranch = Services.prefs.getDefaultBranch(null); - defaultPrefBranch.deleteBranch(PREF_ENABLED); - - // Set the preferences controlling the Telemetry status to a string. - Preferences.set(PREF_ENABLED, "false"); - // Check that Telemetry is not enabled. - yield TelemetryController.testReset(); - Assert.equal(Telemetry.canRecordExtended, false, - "Invalid values must not enable Telemetry recording."); - - // Delete the pref again. - defaultPrefBranch.deleteBranch(PREF_ENABLED); - - // Make sure that flipping it to true works. - Preferences.set(PREF_ENABLED, true); - yield TelemetryController.testReset(); - Assert.equal(Telemetry.canRecordExtended, true, - "True must enable Telemetry recording."); - - // Also check that the false works as well. - Preferences.set(PREF_ENABLED, false); - yield TelemetryController.testReset(); - Assert.equal(Telemetry.canRecordExtended, false, - "False must disable Telemetry recording."); -}); - -add_task(function* test_telemetryCleanFHRDatabase() { - const FHR_DBNAME_PREF = "datareporting.healthreport.dbName"; - const CUSTOM_DB_NAME = "unlikely.to.be.used.sqlite"; - const DEFAULT_DB_NAME = "healthreport.sqlite"; - - // Check that we're able to remove a FHR DB with a custom name. - const CUSTOM_DB_PATHS = [ - OS.Path.join(OS.Constants.Path.profileDir, CUSTOM_DB_NAME), - OS.Path.join(OS.Constants.Path.profileDir, CUSTOM_DB_NAME + "-wal"), - OS.Path.join(OS.Constants.Path.profileDir, CUSTOM_DB_NAME + "-shm"), - ]; - Preferences.set(FHR_DBNAME_PREF, CUSTOM_DB_NAME); - - // Write fake DB files to the profile directory. - for (let dbFilePath of CUSTOM_DB_PATHS) { - yield OS.File.writeAtomic(dbFilePath, "some data"); - } - - // Trigger the cleanup and check that the files were removed. - yield TelemetryStorage.removeFHRDatabase(); - for (let dbFilePath of CUSTOM_DB_PATHS) { - Assert.ok(!(yield OS.File.exists(dbFilePath)), "The DB must not be on the disk anymore: " + dbFilePath); - } - - // We should not break anything if there's no DB file. - yield TelemetryStorage.removeFHRDatabase(); - - // Check that we're able to remove a FHR DB with the default name. - Preferences.reset(FHR_DBNAME_PREF); - - const DEFAULT_DB_PATHS = [ - OS.Path.join(OS.Constants.Path.profileDir, DEFAULT_DB_NAME), - OS.Path.join(OS.Constants.Path.profileDir, DEFAULT_DB_NAME + "-wal"), - OS.Path.join(OS.Constants.Path.profileDir, DEFAULT_DB_NAME + "-shm"), - ]; - - // Write fake DB files to the profile directory. - for (let dbFilePath of DEFAULT_DB_PATHS) { - yield OS.File.writeAtomic(dbFilePath, "some data"); - } - - // Trigger the cleanup and check that the files were removed. - yield TelemetryStorage.removeFHRDatabase(); - for (let dbFilePath of DEFAULT_DB_PATHS) { - Assert.ok(!(yield OS.File.exists(dbFilePath)), "The DB must not be on the disk anymore: " + dbFilePath); - } -}); - -add_task(function* stopServer() { - yield PingServer.stop(); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryControllerBuildID.js b/toolkit/components/telemetry/tests/unit/test_TelemetryControllerBuildID.js deleted file mode 100644 index b8a88afa2..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryControllerBuildID.js +++ /dev/null @@ -1,70 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ -/* Test inclusion of previous build ID in telemetry pings when build ID changes. - * bug 841028 - * - * Cases to cover: - * 1) Run with no "previousBuildID" stored in prefs: - * -> no previousBuildID in telemetry system info, new value set in prefs. - * 2) previousBuildID in prefs, equal to current build ID: - * -> no previousBuildID in telemetry, prefs not updated. - * 3) previousBuildID in prefs, not equal to current build ID: - * -> previousBuildID in telemetry, new value set in prefs. - */ - -"use strict"; - -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetrySession.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -// Force the Telemetry enabled preference so that TelemetrySession.testReset() doesn't exit early. -Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); - -// Set up our dummy AppInfo object so we can control the appBuildID. -Cu.import("resource://testing-common/AppInfo.jsm", this); -updateAppInfo(); - -// Check that when run with no previous build ID stored, we update the pref but do not -// put anything into the metadata. -add_task(function* test_firstRun() { - yield TelemetryController.testReset(); - let metadata = TelemetrySession.getMetadata(); - do_check_false("previousBuildID" in metadata); - let appBuildID = getAppInfo().appBuildID; - let buildIDPref = Services.prefs.getCharPref(TelemetrySession.Constants.PREF_PREVIOUS_BUILDID); - do_check_eq(appBuildID, buildIDPref); -}); - -// Check that a subsequent run with the same build ID does not put prev build ID in -// metadata. Assumes testFirstRun() has already been called to set the previousBuildID pref. -add_task(function* test_secondRun() { - yield TelemetryController.testReset(); - let metadata = TelemetrySession.getMetadata(); - do_check_false("previousBuildID" in metadata); -}); - -// Set up telemetry with a different app build ID and check that the old build ID -// is returned in the metadata and the pref is updated to the new build ID. -// Assumes testFirstRun() has been called to set the previousBuildID pref. -const NEW_BUILD_ID = "20130314"; -add_task(function* test_newBuild() { - let info = getAppInfo(); - let oldBuildID = info.appBuildID; - info.appBuildID = NEW_BUILD_ID; - yield TelemetryController.testReset(); - let metadata = TelemetrySession.getMetadata(); - do_check_eq(metadata.previousBuildId, oldBuildID); - let buildIDPref = Services.prefs.getCharPref(TelemetrySession.Constants.PREF_PREVIOUS_BUILDID); - do_check_eq(NEW_BUILD_ID, buildIDPref); -}); - - -function run_test() { - // Make sure we have a profile directory. - do_get_profile(); - - run_next_test(); -} diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryControllerShutdown.js b/toolkit/components/telemetry/tests/unit/test_TelemetryControllerShutdown.js deleted file mode 100644 index 391db0d9d..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryControllerShutdown.js +++ /dev/null @@ -1,70 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -// Test that TelemetryController sends close to shutdown don't lead -// to AsyncShutdown timeouts. - -"use strict"; - -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetrySend.jsm", this); -Cu.import("resource://gre/modules/Timer.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); -Cu.import("resource://gre/modules/AsyncShutdown.jsm", this); -Cu.import("resource://testing-common/httpd.js", this); - -const PREF_BRANCH = "toolkit.telemetry."; -const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; - -function contentHandler(metadata, response) -{ - dump("contentHandler called for path: " + metadata._path + "\n"); - // We intentionally don't finish writing the response here to let the - // client time out. - response.processAsync(); - response.setHeader("Content-Type", "text/plain"); -} - -add_task(function* test_setup() { - // Addon manager needs a profile directory - do_get_profile(); - loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - - Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); - Services.prefs.setBoolPref(PREF_FHR_UPLOAD_ENABLED, true); -}); - -/** - * Ensures that TelemetryController does not hang processing shutdown - * phases. Assumes that Telemetry shutdown routines do not take longer than - * CRASH_TIMEOUT_MS to complete. - */ -add_task(function* test_sendTelemetryShutsDownWithinReasonableTimeout() { - const CRASH_TIMEOUT_MS = 5 * 1000; - // Enable testing mode for AsyncShutdown, otherwise some testing-only functionality - // is not available. - Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true); - // Reducing the max delay for waitiing on phases to complete from 1 minute - // (standard) to 10 seconds to avoid blocking the tests in case of misbehavior. - Services.prefs.setIntPref("toolkit.asyncshutdown.crash_timeout", CRASH_TIMEOUT_MS); - - let httpServer = new HttpServer(); - httpServer.registerPrefixHandler("/", contentHandler); - httpServer.start(-1); - - yield TelemetryController.testSetup(); - TelemetrySend.setServer("http://localhost:" + httpServer.identity.primaryPort); - let submissionPromise = TelemetryController.submitExternalPing("test-ping-type", {}); - - // Trigger the AsyncShutdown phase TelemetryController hangs off. - AsyncShutdown.profileBeforeChange._trigger(); - AsyncShutdown.sendTelemetry._trigger(); - // Now wait for the ping submission. - yield submissionPromise; - - // If we get here, we didn't time out in the shutdown routines. - Assert.ok(true, "Didn't time out on shutdown."); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryController_idle.js b/toolkit/components/telemetry/tests/unit/test_TelemetryController_idle.js deleted file mode 100644 index ca5d1820b..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryController_idle.js +++ /dev/null @@ -1,73 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -// Check that TelemetrySession notifies correctly on idle-daily. - -Cu.import("resource://testing-common/httpd.js", this); -Cu.import("resource://gre/modules/PromiseUtils.jsm", this); -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://gre/modules/Task.jsm", this); -Cu.import("resource://gre/modules/TelemetryStorage.jsm", this); -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetrySession.jsm", this); -Cu.import("resource://gre/modules/TelemetrySend.jsm", this); - -const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; - -var gHttpServer = null; - -add_task(function* test_setup() { - do_get_profile(); - - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - - Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); - Services.prefs.setBoolPref(PREF_FHR_UPLOAD_ENABLED, true); - - // Start the webserver to check if the pending ping correctly arrives. - gHttpServer = new HttpServer(); - gHttpServer.start(-1); -}); - -add_task(function* testSendPendingOnIdleDaily() { - // Create a valid pending ping. - const PENDING_PING = { - id: "2133234d-4ea1-44f4-909e-ce8c6c41e0fc", - type: "test-ping", - version: 4, - application: {}, - payload: {}, - }; - yield TelemetryStorage.savePing(PENDING_PING, true); - - // Telemetry will not send this ping at startup, because it's not overdue. - yield TelemetryController.testSetup(); - TelemetrySend.setServer("http://localhost:" + gHttpServer.identity.primaryPort); - - let pendingPromise = new Promise(resolve => - gHttpServer.registerPrefixHandler("/submit/telemetry/", request => resolve(request))); - - let gatherPromise = PromiseUtils.defer(); - Services.obs.addObserver(gatherPromise.resolve, "gather-telemetry", false); - - // Check that we are correctly receiving the gather-telemetry notification. - TelemetrySession.observe(null, "idle-daily", null); - yield gatherPromise; - Assert.ok(true, "Received gather-telemetry notification."); - - Services.obs.removeObserver(gatherPromise.resolve, "gather-telemetry"); - - // Check that the pending ping is correctly received. - let ns = {}; - let module = Cu.import("resource://gre/modules/TelemetrySend.jsm", ns); - module.TelemetrySendImpl.observe(null, "idle-daily", null); - let request = yield pendingPromise; - let ping = decodeRequestPayload(request); - - // Validate the ping data. - Assert.equal(ping.id, PENDING_PING.id); - Assert.equal(ping.type, PENDING_PING.type); - - yield new Promise(resolve => gHttpServer.stop(resolve)); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js deleted file mode 100644 index 2518a80ba..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js +++ /dev/null @@ -1,1522 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -Cu.import("resource://gre/modules/AddonManager.jsm"); -Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this); -Cu.import("resource://gre/modules/Preferences.jsm", this); -Cu.import("resource://gre/modules/PromiseUtils.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); -Cu.import("resource://testing-common/AddonManagerTesting.jsm"); -Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://testing-common/MockRegistrar.jsm", this); -Cu.import("resource://gre/modules/FileUtils.jsm"); - -// AttributionCode is only needed for Firefox -XPCOMUtils.defineLazyModuleGetter(this, "AttributionCode", - "resource:///modules/AttributionCode.jsm"); - -// Lazy load |LightweightThemeManager|, we won't be using it on Gonk. -XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager", - "resource://gre/modules/LightweightThemeManager.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge", - "resource://gre/modules/ProfileAge.jsm"); - -// The webserver hosting the addons. -var gHttpServer = null; -// The URL of the webserver root. -var gHttpRoot = null; -// The URL of the data directory, on the webserver. -var gDataRoot = null; - -const PLATFORM_VERSION = "1.9.2"; -const APP_VERSION = "1"; -const APP_ID = "xpcshell@tests.mozilla.org"; -const APP_NAME = "XPCShell"; - -const DISTRIBUTION_ID = "distributor-id"; -const DISTRIBUTION_VERSION = "4.5.6b"; -const DISTRIBUTOR_NAME = "Some Distributor"; -const DISTRIBUTOR_CHANNEL = "A Channel"; -const PARTNER_NAME = "test"; -const PARTNER_ID = "NicePartner-ID-3785"; -const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC = "distribution-customization-complete"; - -const GFX_VENDOR_ID = "0xabcd"; -const GFX_DEVICE_ID = "0x1234"; - -// The profile reset date, in milliseconds (Today) -const PROFILE_RESET_DATE_MS = Date.now(); -// The profile creation date, in milliseconds (Yesterday). -const PROFILE_CREATION_DATE_MS = PROFILE_RESET_DATE_MS - MILLISECONDS_PER_DAY; - -const FLASH_PLUGIN_NAME = "Shockwave Flash"; -const FLASH_PLUGIN_DESC = "A mock flash plugin"; -const FLASH_PLUGIN_VERSION = "\u201c1.1.1.1\u201d"; -const PLUGIN_MIME_TYPE1 = "application/x-shockwave-flash"; -const PLUGIN_MIME_TYPE2 = "text/plain"; - -const PLUGIN2_NAME = "Quicktime"; -const PLUGIN2_DESC = "A mock Quicktime plugin"; -const PLUGIN2_VERSION = "2.3"; - -const PERSONA_ID = "3785"; -// Defined by LightweightThemeManager, it is appended to the PERSONA_ID. -const PERSONA_ID_SUFFIX = "@personas.mozilla.org"; -const PERSONA_NAME = "Test Theme"; -const PERSONA_DESCRIPTION = "A nice theme/persona description."; - -const PLUGIN_UPDATED_TOPIC = "plugins-list-updated"; - -// system add-ons are enabled at startup, so record date when the test starts -const SYSTEM_ADDON_INSTALL_DATE = Date.now(); - -// Valid attribution code to write so that settings.attribution can be tested. -const ATTRIBUTION_CODE = "source%3Dgoogle.com"; - -/** - * Used to mock plugin tags in our fake plugin host. - */ -function PluginTag(aName, aDescription, aVersion, aEnabled) { - this.name = aName; - this.description = aDescription; - this.version = aVersion; - this.disabled = !aEnabled; -} - -PluginTag.prototype = { - name: null, - description: null, - version: null, - filename: null, - fullpath: null, - disabled: false, - blocklisted: false, - clicktoplay: true, - - mimeTypes: [ PLUGIN_MIME_TYPE1, PLUGIN_MIME_TYPE2 ], - - getMimeTypes: function(count) { - count.value = this.mimeTypes.length; - return this.mimeTypes; - } -}; - -// A container for the plugins handled by the fake plugin host. -var gInstalledPlugins = [ - new PluginTag("Java", "A mock Java plugin", "1.0", false /* Disabled */), - new PluginTag(FLASH_PLUGIN_NAME, FLASH_PLUGIN_DESC, FLASH_PLUGIN_VERSION, true), -]; - -// A fake plugin host for testing plugin telemetry environment. -var PluginHost = { - getPluginTags: function(countRef) { - countRef.value = gInstalledPlugins.length; - return gInstalledPlugins; - }, - - QueryInterface: function(iid) { - if (iid.equals(Ci.nsIPluginHost) - || iid.equals(Ci.nsISupports)) - return this; - - throw Components.results.NS_ERROR_NO_INTERFACE; - } -} - -function registerFakePluginHost() { - MockRegistrar.register("@mozilla.org/plugin/host;1", PluginHost); -} - -var SysInfo = { - overrides: {}, - - getProperty(name) { - // Assert.ok(false, "Mock SysInfo: " + name + ", " + JSON.stringify(this.overrides)); - if (name in this.overrides) { - return this.overrides[name]; - } - try { - return this._genuine.getProperty(name); - } catch (ex) { - throw ex; - } - }, - - get(name) { - return this._genuine.get(name); - }, - - QueryInterface(iid) { - if (iid.equals(Ci.nsIPropertyBag2) - || iid.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -function registerFakeSysInfo() { - MockRegistrar.register("@mozilla.org/system-info;1", SysInfo); -} - -function MockAddonWrapper(aAddon) { - this.addon = aAddon; -} -MockAddonWrapper.prototype = { - get id() { - return this.addon.id; - }, - - get type() { - return "service"; - }, - - get appDisabled() { - return false; - }, - - get isCompatible() { - return true; - }, - - get isPlatformCompatible() { - return true; - }, - - get scope() { - return AddonManager.SCOPE_PROFILE; - }, - - get foreignInstall() { - return false; - }, - - get providesUpdatesSecurely() { - return true; - }, - - get blocklistState() { - return 0; // Not blocked. - }, - - get pendingOperations() { - return AddonManager.PENDING_NONE; - }, - - get permissions() { - return AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_DISABLE; - }, - - get isActive() { - return true; - }, - - get name() { - return this.addon.name; - }, - - get version() { - return this.addon.version; - }, - - get creator() { - return new AddonManagerPrivate.AddonAuthor(this.addon.author); - }, - - get userDisabled() { - return this.appDisabled; - }, -}; - -function createMockAddonProvider(aName) { - let mockProvider = { - _addons: [], - - get name() { - return aName; - }, - - addAddon: function(aAddon) { - this._addons.push(aAddon); - AddonManagerPrivate.callAddonListeners("onInstalled", new MockAddonWrapper(aAddon)); - }, - - getAddonsByTypes: function (aTypes, aCallback) { - aCallback(this._addons.map(a => new MockAddonWrapper(a))); - }, - - shutdown() { - return Promise.resolve(); - }, - }; - - return mockProvider; -} - -/** - * Used to spoof the Persona Id. - */ -function spoofTheme(aId, aName, aDesc) { - return { - id: aId, - name: aName, - description: aDesc, - headerURL: "http://lwttest.invalid/a.png", - footerURL: "http://lwttest.invalid/b.png", - textcolor: Math.random().toString(), - accentcolor: Math.random().toString() - }; -} - -function spoofGfxAdapter() { - try { - let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug); - gfxInfo.spoofVendorID(GFX_VENDOR_ID); - gfxInfo.spoofDeviceID(GFX_DEVICE_ID); - } catch (x) { - // If we can't test gfxInfo, that's fine, we'll note it later. - } -} - -function spoofProfileReset() { - let profileAccessor = new ProfileAge(); - - return profileAccessor.writeTimes({ - created: PROFILE_CREATION_DATE_MS, - reset: PROFILE_RESET_DATE_MS - }); -} - -function spoofPartnerInfo() { - let prefsToSpoof = {}; - prefsToSpoof["distribution.id"] = DISTRIBUTION_ID; - prefsToSpoof["distribution.version"] = DISTRIBUTION_VERSION; - prefsToSpoof["app.distributor"] = DISTRIBUTOR_NAME; - prefsToSpoof["app.distributor.channel"] = DISTRIBUTOR_CHANNEL; - prefsToSpoof["app.partner.test"] = PARTNER_NAME; - prefsToSpoof["mozilla.partner.id"] = PARTNER_ID; - - // Spoof the preferences. - for (let pref in prefsToSpoof) { - Preferences.set(pref, prefsToSpoof[pref]); - } -} - -function getAttributionFile() { - let file = Services.dirsvc.get("LocalAppData", Ci.nsIFile); - file.append("mozilla"); - file.append(AppConstants.MOZ_APP_NAME); - file.append("postSigningData"); - return file; -} - -function spoofAttributionData() { - if (gIsWindows) { - AttributionCode._clearCache(); - let stream = Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(Ci.nsIFileOutputStream); - stream.init(getAttributionFile(), -1, -1, 0); - stream.write(ATTRIBUTION_CODE, ATTRIBUTION_CODE.length); - } -} - -function cleanupAttributionData() { - if (gIsWindows) { - getAttributionFile().remove(false); - AttributionCode._clearCache(); - } -} - -/** - * Check that a value is a string and not empty. - * - * @param aValue The variable to check. - * @return True if |aValue| has type "string" and is not empty, False otherwise. - */ -function checkString(aValue) { - return (typeof aValue == "string") && (aValue != ""); -} - -/** - * If value is non-null, check if it's a valid string. - * - * @param aValue The variable to check. - * @return True if it's null or a valid string, false if it's non-null and an invalid - * string. - */ -function checkNullOrString(aValue) { - if (aValue) { - return checkString(aValue); - } else if (aValue === null) { - return true; - } - - return false; -} - -/** - * If value is non-null, check if it's a boolean. - * - * @param aValue The variable to check. - * @return True if it's null or a valid boolean, false if it's non-null and an invalid - * boolean. - */ -function checkNullOrBool(aValue) { - return aValue === null || (typeof aValue == "boolean"); -} - -function checkBuildSection(data) { - const expectedInfo = { - applicationId: APP_ID, - applicationName: APP_NAME, - buildId: gAppInfo.appBuildID, - version: APP_VERSION, - vendor: "Mozilla", - platformVersion: PLATFORM_VERSION, - xpcomAbi: "noarch-spidermonkey", - }; - - Assert.ok("build" in data, "There must be a build section in Environment."); - - for (let f in expectedInfo) { - Assert.ok(checkString(data.build[f]), f + " must be a valid string."); - Assert.equal(data.build[f], expectedInfo[f], f + " must have the correct value."); - } - - // Make sure architecture is in the environment. - Assert.ok(checkString(data.build.architecture)); - - if (gIsMac) { - let macUtils = Cc["@mozilla.org/xpcom/mac-utils;1"].getService(Ci.nsIMacUtils); - if (macUtils && macUtils.isUniversalBinary) { - Assert.ok(checkString(data.build.architecturesInBinary)); - } - } -} - -function checkSettingsSection(data) { - const EXPECTED_FIELDS_TYPES = { - blocklistEnabled: "boolean", - e10sEnabled: "boolean", - e10sCohort: "string", - telemetryEnabled: "boolean", - locale: "string", - update: "object", - userPrefs: "object", - }; - - Assert.ok("settings" in data, "There must be a settings section in Environment."); - - for (let f in EXPECTED_FIELDS_TYPES) { - Assert.equal(typeof data.settings[f], EXPECTED_FIELDS_TYPES[f], - f + " must have the correct type."); - } - - // Check "addonCompatibilityCheckEnabled" separately, as it is not available - // on Gonk. - if (gIsGonk) { - Assert.ok(!("addonCompatibilityCheckEnabled" in data.settings), "Must not be available on Gonk."); - } else { - Assert.equal(data.settings.addonCompatibilityCheckEnabled, AddonManager.checkCompatibility); - } - - // Check "isDefaultBrowser" separately, as it is not available on Android an can either be - // null or boolean on other platforms. - if (gIsAndroid) { - Assert.ok(!("isDefaultBrowser" in data.settings), "Must not be available on Android."); - } else { - Assert.ok(checkNullOrBool(data.settings.isDefaultBrowser)); - } - - // Check "channel" separately, as it can either be null or string. - let update = data.settings.update; - Assert.ok(checkNullOrString(update.channel)); - Assert.equal(typeof update.enabled, "boolean"); - Assert.equal(typeof update.autoDownload, "boolean"); - - // Check "defaultSearchEngine" separately, as it can either be undefined or string. - if ("defaultSearchEngine" in data.settings) { - checkString(data.settings.defaultSearchEngine); - Assert.equal(typeof data.settings.defaultSearchEngineData, "object"); - } - - if ("attribution" in data.settings) { - Assert.equal(typeof data.settings.attribution, "object"); - Assert.equal(data.settings.attribution.source, "google.com"); - } -} - -function checkProfileSection(data) { - Assert.ok("profile" in data, "There must be a profile section in Environment."); - Assert.equal(data.profile.creationDate, truncateToDays(PROFILE_CREATION_DATE_MS)); - Assert.equal(data.profile.resetDate, truncateToDays(PROFILE_RESET_DATE_MS)); -} - -function checkPartnerSection(data, isInitial) { - const EXPECTED_FIELDS = { - distributionId: DISTRIBUTION_ID, - distributionVersion: DISTRIBUTION_VERSION, - partnerId: PARTNER_ID, - distributor: DISTRIBUTOR_NAME, - distributorChannel: DISTRIBUTOR_CHANNEL, - }; - - Assert.ok("partner" in data, "There must be a partner section in Environment."); - - for (let f in EXPECTED_FIELDS) { - let expected = isInitial ? null : EXPECTED_FIELDS[f]; - Assert.strictEqual(data.partner[f], expected, f + " must have the correct value."); - } - - // Check that "partnerNames" exists and contains the correct element. - Assert.ok(Array.isArray(data.partner.partnerNames)); - if (isInitial) { - Assert.equal(data.partner.partnerNames.length, 0); - } else { - Assert.ok(data.partner.partnerNames.includes(PARTNER_NAME)); - } -} - -function checkGfxAdapter(data) { - const EXPECTED_ADAPTER_FIELDS_TYPES = { - description: "string", - vendorID: "string", - deviceID: "string", - subsysID: "string", - RAM: "number", - driver: "string", - driverVersion: "string", - driverDate: "string", - GPUActive: "boolean", - }; - - for (let f in EXPECTED_ADAPTER_FIELDS_TYPES) { - Assert.ok(f in data, f + " must be available."); - - if (data[f]) { - // Since we have a non-null value, check if it has the correct type. - Assert.equal(typeof data[f], EXPECTED_ADAPTER_FIELDS_TYPES[f], - f + " must have the correct type."); - } - } -} - -function checkSystemSection(data) { - const EXPECTED_FIELDS = [ "memoryMB", "cpu", "os", "hdd", "gfx" ]; - const EXPECTED_HDD_FIELDS = [ "profile", "binary", "system" ]; - - Assert.ok("system" in data, "There must be a system section in Environment."); - - // Make sure we have all the top level sections and fields. - for (let f of EXPECTED_FIELDS) { - Assert.ok(f in data.system, f + " must be available."); - } - - Assert.ok(Number.isFinite(data.system.memoryMB), "MemoryMB must be a number."); - - if (gIsWindows || gIsMac || gIsLinux) { - let EXTRA_CPU_FIELDS = ["cores", "model", "family", "stepping", - "l2cacheKB", "l3cacheKB", "speedMHz", "vendor"]; - - for (let f of EXTRA_CPU_FIELDS) { - // Note this is testing TelemetryEnvironment.js only, not that the - // values are valid - null is the fallback. - Assert.ok(f in data.system.cpu, f + " must be available under cpu."); - } - - if (gIsWindows) { - Assert.equal(typeof data.system.isWow64, "boolean", - "isWow64 must be available on Windows and have the correct type."); - Assert.ok("virtualMaxMB" in data.system, "virtualMaxMB must be available."); - Assert.ok(Number.isFinite(data.system.virtualMaxMB), - "virtualMaxMB must be a number."); - } - - // We insist these are available - for (let f of ["cores"]) { - Assert.ok(!(f in data.system.cpu) || - Number.isFinite(data.system.cpu[f]), - f + " must be a number if non null."); - } - - // These should be numbers if they are not null - for (let f of ["model", "family", "stepping", "l2cacheKB", - "l3cacheKB", "speedMHz"]) { - Assert.ok(!(f in data.system.cpu) || - data.system.cpu[f] === null || - Number.isFinite(data.system.cpu[f]), - f + " must be a number if non null."); - } - } - - let cpuData = data.system.cpu; - Assert.ok(Number.isFinite(cpuData.count), "CPU count must be a number."); - Assert.ok(Array.isArray(cpuData.extensions), "CPU extensions must be available."); - - // Device data is only available on Android or Gonk. - if (gIsAndroid || gIsGonk) { - let deviceData = data.system.device; - Assert.ok(checkNullOrString(deviceData.model)); - Assert.ok(checkNullOrString(deviceData.manufacturer)); - Assert.ok(checkNullOrString(deviceData.hardware)); - Assert.ok(checkNullOrBool(deviceData.isTablet)); - } - - let osData = data.system.os; - Assert.ok(checkNullOrString(osData.name)); - Assert.ok(checkNullOrString(osData.version)); - Assert.ok(checkNullOrString(osData.locale)); - - // Service pack is only available on Windows. - if (gIsWindows) { - Assert.ok(Number.isFinite(osData["servicePackMajor"]), - "ServicePackMajor must be a number."); - Assert.ok(Number.isFinite(osData["servicePackMinor"]), - "ServicePackMinor must be a number."); - if ("windowsBuildNumber" in osData) { - // This might not be available on all Windows platforms. - Assert.ok(Number.isFinite(osData["windowsBuildNumber"]), - "windowsBuildNumber must be a number."); - } - if ("windowsUBR" in osData) { - // This might not be available on all Windows platforms. - Assert.ok((osData["windowsUBR"] === null) || Number.isFinite(osData["windowsUBR"]), - "windowsUBR must be null or a number."); - } - } else if (gIsAndroid || gIsGonk) { - Assert.ok(checkNullOrString(osData.kernelVersion)); - } - - let check = gIsWindows ? checkString : checkNullOrString; - for (let disk of EXPECTED_HDD_FIELDS) { - Assert.ok(check(data.system.hdd[disk].model)); - Assert.ok(check(data.system.hdd[disk].revision)); - } - - let gfxData = data.system.gfx; - Assert.ok("D2DEnabled" in gfxData); - Assert.ok("DWriteEnabled" in gfxData); - // DWriteVersion is disabled due to main thread jank and will be enabled - // again as part of bug 1154500. - // Assert.ok("DWriteVersion" in gfxData); - if (gIsWindows) { - Assert.equal(typeof gfxData.D2DEnabled, "boolean"); - Assert.equal(typeof gfxData.DWriteEnabled, "boolean"); - // As above, will be enabled again as part of bug 1154500. - // Assert.ok(checkString(gfxData.DWriteVersion)); - } - - Assert.ok("adapters" in gfxData); - Assert.ok(gfxData.adapters.length > 0, "There must be at least one GFX adapter."); - for (let adapter of gfxData.adapters) { - checkGfxAdapter(adapter); - } - Assert.equal(typeof gfxData.adapters[0].GPUActive, "boolean"); - Assert.ok(gfxData.adapters[0].GPUActive, "The first GFX adapter must be active."); - - Assert.ok(Array.isArray(gfxData.monitors)); - if (gIsWindows || gIsMac) { - Assert.ok(gfxData.monitors.length >= 1, "There is at least one monitor."); - Assert.equal(typeof gfxData.monitors[0].screenWidth, "number"); - Assert.equal(typeof gfxData.monitors[0].screenHeight, "number"); - if (gIsWindows) { - Assert.equal(typeof gfxData.monitors[0].refreshRate, "number"); - Assert.equal(typeof gfxData.monitors[0].pseudoDisplay, "boolean"); - } - if (gIsMac) { - Assert.equal(typeof gfxData.monitors[0].scale, "number"); - } - } - - Assert.equal(typeof gfxData.features, "object"); - Assert.equal(typeof gfxData.features.compositor, "string"); - - try { - // If we've not got nsIGfxInfoDebug, then this will throw and stop us doing - // this test. - let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug); - - if (gIsWindows || gIsMac) { - Assert.equal(GFX_VENDOR_ID, gfxData.adapters[0].vendorID); - Assert.equal(GFX_DEVICE_ID, gfxData.adapters[0].deviceID); - } - - let features = gfxInfo.getFeatures(); - Assert.equal(features.compositor, gfxData.features.compositor); - Assert.equal(features.opengl, gfxData.features.opengl); - Assert.equal(features.webgl, gfxData.features.webgl); - } - catch (e) {} -} - -function checkActiveAddon(data) { - let signedState = mozinfo.addon_signing ? "number" : "undefined"; - // system add-ons have an undefined signState - if (data.isSystem) - signedState = "undefined"; - - const EXPECTED_ADDON_FIELDS_TYPES = { - blocklisted: "boolean", - name: "string", - userDisabled: "boolean", - appDisabled: "boolean", - version: "string", - scope: "number", - type: "string", - foreignInstall: "boolean", - hasBinaryComponents: "boolean", - installDay: "number", - updateDay: "number", - signedState: signedState, - isSystem: "boolean", - }; - - for (let f in EXPECTED_ADDON_FIELDS_TYPES) { - Assert.ok(f in data, f + " must be available."); - Assert.equal(typeof data[f], EXPECTED_ADDON_FIELDS_TYPES[f], - f + " must have the correct type."); - } - - // We check "description" separately, as it can be null. - Assert.ok(checkNullOrString(data.description)); -} - -function checkPlugin(data) { - const EXPECTED_PLUGIN_FIELDS_TYPES = { - name: "string", - version: "string", - description: "string", - blocklisted: "boolean", - disabled: "boolean", - clicktoplay: "boolean", - updateDay: "number", - }; - - for (let f in EXPECTED_PLUGIN_FIELDS_TYPES) { - Assert.ok(f in data, f + " must be available."); - Assert.equal(typeof data[f], EXPECTED_PLUGIN_FIELDS_TYPES[f], - f + " must have the correct type."); - } - - Assert.ok(Array.isArray(data.mimeTypes)); - for (let type of data.mimeTypes) { - Assert.ok(checkString(type)); - } -} - -function checkTheme(data) { - // "hasBinaryComponents" is not available when testing. - const EXPECTED_THEME_FIELDS_TYPES = { - id: "string", - blocklisted: "boolean", - name: "string", - userDisabled: "boolean", - appDisabled: "boolean", - version: "string", - scope: "number", - foreignInstall: "boolean", - installDay: "number", - updateDay: "number", - }; - - for (let f in EXPECTED_THEME_FIELDS_TYPES) { - Assert.ok(f in data, f + " must be available."); - Assert.equal(typeof data[f], EXPECTED_THEME_FIELDS_TYPES[f], - f + " must have the correct type."); - } - - // We check "description" separately, as it can be null. - Assert.ok(checkNullOrString(data.description)); -} - -function checkActiveGMPlugin(data) { - // GMP plugin version defaults to null until GMPDownloader runs to update it. - if (data.version) { - Assert.equal(typeof data.version, "string"); - } - Assert.equal(typeof data.userDisabled, "boolean"); - Assert.equal(typeof data.applyBackgroundUpdates, "number"); -} - -function checkAddonsSection(data, expectBrokenAddons) { - const EXPECTED_FIELDS = [ - "activeAddons", "theme", "activePlugins", "activeGMPlugins", "activeExperiment", - "persona", - ]; - - Assert.ok("addons" in data, "There must be an addons section in Environment."); - for (let f of EXPECTED_FIELDS) { - Assert.ok(f in data.addons, f + " must be available."); - } - - // Check the active addons, if available. - if (!expectBrokenAddons) { - let activeAddons = data.addons.activeAddons; - for (let addon in activeAddons) { - checkActiveAddon(activeAddons[addon]); - } - } - - // Check "theme" structure. - if (Object.keys(data.addons.theme).length !== 0) { - checkTheme(data.addons.theme); - } - - // Check the active plugins. - Assert.ok(Array.isArray(data.addons.activePlugins)); - for (let plugin of data.addons.activePlugins) { - checkPlugin(plugin); - } - - // Check active GMPlugins - let activeGMPlugins = data.addons.activeGMPlugins; - for (let gmPlugin in activeGMPlugins) { - checkActiveGMPlugin(activeGMPlugins[gmPlugin]); - } - - // Check the active Experiment - let experiment = data.addons.activeExperiment; - if (Object.keys(experiment).length !== 0) { - Assert.ok(checkString(experiment.id)); - Assert.ok(checkString(experiment.branch)); - } - - // Check persona - Assert.ok(checkNullOrString(data.addons.persona)); -} - -function checkEnvironmentData(data, isInitial = false, expectBrokenAddons = false) { - checkBuildSection(data); - checkSettingsSection(data); - checkProfileSection(data); - checkPartnerSection(data, isInitial); - checkSystemSection(data); - checkAddonsSection(data, expectBrokenAddons); -} - -add_task(function* setup() { - // Load a custom manifest to provide search engine loading from JAR files. - do_load_manifest("chrome.manifest"); - registerFakeSysInfo(); - spoofGfxAdapter(); - do_get_profile(); - - // The system add-on must be installed before AddonManager is started. - const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "app0"], true); - do_get_file("system.xpi").copyTo(distroDir, "tel-system-xpi@tests.mozilla.org.xpi"); - let system_addon = FileUtils.File(distroDir.path); - system_addon.append("tel-system-xpi@tests.mozilla.org.xpi"); - system_addon.lastModifiedTime = SYSTEM_ADDON_INSTALL_DATE; - loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION); - - // Spoof the persona ID, but not on Gonk. - if (!gIsGonk) { - LightweightThemeManager.currentTheme = - spoofTheme(PERSONA_ID, PERSONA_NAME, PERSONA_DESCRIPTION); - } - // Register a fake plugin host for consistent flash version data. - registerFakePluginHost(); - - // Setup a webserver to serve Addons, Plugins, etc. - gHttpServer = new HttpServer(); - gHttpServer.start(-1); - let port = gHttpServer.identity.primaryPort; - gHttpRoot = "http://localhost:" + port + "/"; - gDataRoot = gHttpRoot + "data/"; - gHttpServer.registerDirectory("/data/", do_get_cwd()); - do_register_cleanup(() => gHttpServer.stop(() => {})); - - // Create the attribution data file, so that settings.attribution will exist. - // The attribution functionality only exists in Firefox. - if (AppConstants.MOZ_BUILD_APP == "browser") { - spoofAttributionData(); - do_register_cleanup(cleanupAttributionData); - } - - yield spoofProfileReset(); - TelemetryEnvironment.delayedInit(); -}); - -add_task(function* test_checkEnvironment() { - let environmentData = yield TelemetryEnvironment.onInitialized(); - checkEnvironmentData(environmentData, true); - - spoofPartnerInfo(); - Services.obs.notifyObservers(null, DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC, null); - - environmentData = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(environmentData); -}); - -add_task(function* test_prefWatchPolicies() { - const PREF_TEST_1 = "toolkit.telemetry.test.pref_new"; - const PREF_TEST_2 = "toolkit.telemetry.test.pref1"; - const PREF_TEST_3 = "toolkit.telemetry.test.pref2"; - const PREF_TEST_4 = "toolkit.telemetry.test.pref_old"; - const PREF_TEST_5 = "toolkit.telemetry.test.requiresRestart"; - - const expectedValue = "some-test-value"; - const unexpectedValue = "unexpected-test-value"; - - const PREFS_TO_WATCH = new Map([ - [PREF_TEST_1, {what: TelemetryEnvironment.RECORD_PREF_VALUE}], - [PREF_TEST_2, {what: TelemetryEnvironment.RECORD_PREF_STATE}], - [PREF_TEST_3, {what: TelemetryEnvironment.RECORD_PREF_STATE}], - [PREF_TEST_4, {what: TelemetryEnvironment.RECORD_PREF_VALUE}], - [PREF_TEST_5, {what: TelemetryEnvironment.RECORD_PREF_VALUE, requiresRestart: true}], - ]); - - Preferences.set(PREF_TEST_4, expectedValue); - Preferences.set(PREF_TEST_5, expectedValue); - - // Set the Environment preferences to watch. - TelemetryEnvironment.testWatchPreferences(PREFS_TO_WATCH); - let deferred = PromiseUtils.defer(); - - // Check that the pref values are missing or present as expected - Assert.strictEqual(TelemetryEnvironment.currentEnvironment.settings.userPrefs[PREF_TEST_1], undefined); - Assert.strictEqual(TelemetryEnvironment.currentEnvironment.settings.userPrefs[PREF_TEST_4], expectedValue); - Assert.strictEqual(TelemetryEnvironment.currentEnvironment.settings.userPrefs[PREF_TEST_5], expectedValue); - - TelemetryEnvironment.registerChangeListener("testWatchPrefs", - (reason, data) => deferred.resolve(data)); - let oldEnvironmentData = TelemetryEnvironment.currentEnvironment; - - // Trigger a change in the watched preferences. - Preferences.set(PREF_TEST_1, expectedValue); - Preferences.set(PREF_TEST_2, false); - Preferences.set(PREF_TEST_5, unexpectedValue); - let eventEnvironmentData = yield deferred.promise; - - // Unregister the listener. - TelemetryEnvironment.unregisterChangeListener("testWatchPrefs"); - - // Check environment contains the correct data. - Assert.deepEqual(oldEnvironmentData, eventEnvironmentData); - let userPrefs = TelemetryEnvironment.currentEnvironment.settings.userPrefs; - - Assert.equal(userPrefs[PREF_TEST_1], expectedValue, - "Environment contains the correct preference value."); - Assert.equal(userPrefs[PREF_TEST_2], "<user-set>", - "Report that the pref was user set but the value is not shown."); - Assert.ok(!(PREF_TEST_3 in userPrefs), - "Do not report if preference not user set."); - Assert.equal(userPrefs[PREF_TEST_5], expectedValue, - "The pref value in the environment data should still be the same"); -}); - -add_task(function* test_prefWatch_prefReset() { - const PREF_TEST = "toolkit.telemetry.test.pref1"; - const PREFS_TO_WATCH = new Map([ - [PREF_TEST, {what: TelemetryEnvironment.RECORD_PREF_STATE}], - ]); - - // Set the preference to a non-default value. - Preferences.set(PREF_TEST, false); - - // Set the Environment preferences to watch. - TelemetryEnvironment.testWatchPreferences(PREFS_TO_WATCH); - let deferred = PromiseUtils.defer(); - TelemetryEnvironment.registerChangeListener("testWatchPrefs_reset", deferred.resolve); - - Assert.strictEqual(TelemetryEnvironment.currentEnvironment.settings.userPrefs[PREF_TEST], "<user-set>"); - - // Trigger a change in the watched preferences. - Preferences.reset(PREF_TEST); - yield deferred.promise; - - Assert.strictEqual(TelemetryEnvironment.currentEnvironment.settings.userPrefs[PREF_TEST], undefined); - - // Unregister the listener. - TelemetryEnvironment.unregisterChangeListener("testWatchPrefs_reset"); -}); - -add_task(function* test_addonsWatch_InterestingChange() { - const ADDON_INSTALL_URL = gDataRoot + "restartless.xpi"; - const ADDON_ID = "tel-restartless-xpi@tests.mozilla.org"; - // We only expect a single notification for each install, uninstall, enable, disable. - const EXPECTED_NOTIFICATIONS = 4; - - let receivedNotifications = 0; - - let registerCheckpointPromise = (aExpected) => { - return new Promise(resolve => TelemetryEnvironment.registerChangeListener( - "testWatchAddons_Changes" + aExpected, (reason, data) => { - Assert.equal(reason, "addons-changed"); - receivedNotifications++; - resolve(); - })); - }; - - let assertCheckpoint = (aExpected) => { - Assert.equal(receivedNotifications, aExpected); - TelemetryEnvironment.unregisterChangeListener("testWatchAddons_Changes" + aExpected); - }; - - // Test for receiving one notification after each change. - let checkpointPromise = registerCheckpointPromise(1); - yield AddonManagerTesting.installXPIFromURL(ADDON_INSTALL_URL); - yield checkpointPromise; - assertCheckpoint(1); - Assert.ok(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons); - - checkpointPromise = registerCheckpointPromise(2); - let addon = yield AddonManagerTesting.getAddonById(ADDON_ID); - addon.userDisabled = true; - yield checkpointPromise; - assertCheckpoint(2); - Assert.ok(!(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons)); - - checkpointPromise = registerCheckpointPromise(3); - addon.userDisabled = false; - yield checkpointPromise; - assertCheckpoint(3); - Assert.ok(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons); - - checkpointPromise = registerCheckpointPromise(4); - yield AddonManagerTesting.uninstallAddonByID(ADDON_ID); - yield checkpointPromise; - assertCheckpoint(4); - Assert.ok(!(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons)); - - Assert.equal(receivedNotifications, EXPECTED_NOTIFICATIONS, - "We must only receive the notifications we expect."); -}); - -add_task(function* test_pluginsWatch_Add() { - if (gIsAndroid) { - Assert.ok(true, "Skipping: there is no Plugin Manager on Android."); - return; - } - - Assert.equal(TelemetryEnvironment.currentEnvironment.addons.activePlugins.length, 1); - - let newPlugin = new PluginTag(PLUGIN2_NAME, PLUGIN2_DESC, PLUGIN2_VERSION, true); - gInstalledPlugins.push(newPlugin); - - let deferred = PromiseUtils.defer(); - let receivedNotifications = 0; - let callback = (reason, data) => { - receivedNotifications++; - Assert.equal(reason, "addons-changed"); - deferred.resolve(); - }; - TelemetryEnvironment.registerChangeListener("testWatchPlugins_Add", callback); - - Services.obs.notifyObservers(null, PLUGIN_UPDATED_TOPIC, null); - yield deferred.promise; - - Assert.equal(TelemetryEnvironment.currentEnvironment.addons.activePlugins.length, 2); - - TelemetryEnvironment.unregisterChangeListener("testWatchPlugins_Add"); - - Assert.equal(receivedNotifications, 1, "We must only receive one notification."); -}); - -add_task(function* test_pluginsWatch_Remove() { - if (gIsAndroid) { - Assert.ok(true, "Skipping: there is no Plugin Manager on Android."); - return; - } - - // Find the test plugin. - let plugin = gInstalledPlugins.find(p => (p.name == PLUGIN2_NAME)); - Assert.ok(plugin, "The test plugin must exist."); - - // Remove it from the PluginHost. - gInstalledPlugins = gInstalledPlugins.filter(p => p != plugin); - - let deferred = PromiseUtils.defer(); - let receivedNotifications = 0; - let callback = () => { - receivedNotifications++; - deferred.resolve(); - }; - TelemetryEnvironment.registerChangeListener("testWatchPlugins_Remove", callback); - - Services.obs.notifyObservers(null, PLUGIN_UPDATED_TOPIC, null); - yield deferred.promise; - - TelemetryEnvironment.unregisterChangeListener("testWatchPlugins_Remove"); - - Assert.equal(receivedNotifications, 1, "We must only receive one notification."); -}); - -add_task(function* test_addonsWatch_NotInterestingChange() { - // We are not interested to dictionary addons changes. - const DICTIONARY_ADDON_INSTALL_URL = gDataRoot + "dictionary.xpi"; - const INTERESTING_ADDON_INSTALL_URL = gDataRoot + "restartless.xpi"; - - let receivedNotification = false; - let deferred = PromiseUtils.defer(); - TelemetryEnvironment.registerChangeListener("testNotInteresting", - () => { - Assert.ok(!receivedNotification, "Should not receive multiple notifications"); - receivedNotification = true; - deferred.resolve(); - }); - - yield AddonManagerTesting.installXPIFromURL(DICTIONARY_ADDON_INSTALL_URL); - yield AddonManagerTesting.installXPIFromURL(INTERESTING_ADDON_INSTALL_URL); - - yield deferred.promise; - Assert.ok(!("telemetry-dictionary@tests.mozilla.org" in - TelemetryEnvironment.currentEnvironment.addons.activeAddons), - "Dictionaries should not appear in active addons."); - - TelemetryEnvironment.unregisterChangeListener("testNotInteresting"); -}); - -add_task(function* test_addonsAndPlugins() { - const ADDON_INSTALL_URL = gDataRoot + "restartless.xpi"; - const ADDON_ID = "tel-restartless-xpi@tests.mozilla.org"; - const ADDON_INSTALL_DATE = truncateToDays(Date.now()); - const EXPECTED_ADDON_DATA = { - blocklisted: false, - description: "A restartless addon which gets enabled without a reboot.", - name: "XPI Telemetry Restartless Test", - userDisabled: false, - appDisabled: false, - version: "1.0", - scope: 1, - type: "extension", - foreignInstall: false, - hasBinaryComponents: false, - installDay: ADDON_INSTALL_DATE, - updateDay: ADDON_INSTALL_DATE, - signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED, - isSystem: false, - }; - const SYSTEM_ADDON_ID = "tel-system-xpi@tests.mozilla.org"; - const EXPECTED_SYSTEM_ADDON_DATA = { - blocklisted: false, - description: "A system addon which is shipped with Firefox.", - name: "XPI Telemetry System Add-on Test", - userDisabled: false, - appDisabled: false, - version: "1.0", - scope: 1, - type: "extension", - foreignInstall: false, - hasBinaryComponents: false, - installDay: truncateToDays(SYSTEM_ADDON_INSTALL_DATE), - updateDay: truncateToDays(SYSTEM_ADDON_INSTALL_DATE), - signedState: undefined, - isSystem: true, - }; - - const EXPECTED_PLUGIN_DATA = { - name: FLASH_PLUGIN_NAME, - version: FLASH_PLUGIN_VERSION, - description: FLASH_PLUGIN_DESC, - blocklisted: false, - disabled: false, - clicktoplay: true, - }; - - // Install an addon so we have some data. - yield AddonManagerTesting.installXPIFromURL(ADDON_INSTALL_URL); - - let data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - - // Check addon data. - Assert.ok(ADDON_ID in data.addons.activeAddons, "We must have one active addon."); - let targetAddon = data.addons.activeAddons[ADDON_ID]; - for (let f in EXPECTED_ADDON_DATA) { - Assert.equal(targetAddon[f], EXPECTED_ADDON_DATA[f], f + " must have the correct value."); - } - - // Check system add-on data. - Assert.ok(SYSTEM_ADDON_ID in data.addons.activeAddons, "We must have one active system addon."); - let targetSystemAddon = data.addons.activeAddons[SYSTEM_ADDON_ID]; - for (let f in EXPECTED_SYSTEM_ADDON_DATA) { - Assert.equal(targetSystemAddon[f], EXPECTED_SYSTEM_ADDON_DATA[f], f + " must have the correct value."); - } - - // Check theme data. - let theme = data.addons.theme; - Assert.equal(theme.id, (PERSONA_ID + PERSONA_ID_SUFFIX)); - Assert.equal(theme.name, PERSONA_NAME); - Assert.equal(theme.description, PERSONA_DESCRIPTION); - - // Check plugin data. - Assert.equal(data.addons.activePlugins.length, 1, "We must have only one active plugin."); - let targetPlugin = data.addons.activePlugins[0]; - for (let f in EXPECTED_PLUGIN_DATA) { - Assert.equal(targetPlugin[f], EXPECTED_PLUGIN_DATA[f], f + " must have the correct value."); - } - - // Check plugin mime types. - Assert.ok(targetPlugin.mimeTypes.find(m => m == PLUGIN_MIME_TYPE1)); - Assert.ok(targetPlugin.mimeTypes.find(m => m == PLUGIN_MIME_TYPE2)); - Assert.ok(!targetPlugin.mimeTypes.find(m => m == "Not There.")); - - let personaId = (gIsGonk) ? null : PERSONA_ID; - Assert.equal(data.addons.persona, personaId, "The correct Persona Id must be reported."); - - // Uninstall the addon. - yield AddonManagerTesting.uninstallAddonByID(ADDON_ID); -}); - -add_task(function* test_signedAddon() { - const ADDON_INSTALL_URL = gDataRoot + "signed.xpi"; - const ADDON_ID = "tel-signed-xpi@tests.mozilla.org"; - const ADDON_INSTALL_DATE = truncateToDays(Date.now()); - const EXPECTED_ADDON_DATA = { - blocklisted: false, - description: "A signed addon which gets enabled without a reboot.", - name: "XPI Telemetry Signed Test", - userDisabled: false, - appDisabled: false, - version: "1.0", - scope: 1, - type: "extension", - foreignInstall: false, - hasBinaryComponents: false, - installDay: ADDON_INSTALL_DATE, - updateDay: ADDON_INSTALL_DATE, - signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED, - }; - - let deferred = PromiseUtils.defer(); - TelemetryEnvironment.registerChangeListener("test_signedAddon", deferred.resolve); - - // Install the addon. - yield AddonManagerTesting.installXPIFromURL(ADDON_INSTALL_URL); - - yield deferred.promise; - // Unregister the listener. - TelemetryEnvironment.unregisterChangeListener("test_signedAddon"); - - let data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - - // Check addon data. - Assert.ok(ADDON_ID in data.addons.activeAddons, "Add-on should be in the environment."); - let targetAddon = data.addons.activeAddons[ADDON_ID]; - for (let f in EXPECTED_ADDON_DATA) { - Assert.equal(targetAddon[f], EXPECTED_ADDON_DATA[f], f + " must have the correct value."); - } -}); - -add_task(function* test_addonsFieldsLimit() { - const ADDON_INSTALL_URL = gDataRoot + "long-fields.xpi"; - const ADDON_ID = "tel-longfields-xpi@tests.mozilla.org"; - - // Install the addon and wait for the TelemetryEnvironment to pick it up. - let deferred = PromiseUtils.defer(); - TelemetryEnvironment.registerChangeListener("test_longFieldsAddon", deferred.resolve); - yield AddonManagerTesting.installXPIFromURL(ADDON_INSTALL_URL); - yield deferred.promise; - TelemetryEnvironment.unregisterChangeListener("test_longFieldsAddon"); - - let data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - - // Check that the addon is available and that the string fields are limited. - Assert.ok(ADDON_ID in data.addons.activeAddons, "Add-on should be in the environment."); - let targetAddon = data.addons.activeAddons[ADDON_ID]; - - // TelemetryEnvironment limits the length of string fields for activeAddons to 100 chars, - // to mitigate misbehaving addons. - Assert.lessOrEqual(targetAddon.version.length, 100, - "The version string must have been limited"); - Assert.lessOrEqual(targetAddon.name.length, 100, - "The name string must have been limited"); - Assert.lessOrEqual(targetAddon.description.length, 100, - "The description string must have been limited"); -}); - -add_task(function* test_collectionWithbrokenAddonData() { - const BROKEN_ADDON_ID = "telemetry-test2.example.com@services.mozilla.org"; - const BROKEN_MANIFEST = { - id: "telemetry-test2.example.com@services.mozilla.org", - name: "telemetry broken addon", - origin: "https://telemetry-test2.example.com", - version: 1, // This is intentionally not a string. - signedState: AddonManager.SIGNEDSTATE_SIGNED, - }; - - const ADDON_INSTALL_URL = gDataRoot + "restartless.xpi"; - const ADDON_ID = "tel-restartless-xpi@tests.mozilla.org"; - const ADDON_INSTALL_DATE = truncateToDays(Date.now()); - const EXPECTED_ADDON_DATA = { - blocklisted: false, - description: "A restartless addon which gets enabled without a reboot.", - name: "XPI Telemetry Restartless Test", - userDisabled: false, - appDisabled: false, - version: "1.0", - scope: 1, - type: "extension", - foreignInstall: false, - hasBinaryComponents: false, - installDay: ADDON_INSTALL_DATE, - updateDay: ADDON_INSTALL_DATE, - signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : - AddonManager.SIGNEDSTATE_NOT_REQUIRED, - }; - - let receivedNotifications = 0; - - let registerCheckpointPromise = (aExpected) => { - return new Promise(resolve => TelemetryEnvironment.registerChangeListener( - "testBrokenAddon_collection" + aExpected, (reason, data) => { - Assert.equal(reason, "addons-changed"); - receivedNotifications++; - resolve(); - })); - }; - - let assertCheckpoint = (aExpected) => { - Assert.equal(receivedNotifications, aExpected); - TelemetryEnvironment.unregisterChangeListener("testBrokenAddon_collection" + aExpected); - }; - - // Register the broken provider and install the broken addon. - let checkpointPromise = registerCheckpointPromise(1); - let brokenAddonProvider = createMockAddonProvider("Broken Extensions Provider"); - AddonManagerPrivate.registerProvider(brokenAddonProvider); - brokenAddonProvider.addAddon(BROKEN_MANIFEST); - yield checkpointPromise; - assertCheckpoint(1); - - // Now install an addon which returns the correct information. - checkpointPromise = registerCheckpointPromise(2); - yield AddonManagerTesting.installXPIFromURL(ADDON_INSTALL_URL); - yield checkpointPromise; - assertCheckpoint(2); - - // Check that the new environment contains the Social addon installed with the broken - // manifest and the rest of the data. - let data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data, false, true /* expect broken addons*/); - - let activeAddons = data.addons.activeAddons; - Assert.ok(BROKEN_ADDON_ID in activeAddons, - "The addon with the broken manifest must be reported."); - Assert.equal(activeAddons[BROKEN_ADDON_ID].version, null, - "null should be reported for invalid data."); - Assert.ok(ADDON_ID in activeAddons, - "The valid addon must be reported."); - Assert.equal(activeAddons[ADDON_ID].description, EXPECTED_ADDON_DATA.description, - "The description for the valid addon should be correct."); - - // Unregister the broken provider so we don't mess with other tests. - AddonManagerPrivate.unregisterProvider(brokenAddonProvider); - - // Uninstall the valid addon. - yield AddonManagerTesting.uninstallAddonByID(ADDON_ID); -}); - -add_task(function* test_defaultSearchEngine() { - // Check that no default engine is in the environment before the search service is - // initialized. - let data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - Assert.ok(!("defaultSearchEngine" in data.settings)); - Assert.ok(!("defaultSearchEngineData" in data.settings)); - - // Load the engines definitions from a custom JAR file: that's needed so that - // the search provider reports an engine identifier. - let url = "chrome://testsearchplugin/locale/searchplugins/"; - let resProt = Services.io.getProtocolHandler("resource") - .QueryInterface(Ci.nsIResProtocolHandler); - resProt.setSubstitution("search-plugins", - Services.io.newURI(url, null, null)); - - // Initialize the search service. - yield new Promise(resolve => Services.search.init(resolve)); - - // Our default engine from the JAR file has an identifier. Check if it is correctly - // reported. - data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - Assert.equal(data.settings.defaultSearchEngine, "telemetrySearchIdentifier"); - let expectedSearchEngineData = { - name: "telemetrySearchIdentifier", - loadPath: "jar:[other]/searchTest.jar!testsearchplugin/telemetrySearchIdentifier.xml", - origin: "default", - submissionURL: "http://ar.wikipedia.org/wiki/%D8%AE%D8%A7%D8%B5:%D8%A8%D8%AD%D8%AB?search=&sourceid=Mozilla-search" - }; - Assert.deepEqual(data.settings.defaultSearchEngineData, expectedSearchEngineData); - - // Remove all the search engines. - for (let engine of Services.search.getEngines()) { - Services.search.removeEngine(engine); - } - // The search service does not notify "engine-current" when removing a default engine. - // Manually force the notification. - // TODO: remove this when bug 1165341 is resolved. - Services.obs.notifyObservers(null, "browser-search-engine-modified", "engine-current"); - - // Then check that no default engine is reported if none is available. - data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - Assert.equal(data.settings.defaultSearchEngine, "NONE"); - Assert.deepEqual(data.settings.defaultSearchEngineData, {name:"NONE"}); - - // Add a new search engine (this will have no engine identifier). - const SEARCH_ENGINE_ID = "telemetry_default"; - const SEARCH_ENGINE_URL = "http://www.example.org/?search={searchTerms}"; - Services.search.addEngineWithDetails(SEARCH_ENGINE_ID, "", null, "", "get", SEARCH_ENGINE_URL); - - // Register a new change listener and then wait for the search engine change to be notified. - let deferred = PromiseUtils.defer(); - TelemetryEnvironment.registerChangeListener("testWatch_SearchDefault", deferred.resolve); - Services.search.defaultEngine = Services.search.getEngineByName(SEARCH_ENGINE_ID); - yield deferred.promise; - - data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - - const EXPECTED_SEARCH_ENGINE = "other-" + SEARCH_ENGINE_ID; - Assert.equal(data.settings.defaultSearchEngine, EXPECTED_SEARCH_ENGINE); - - const EXPECTED_SEARCH_ENGINE_DATA = { - name: "telemetry_default", - loadPath: "[other]addEngineWithDetails", - origin: "verified" - }; - Assert.deepEqual(data.settings.defaultSearchEngineData, EXPECTED_SEARCH_ENGINE_DATA); - TelemetryEnvironment.unregisterChangeListener("testWatch_SearchDefault"); - - // Cleanly install an engine from an xml file, and check if origin is - // recorded as "verified". - let promise = new Promise(resolve => { - TelemetryEnvironment.registerChangeListener("testWatch_SearchDefault", resolve); - }); - let engine = yield new Promise((resolve, reject) => { - Services.obs.addObserver(function obs(obsSubject, obsTopic, obsData) { - try { - let searchEngine = obsSubject.QueryInterface(Ci.nsISearchEngine); - do_print("Observed " + obsData + " for " + searchEngine.name); - if (obsData != "engine-added" || searchEngine.name != "engine-telemetry") { - return; - } - - Services.obs.removeObserver(obs, "browser-search-engine-modified"); - resolve(searchEngine); - } catch (ex) { - reject(ex); - } - }, "browser-search-engine-modified", false); - Services.search.addEngine("file://" + do_get_cwd().path + "/engine.xml", - null, null, false); - }); - Services.search.defaultEngine = engine; - yield promise; - TelemetryEnvironment.unregisterChangeListener("testWatch_SearchDefault"); - data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - Assert.deepEqual(data.settings.defaultSearchEngineData, - {"name":"engine-telemetry", "loadPath":"[other]/engine.xml", "origin":"verified"}); - - // Now break this engine's load path hash. - promise = new Promise(resolve => { - TelemetryEnvironment.registerChangeListener("testWatch_SearchDefault", resolve); - }); - engine.wrappedJSObject.setAttr("loadPathHash", "broken"); - Services.obs.notifyObservers(null, "browser-search-engine-modified", "engine-current"); - yield promise; - TelemetryEnvironment.unregisterChangeListener("testWatch_SearchDefault"); - data = TelemetryEnvironment.currentEnvironment; - Assert.equal(data.settings.defaultSearchEngineData.origin, "invalid"); - Services.search.removeEngine(engine); - - // Define and reset the test preference. - const PREF_TEST = "toolkit.telemetry.test.pref1"; - const PREFS_TO_WATCH = new Map([ - [PREF_TEST, {what: TelemetryEnvironment.RECORD_PREF_STATE}], - ]); - Preferences.reset(PREF_TEST); - - // Watch the test preference. - TelemetryEnvironment.testWatchPreferences(PREFS_TO_WATCH); - deferred = PromiseUtils.defer(); - TelemetryEnvironment.registerChangeListener("testSearchEngine_pref", deferred.resolve); - // Trigger an environment change. - Preferences.set(PREF_TEST, 1); - yield deferred.promise; - TelemetryEnvironment.unregisterChangeListener("testSearchEngine_pref"); - - // Check that the search engine information is correctly retained when prefs change. - data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - Assert.equal(data.settings.defaultSearchEngine, EXPECTED_SEARCH_ENGINE); - - // Check that by default we are not sending a cohort identifier... - Assert.equal(data.settings.searchCohort, undefined); - - // ... but that if a cohort identifier is set, we send it. - Services.prefs.setCharPref("browser.search.cohort", "testcohort"); - Services.obs.notifyObservers(null, "browser-search-service", "init-complete"); - data = TelemetryEnvironment.currentEnvironment; - Assert.equal(data.settings.searchCohort, "testcohort"); -}); - -add_task(function* test_osstrings() { - // First test that numbers in sysinfo properties are converted to string fields - // in system.os. - SysInfo.overrides = { - version: 1, - name: 2, - kernel_version: 3, - }; - - yield TelemetryEnvironment.testCleanRestart().onInitialized(); - let data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - - Assert.equal(data.system.os.version, "1"); - Assert.equal(data.system.os.name, "2"); - if (AppConstants.platform == "android") { - Assert.equal(data.system.os.kernelVersion, "3"); - } - - // Check that null values are also handled. - SysInfo.overrides = { - version: null, - name: null, - kernel_version: null, - }; - - yield TelemetryEnvironment.testCleanRestart().onInitialized(); - data = TelemetryEnvironment.currentEnvironment; - checkEnvironmentData(data); - - Assert.equal(data.system.os.version, null); - Assert.equal(data.system.os.name, null); - if (AppConstants.platform == "android") { - Assert.equal(data.system.os.kernelVersion, null); - } - - // Clean up. - SysInfo.overrides = {}; - yield TelemetryEnvironment.testCleanRestart().onInitialized(); -}); - -add_task(function* test_environmentShutdown() { - // Define and reset the test preference. - const PREF_TEST = "toolkit.telemetry.test.pref1"; - const PREFS_TO_WATCH = new Map([ - [PREF_TEST, {what: TelemetryEnvironment.RECORD_PREF_STATE}], - ]); - Preferences.reset(PREF_TEST); - - // Set up the preferences and listener, then the trigger shutdown - TelemetryEnvironment.testWatchPreferences(PREFS_TO_WATCH); - TelemetryEnvironment.registerChangeListener("test_environmentShutdownChange", () => { - // Register a new change listener that asserts if change is propogated - Assert.ok(false, "No change should be propagated after shutdown."); - }); - TelemetryEnvironment.shutdown(); - - // Flipping the test preference after shutdown should not trigger the listener - Preferences.set(PREF_TEST, 1); - - // Unregister the listener. - TelemetryEnvironment.unregisterChangeListener("test_environmentShutdownChange"); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js b/toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js deleted file mode 100644 index 2bfb62c14..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js +++ /dev/null @@ -1,249 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ - -const OPTIN = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN; -const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT; - -function checkEventFormat(events) { - Assert.ok(Array.isArray(events), "Events should be serialized to an array."); - for (let e of events) { - Assert.ok(Array.isArray(e), "Event should be an array."); - Assert.greaterOrEqual(e.length, 4, "Event should have at least 4 elements."); - Assert.lessOrEqual(e.length, 6, "Event should have at most 6 elements."); - - Assert.equal(typeof(e[0]), "number", "Element 0 should be a number."); - Assert.equal(typeof(e[1]), "string", "Element 1 should be a string."); - Assert.equal(typeof(e[2]), "string", "Element 2 should be a string."); - Assert.equal(typeof(e[3]), "string", "Element 3 should be a string."); - - if (e.length > 4) { - Assert.ok(e[4] === null || typeof(e[4]) == "string", - "Event element 4 should be null or a string."); - } - if (e.length > 5) { - Assert.ok(e[5] === null || typeof(e[5]) == "object", - "Event element 5 should be null or an object."); - } - - let extra = e[5]; - if (extra) { - Assert.ok(Object.keys(extra).every(k => typeof(k) == "string"), - "All extra keys should be strings."); - Assert.ok(Object.values(extra).every(v => typeof(v) == "string"), - "All extra values should be strings."); - } - } -} - -add_task(function* test_recording() { - Telemetry.clearEvents(); - - // Record some events. - let expected = [ - {optout: false, event: ["telemetry.test", "test1", "object1"]}, - {optout: false, event: ["telemetry.test", "test2", "object2"]}, - - {optout: false, event: ["telemetry.test", "test1", "object1", "value"]}, - {optout: false, event: ["telemetry.test", "test1", "object1", "value", null]}, - {optout: false, event: ["telemetry.test", "test1", "object1", null, {"key1": "value1"}]}, - {optout: false, event: ["telemetry.test", "test1", "object1", "value", {"key1": "value1", "key2": "value2"}]}, - - {optout: true, event: ["telemetry.test", "optout", "object1"]}, - {optout: false, event: ["telemetry.test.second", "test", "object1"]}, - {optout: false, event: ["telemetry.test.second", "test", "object1", null, {"key1": "value1"}]}, - ]; - - for (let entry of expected) { - entry.tsBefore = Math.floor(Telemetry.msSinceProcessStart()); - try { - Telemetry.recordEvent(...entry.event); - } catch (ex) { - Assert.ok(false, `Failed to record event ${JSON.stringify(entry.event)}: ${ex}`); - } - entry.tsAfter = Math.floor(Telemetry.msSinceProcessStart()); - } - - // Strip off trailing null values to match the serialized events. - for (let entry of expected) { - let e = entry.event; - while ((e.length >= 3) && (e[e.length - 1] === null)) { - e.pop(); - } - } - - // The following should not result in any recorded events. - Assert.throws(() => Telemetry.recordEvent("unknown.category", "test1", "object1"), - /Error: Unknown event: \["unknown.category", "test1", "object1"\]/, - "Should throw on unknown category."); - Assert.throws(() => Telemetry.recordEvent("telemetry.test", "unknown", "object1"), - /Error: Unknown event: \["telemetry.test", "unknown", "object1"\]/, - "Should throw on unknown method."); - Assert.throws(() => Telemetry.recordEvent("telemetry.test", "test1", "unknown"), - /Error: Unknown event: \["telemetry.test", "test1", "unknown"\]/, - "Should throw on unknown object."); - - let checkEvents = (events, expectedEvents) => { - checkEventFormat(events); - Assert.equal(events.length, expectedEvents.length, - "Snapshot should have the right number of events."); - - for (let i = 0; i < events.length; ++i) { - let {tsBefore, tsAfter} = expectedEvents[i]; - let ts = events[i][0]; - Assert.greaterOrEqual(ts, tsBefore, "The recorded timestamp should be greater than the one before recording."); - Assert.lessOrEqual(ts, tsAfter, "The recorded timestamp should be less than the one after recording."); - - let recordedData = events[i].slice(1); - let expectedData = expectedEvents[i].event.slice(); - Assert.deepEqual(recordedData, expectedData, "The recorded event data should match."); - } - }; - - // Check that the expected events were recorded. - let events = Telemetry.snapshotBuiltinEvents(OPTIN, false); - checkEvents(events, expected); - - // Check serializing only opt-out events. - events = Telemetry.snapshotBuiltinEvents(OPTOUT, false); - filtered = expected.filter(e => e.optout == true); - checkEvents(events, filtered); -}); - -add_task(function* test_clear() { - Telemetry.clearEvents(); - - const COUNT = 10; - for (let i = 0; i < COUNT; ++i) { - Telemetry.recordEvent("telemetry.test", "test1", "object1"); - Telemetry.recordEvent("telemetry.test.second", "test", "object1"); - } - - // Check that events were recorded. - // The events are cleared by passing the respective flag. - let events = Telemetry.snapshotBuiltinEvents(OPTIN, true); - Assert.equal(events.length, 2 * COUNT, `Should have recorded ${2 * COUNT} events.`); - - // Now the events should be cleared. - events = Telemetry.snapshotBuiltinEvents(OPTIN, false); - Assert.equal(events.length, 0, `Should have cleared the events.`); -}); - -add_task(function* test_expiry() { - Telemetry.clearEvents(); - - // Recording call with event that is expired by version. - Telemetry.recordEvent("telemetry.test", "expired_version", "object1"); - let events = Telemetry.snapshotBuiltinEvents(OPTIN, true); - Assert.equal(events.length, 0, "Should not record event with expired version."); - - // Recording call with event that is expired by date. - Telemetry.recordEvent("telemetry.test", "expired_date", "object1"); - events = Telemetry.snapshotBuiltinEvents(OPTIN, true); - Assert.equal(events.length, 0, "Should not record event with expired date."); - - // Recording call with event that has expiry_version and expiry_date in the future. - Telemetry.recordEvent("telemetry.test", "not_expired_optout", "object1"); - events = Telemetry.snapshotBuiltinEvents(OPTOUT, true); - Assert.equal(events.length, 1, "Should record event when date and version are not expired."); -}); - -add_task(function* test_invalidParams() { - Telemetry.clearEvents(); - - // Recording call with wrong type for value argument. - Telemetry.recordEvent("telemetry.test", "test1", "object1", 1); - let events = Telemetry.snapshotBuiltinEvents(OPTIN, true); - Assert.equal(events.length, 0, "Should not record event when value argument with invalid type is passed."); - - // Recording call with wrong type for extra argument. - Telemetry.recordEvent("telemetry.test", "test1", "object1", null, "invalid"); - events = Telemetry.snapshotBuiltinEvents(OPTIN, true); - Assert.equal(events.length, 0, "Should not record event when extra argument with invalid type is passed."); - - // Recording call with unknown extra key. - Telemetry.recordEvent("telemetry.test", "test1", "object1", null, {"key3": "x"}); - events = Telemetry.snapshotBuiltinEvents(OPTIN, true); - Assert.equal(events.length, 0, "Should not record event when extra argument with invalid key is passed."); - - // Recording call with invalid value type. - Telemetry.recordEvent("telemetry.test", "test1", "object1", null, {"key3": 1}); - events = Telemetry.snapshotBuiltinEvents(OPTIN, true); - Assert.equal(events.length, 0, "Should not record event when extra argument with invalid value type is passed."); -}); - -add_task(function* test_storageLimit() { - Telemetry.clearEvents(); - - // Record more events than the storage limit allows. - let LIMIT = 1000; - let COUNT = LIMIT + 10; - for (let i = 0; i < COUNT; ++i) { - Telemetry.recordEvent("telemetry.test", "test1", "object1", String(i)); - } - - // Check that the right events were recorded. - let events = Telemetry.snapshotBuiltinEvents(OPTIN, true); - Assert.equal(events.length, LIMIT, `Should have only recorded ${LIMIT} events`); - Assert.ok(events.every((e, idx) => e[4] === String(idx)), - "Should have recorded all events from before hitting the limit."); -}); - -add_task(function* test_valueLimits() { - Telemetry.clearEvents(); - - // Record values that are at or over the limits for string lengths. - let LIMIT = 80; - let expected = [ - ["telemetry.test", "test1", "object1", "a".repeat(LIMIT - 10), null], - ["telemetry.test", "test1", "object1", "a".repeat(LIMIT ), null], - ["telemetry.test", "test1", "object1", "a".repeat(LIMIT + 1), null], - ["telemetry.test", "test1", "object1", "a".repeat(LIMIT + 10), null], - - ["telemetry.test", "test1", "object1", null, {key1: "a".repeat(LIMIT - 10)}], - ["telemetry.test", "test1", "object1", null, {key1: "a".repeat(LIMIT )}], - ["telemetry.test", "test1", "object1", null, {key1: "a".repeat(LIMIT + 1)}], - ["telemetry.test", "test1", "object1", null, {key1: "a".repeat(LIMIT + 10)}], - ]; - - for (let event of expected) { - Telemetry.recordEvent(...event); - if (event[3]) { - event[3] = event[3].substr(0, LIMIT); - } - if (event[4]) { - event[4].key1 = event[4].key1.substr(0, LIMIT); - } - } - - // Strip off trailing null values to match the serialized events. - for (let e of expected) { - while ((e.length >= 3) && (e[e.length - 1] === null)) { - e.pop(); - } - } - - // Check that the right events were recorded. - let events = Telemetry.snapshotBuiltinEvents(OPTIN, true); - Assert.equal(events.length, expected.length, - "Should have recorded the expected number of events"); - for (let i = 0; i < expected.length; ++i) { - Assert.deepEqual(events[i].slice(1), expected[i], - "Should have recorded the expected event data."); - } -}); - -add_task(function* test_unicodeValues() { - Telemetry.clearEvents(); - - // Record string values containing unicode characters. - let value = "漢語"; - Telemetry.recordEvent("telemetry.test", "test1", "object1", value); - Telemetry.recordEvent("telemetry.test", "test1", "object1", null, {"key1": value}); - - // Check that the values were correctly recorded. - let events = Telemetry.snapshotBuiltinEvents(OPTIN, true); - Assert.equal(events.length, 2, "Should have recorded 2 events."); - Assert.equal(events[0][4], value, "Should have recorded the right value."); - Assert.equal(events[1][5]["key1"], value, "Should have recorded the right extra value."); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryFlagClear.js b/toolkit/components/telemetry/tests/unit/test_TelemetryFlagClear.js deleted file mode 100644 index 712aceb3b..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryFlagClear.js +++ /dev/null @@ -1,14 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -function run_test() -{ - let testFlag = Services.telemetry.getHistogramById("TELEMETRY_TEST_FLAG"); - equal(JSON.stringify(testFlag.snapshot().counts), "[1,0,0]", "Original value is correct"); - testFlag.add(1); - equal(JSON.stringify(testFlag.snapshot().counts), "[0,1,0]", "Value is correct after ping."); - testFlag.clear(); - equal(JSON.stringify(testFlag.snapshot().counts), "[1,0,0]", "Value is correct after calling clear()"); - testFlag.add(1); - equal(JSON.stringify(testFlag.snapshot().counts), "[0,1,0]", "Value is correct after ping."); -} diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryLateWrites.js b/toolkit/components/telemetry/tests/unit/test_TelemetryLateWrites.js deleted file mode 100644 index f2b2b3bba..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryLateWrites.js +++ /dev/null @@ -1,127 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ -/* A testcase to make sure reading late writes stacks works. */ - -Cu.import("resource://gre/modules/Services.jsm", this); - -// Constants from prio.h for nsIFileOutputStream.init -const PR_WRONLY = 0x2; -const PR_CREATE_FILE = 0x8; -const PR_TRUNCATE = 0x20; -const RW_OWNER = parseInt("0600", 8); - -const STACK_SUFFIX1 = "stack1.txt"; -const STACK_SUFFIX2 = "stack2.txt"; -const STACK_BOGUS_SUFFIX = "bogus.txt"; -const LATE_WRITE_PREFIX = "Telemetry.LateWriteFinal-"; - -// The names and IDs don't matter, but the format of the IDs does. -const LOADED_MODULES = { - '4759A7E6993548C89CAF716A67EC242D00': 'libtest.so', - 'F77AF15BB8D6419FA875954B4A3506CA00': 'libxul.so', - '1E2F7FB590424E8F93D60BB88D66B8C500': 'libc.so' -}; -const N_MODULES = Object.keys(LOADED_MODULES).length; - -// Format of individual items is [index, offset-in-library]. -const STACK1 = [ - [ 0, 0 ], - [ 1, 1 ], - [ 2, 2 ] -]; -const STACK2 = [ - [ 0, 0 ], - [ 1, 5 ], - [ 2, 10 ], -]; -// XXX The only error checking is for a zero-sized stack. -const STACK_BOGUS = []; - -function write_string_to_file(file, contents) { - let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"] - .createInstance(Ci.nsIFileOutputStream); - ostream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, - RW_OWNER, ostream.DEFER_OPEN); - ostream.write(contents, contents.length); - ostream.QueryInterface(Ci.nsISafeOutputStream).finish(); - ostream.close(); -} - -function construct_file(suffix) { - let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile); - let file = profileDirectory.clone(); - file.append(LATE_WRITE_PREFIX + suffix); - return file; -} - -function write_late_writes_file(stack, suffix) -{ - let file = construct_file(suffix); - let contents = N_MODULES + "\n"; - for (let id in LOADED_MODULES) { - contents += id + " " + LOADED_MODULES[id] + "\n"; - } - - contents += stack.length + "\n"; - for (let element of stack) { - contents += element[0] + " " + element[1].toString(16) + "\n"; - } - - write_string_to_file(file, contents); -} - -function run_test() { - do_get_profile(); - - write_late_writes_file(STACK1, STACK_SUFFIX1); - write_late_writes_file(STACK2, STACK_SUFFIX2); - write_late_writes_file(STACK_BOGUS, STACK_BOGUS_SUFFIX); - - let lateWrites = Telemetry.lateWrites; - do_check_true("memoryMap" in lateWrites); - do_check_eq(lateWrites.memoryMap.length, 0); - do_check_true("stacks" in lateWrites); - do_check_eq(lateWrites.stacks.length, 0); - - do_test_pending(); - Telemetry.asyncFetchTelemetryData(function () { - actual_test(); - }); -} - -function actual_test() { - do_check_false(construct_file(STACK_SUFFIX1).exists()); - do_check_false(construct_file(STACK_SUFFIX2).exists()); - do_check_false(construct_file(STACK_BOGUS_SUFFIX).exists()); - - let lateWrites = Telemetry.lateWrites; - - do_check_true("memoryMap" in lateWrites); - do_check_eq(lateWrites.memoryMap.length, N_MODULES); - for (let id in LOADED_MODULES) { - let matchingLibrary = lateWrites.memoryMap.filter(function(library, idx, array) { - return library[1] == id; - }); - do_check_eq(matchingLibrary.length, 1); - let library = matchingLibrary[0] - let name = library[0]; - do_check_eq(LOADED_MODULES[id], name); - } - - do_check_true("stacks" in lateWrites); - do_check_eq(lateWrites.stacks.length, 2); - let uneval_STACKS = [uneval(STACK1), uneval(STACK2)]; - let first_stack = lateWrites.stacks[0]; - let second_stack = lateWrites.stacks[1]; - function stackChecker(canonicalStack) { - let unevalCanonicalStack = uneval(canonicalStack); - return function(obj, idx, array) { - return unevalCanonicalStack == obj; - } - } - do_check_eq(uneval_STACKS.filter(stackChecker(first_stack)).length, 1); - do_check_eq(uneval_STACKS.filter(stackChecker(second_stack)).length, 1); - - do_test_finished(); -} diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryLockCount.js b/toolkit/components/telemetry/tests/unit/test_TelemetryLockCount.js deleted file mode 100644 index 808f2f3ec..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryLockCount.js +++ /dev/null @@ -1,53 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ -/* A testcase to make sure reading the failed profile lock count works. */ - -Cu.import("resource://gre/modules/Services.jsm", this); - -const LOCK_FILE_NAME = "Telemetry.FailedProfileLocks.txt"; -const N_FAILED_LOCKS = 10; - -// Constants from prio.h for nsIFileOutputStream.init -const PR_WRONLY = 0x2; -const PR_CREATE_FILE = 0x8; -const PR_TRUNCATE = 0x20; -const RW_OWNER = parseInt("0600", 8); - -function write_string_to_file(file, contents) { - let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"] - .createInstance(Ci.nsIFileOutputStream); - ostream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, - RW_OWNER, ostream.DEFER_OPEN); - ostream.write(contents, contents.length); - ostream.QueryInterface(Ci.nsISafeOutputStream).finish(); - ostream.close(); -} - -function construct_file() { - let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile); - let file = profileDirectory.clone(); - file.append(LOCK_FILE_NAME); - return file; -} - -function run_test() { - do_get_profile(); - - do_check_eq(Telemetry.failedProfileLockCount, 0); - - write_string_to_file(construct_file(), N_FAILED_LOCKS.toString()); - - // Make sure that we're not eagerly reading the count now that the - // file exists. - do_check_eq(Telemetry.failedProfileLockCount, 0); - - do_test_pending(); - Telemetry.asyncFetchTelemetryData(actual_test); -} - -function actual_test() { - do_check_eq(Telemetry.failedProfileLockCount, N_FAILED_LOCKS); - do_check_false(construct_file().exists()); - do_test_finished(); -} diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryLog.js b/toolkit/components/telemetry/tests/unit/test_TelemetryLog.js deleted file mode 100644 index ea37a1bc5..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryLog.js +++ /dev/null @@ -1,51 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -Cu.import("resource://gre/modules/TelemetryLog.jsm", this); -Cu.import("resource://gre/modules/TelemetrySession.jsm", this); - -const TEST_PREFIX = "TEST-"; -const TEST_REGEX = new RegExp("^" + TEST_PREFIX); - -function check_event(event, id, data) -{ - do_print("Checking message " + id); - do_check_eq(event[0], id); - do_check_true(event[1] > 0); - - if (data === undefined) { - do_check_true(event.length == 2); - } else { - do_check_eq(event.length, data.length + 2); - for (var i = 0; i < data.length; ++i) { - do_check_eq(typeof(event[i + 2]), "string"); - do_check_eq(event[i + 2], data[i]); - } - } -} - -add_task(function* () -{ - do_get_profile(); - // TODO: After Bug 1254550 lands we should not need to set the pref here. - Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); - yield TelemetryController.testSetup(); - - TelemetryLog.log(TEST_PREFIX + "1", ["val", 123, undefined]); - TelemetryLog.log(TEST_PREFIX + "2", []); - TelemetryLog.log(TEST_PREFIX + "3"); - - var log = TelemetrySession.getPayload().log.filter(function(e) { - // Only want events that were generated by the test. - return TEST_REGEX.test(e[0]); - }); - - do_check_eq(log.length, 3); - check_event(log[0], TEST_PREFIX + "1", ["val", "123", "undefined"]); - check_event(log[1], TEST_PREFIX + "2", []); - check_event(log[2], TEST_PREFIX + "3", undefined); - do_check_true(log[0][1] <= log[1][1]); - do_check_true(log[1][1] <= log[2][1]); - - yield TelemetryController.testShutdown(); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryReportingPolicy.js b/toolkit/components/telemetry/tests/unit/test_TelemetryReportingPolicy.js deleted file mode 100644 index 68606a98e..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryReportingPolicy.js +++ /dev/null @@ -1,268 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -// Test that TelemetryController sends close to shutdown don't lead -// to AsyncShutdown timeouts. - -"use strict"; - -Cu.import("resource://gre/modules/Preferences.jsm", this); -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetrySend.jsm", this); -Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm", this); -Cu.import("resource://gre/modules/TelemetryUtils.jsm", this); -Cu.import("resource://gre/modules/Timer.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); -Cu.import("resource://gre/modules/UpdateUtils.jsm", this); - -const PREF_BRANCH = "toolkit.telemetry."; -const PREF_SERVER = PREF_BRANCH + "server"; - -const TEST_CHANNEL = "TestChannelABC"; - -const PREF_POLICY_BRANCH = "datareporting.policy."; -const PREF_BYPASS_NOTIFICATION = PREF_POLICY_BRANCH + "dataSubmissionPolicyBypassNotification"; -const PREF_DATA_SUBMISSION_ENABLED = PREF_POLICY_BRANCH + "dataSubmissionEnabled"; -const PREF_CURRENT_POLICY_VERSION = PREF_POLICY_BRANCH + "currentPolicyVersion"; -const PREF_MINIMUM_POLICY_VERSION = PREF_POLICY_BRANCH + "minimumPolicyVersion"; -const PREF_MINIMUM_CHANNEL_POLICY_VERSION = PREF_MINIMUM_POLICY_VERSION + ".channel-" + TEST_CHANNEL; -const PREF_ACCEPTED_POLICY_VERSION = PREF_POLICY_BRANCH + "dataSubmissionPolicyAcceptedVersion"; -const PREF_ACCEPTED_POLICY_DATE = PREF_POLICY_BRANCH + "dataSubmissionPolicyNotifiedTime"; - -function fakeShowPolicyTimeout(set, clear) { - let reportingPolicy = Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm"); - reportingPolicy.Policy.setShowInfobarTimeout = set; - reportingPolicy.Policy.clearShowInfobarTimeout = clear; -} - -function fakeResetAcceptedPolicy() { - Preferences.reset(PREF_ACCEPTED_POLICY_DATE); - Preferences.reset(PREF_ACCEPTED_POLICY_VERSION); -} - -function setMinimumPolicyVersion(aNewPolicyVersion) { - const CHANNEL_NAME = UpdateUtils.getUpdateChannel(false); - // We might have channel-dependent minimum policy versions. - const CHANNEL_DEPENDENT_PREF = PREF_MINIMUM_POLICY_VERSION + ".channel-" + CHANNEL_NAME; - - // Does the channel-dependent pref exist? If so, set its value. - if (Preferences.get(CHANNEL_DEPENDENT_PREF, undefined)) { - Preferences.set(CHANNEL_DEPENDENT_PREF, aNewPolicyVersion); - return; - } - - // We don't have a channel specific minimu, so set the common one. - Preferences.set(PREF_MINIMUM_POLICY_VERSION, aNewPolicyVersion); -} - -add_task(function* test_setup() { - // Addon manager needs a profile directory - do_get_profile(true); - loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - - Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); - // Don't bypass the notifications in this test, we'll fake it. - Services.prefs.setBoolPref(PREF_BYPASS_NOTIFICATION, false); - - TelemetryReportingPolicy.setup(); -}); - -add_task(function* test_firstRun() { - const PREF_FIRST_RUN = "toolkit.telemetry.reportingpolicy.firstRun"; - const FIRST_RUN_TIMEOUT_MSEC = 60 * 1000; // 60s - const OTHER_RUNS_TIMEOUT_MSEC = 10 * 1000; // 10s - - Preferences.reset(PREF_FIRST_RUN); - - let startupTimeout = 0; - fakeShowPolicyTimeout((callback, timeout) => startupTimeout = timeout, () => {}); - TelemetryReportingPolicy.reset(); - - Services.obs.notifyObservers(null, "sessionstore-windows-restored", null); - Assert.equal(startupTimeout, FIRST_RUN_TIMEOUT_MSEC, - "The infobar display timeout should be 60s on the first run."); - - // Run again, and check that we actually wait only 10 seconds. - TelemetryReportingPolicy.reset(); - Services.obs.notifyObservers(null, "sessionstore-windows-restored", null); - Assert.equal(startupTimeout, OTHER_RUNS_TIMEOUT_MSEC, - "The infobar display timeout should be 10s on other runs."); -}); - -add_task(function* test_prefs() { - TelemetryReportingPolicy.reset(); - - let now = fakeNow(2009, 11, 18); - - // If the date is not valid (earlier than 2012), we don't regard the policy as accepted. - TelemetryReportingPolicy.testInfobarShown(); - Assert.ok(!TelemetryReportingPolicy.testIsUserNotified()); - Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_DATE, null), 0, - "Invalid dates should not make the policy accepted."); - - // Check that the notification date and version are correctly saved to the prefs. - now = fakeNow(2012, 11, 18); - TelemetryReportingPolicy.testInfobarShown(); - Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_DATE, null), now.getTime(), - "A valid date must correctly be saved."); - - // Now that user is notified, check if we are allowed to upload. - Assert.ok(TelemetryReportingPolicy.canUpload(), - "We must be able to upload after the policy is accepted."); - - // Disable submission and check that we're no longer allowed to upload. - Preferences.set(PREF_DATA_SUBMISSION_ENABLED, false); - Assert.ok(!TelemetryReportingPolicy.canUpload(), - "We must not be able to upload if data submission is disabled."); - - // Turn the submission back on. - Preferences.set(PREF_DATA_SUBMISSION_ENABLED, true); - Assert.ok(TelemetryReportingPolicy.canUpload(), - "We must be able to upload if data submission is enabled and the policy was accepted."); - - // Set a new minimum policy version and check that user is no longer notified. - let newMinimum = Preferences.get(PREF_CURRENT_POLICY_VERSION, 1) + 1; - setMinimumPolicyVersion(newMinimum); - Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(), - "A greater minimum policy version must invalidate the policy and disable upload."); - - // Eventually accept the policy and make sure user is notified. - Preferences.set(PREF_CURRENT_POLICY_VERSION, newMinimum); - TelemetryReportingPolicy.testInfobarShown(); - Assert.ok(TelemetryReportingPolicy.testIsUserNotified(), - "Accepting the policy again should show the user as notified."); - Assert.ok(TelemetryReportingPolicy.canUpload(), - "Accepting the policy again should let us upload data."); - - // Set a new, per channel, minimum policy version. Start by setting a test current channel. - let defaultPrefs = new Preferences({ defaultBranch: true }); - defaultPrefs.set("app.update.channel", TEST_CHANNEL); - - // Increase and set the new minimum version, then check that we're not notified anymore. - newMinimum++; - Preferences.set(PREF_MINIMUM_CHANNEL_POLICY_VERSION, newMinimum); - Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(), - "Increasing the minimum policy version should invalidate the policy."); - - // Eventually accept the policy and make sure user is notified. - Preferences.set(PREF_CURRENT_POLICY_VERSION, newMinimum); - TelemetryReportingPolicy.testInfobarShown(); - Assert.ok(TelemetryReportingPolicy.testIsUserNotified(), - "Accepting the policy again should show the user as notified."); - Assert.ok(TelemetryReportingPolicy.canUpload(), - "Accepting the policy again should let us upload data."); -}); - -add_task(function* test_migratePrefs() { - const DEPRECATED_FHR_PREFS = { - "datareporting.policy.dataSubmissionPolicyAccepted": true, - "datareporting.policy.dataSubmissionPolicyBypassAcceptance": true, - "datareporting.policy.dataSubmissionPolicyResponseType": "foxyeah", - "datareporting.policy.dataSubmissionPolicyResponseTime": Date.now().toString(), - }; - - // Make sure the preferences are set before setting up the policy. - for (let name in DEPRECATED_FHR_PREFS) { - Preferences.set(name, DEPRECATED_FHR_PREFS[name]); - } - // Set up the policy. - TelemetryReportingPolicy.reset(); - // They should have been removed by now. - for (let name in DEPRECATED_FHR_PREFS) { - Assert.ok(!Preferences.has(name), name + " should have been removed."); - } -}); - -add_task(function* test_userNotifiedOfCurrentPolicy() { - fakeResetAcceptedPolicy(); - TelemetryReportingPolicy.reset(); - - // User should be reported as not notified by default. - Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(), - "The initial state should be unnotified."); - - // Forcing a policy version should not automatically make the user notified. - Preferences.set(PREF_ACCEPTED_POLICY_VERSION, - TelemetryReportingPolicy.DEFAULT_DATAREPORTING_POLICY_VERSION); - Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(), - "The default state of the date should have a time of 0 and it should therefore fail"); - - // Showing the notification bar should make the user notified. - fakeNow(2012, 11, 11); - TelemetryReportingPolicy.testInfobarShown(); - Assert.ok(TelemetryReportingPolicy.testIsUserNotified(), - "Using the proper API causes user notification to report as true."); - - // It is assumed that later versions of the policy will incorporate previous - // ones, therefore this should also return true. - let newVersion = - Preferences.get(PREF_CURRENT_POLICY_VERSION, 1) + 1; - Preferences.set(PREF_ACCEPTED_POLICY_VERSION, newVersion); - Assert.ok(TelemetryReportingPolicy.testIsUserNotified(), - "A future version of the policy should pass."); - - newVersion = - Preferences.get(PREF_CURRENT_POLICY_VERSION, 1) - 1; - Preferences.set(PREF_ACCEPTED_POLICY_VERSION, newVersion); - Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(), - "A previous version of the policy should fail."); -}); - -add_task(function* test_canSend() { - const TEST_PING_TYPE = "test-ping"; - - PingServer.start(); - Preferences.set(PREF_SERVER, "http://localhost:" + PingServer.port); - - yield TelemetryController.testReset(); - TelemetryReportingPolicy.reset(); - - // User should be reported as not notified by default. - Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(), - "The initial state should be unnotified."); - - // Assert if we receive any ping before the policy is accepted. - PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now")); - yield TelemetryController.submitExternalPing(TEST_PING_TYPE, {}); - - // Reset the ping handler. - PingServer.resetPingHandler(); - - // Fake the infobar: this should also trigger the ping send task. - TelemetryReportingPolicy.testInfobarShown(); - let ping = yield PingServer.promiseNextPings(1); - Assert.equal(ping.length, 1, "We should have received one ping."); - Assert.equal(ping[0].type, TEST_PING_TYPE, - "We should have received the previous ping."); - - // Submit another ping, to make sure it gets sent. - yield TelemetryController.submitExternalPing(TEST_PING_TYPE, {}); - - // Get the ping and check its type. - ping = yield PingServer.promiseNextPings(1); - Assert.equal(ping.length, 1, "We should have received one ping."); - Assert.equal(ping[0].type, TEST_PING_TYPE, "We should have received the new ping."); - - // Fake a restart with a pending ping. - yield TelemetryController.addPendingPing(TEST_PING_TYPE, {}); - yield TelemetryController.testReset(); - - // We should be immediately sending the ping out. - ping = yield PingServer.promiseNextPings(1); - Assert.equal(ping.length, 1, "We should have received one ping."); - Assert.equal(ping[0].type, TEST_PING_TYPE, "We should have received the pending ping."); - - // Submit another ping, to make sure it gets sent. - yield TelemetryController.submitExternalPing(TEST_PING_TYPE, {}); - - // Get the ping and check its type. - ping = yield PingServer.promiseNextPings(1); - Assert.equal(ping.length, 1, "We should have received one ping."); - Assert.equal(ping[0].type, TEST_PING_TYPE, "We should have received the new ping."); - - yield PingServer.stop(); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryScalars.js b/toolkit/components/telemetry/tests/unit/test_TelemetryScalars.js deleted file mode 100644 index 5914a4235..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryScalars.js +++ /dev/null @@ -1,574 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ - -const UINT_SCALAR = "telemetry.test.unsigned_int_kind"; -const STRING_SCALAR = "telemetry.test.string_kind"; -const BOOLEAN_SCALAR = "telemetry.test.boolean_kind"; -const KEYED_UINT_SCALAR = "telemetry.test.keyed_unsigned_int"; - -add_task(function* test_serializationFormat() { - Telemetry.clearScalars(); - - // Set the scalars to a known value. - const expectedUint = 3785; - const expectedString = "some value"; - Telemetry.scalarSet(UINT_SCALAR, expectedUint); - Telemetry.scalarSet(STRING_SCALAR, expectedString); - Telemetry.scalarSet(BOOLEAN_SCALAR, true); - Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, "first_key", 1234); - - // Get a snapshot of the scalars. - const scalars = - Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - - // Check that they are serialized to the correct format. - Assert.equal(typeof(scalars[UINT_SCALAR]), "number", - UINT_SCALAR + " must be serialized to the correct format."); - Assert.ok(Number.isInteger(scalars[UINT_SCALAR]), - UINT_SCALAR + " must be a finite integer."); - Assert.equal(scalars[UINT_SCALAR], expectedUint, - UINT_SCALAR + " must have the correct value."); - Assert.equal(typeof(scalars[STRING_SCALAR]), "string", - STRING_SCALAR + " must be serialized to the correct format."); - Assert.equal(scalars[STRING_SCALAR], expectedString, - STRING_SCALAR + " must have the correct value."); - Assert.equal(typeof(scalars[BOOLEAN_SCALAR]), "boolean", - BOOLEAN_SCALAR + " must be serialized to the correct format."); - Assert.equal(scalars[BOOLEAN_SCALAR], true, - BOOLEAN_SCALAR + " must have the correct value."); - Assert.ok(!(KEYED_UINT_SCALAR in scalars), - "Keyed scalars must be reported in a separate section."); -}); - -add_task(function* test_keyedSerializationFormat() { - Telemetry.clearScalars(); - - const expectedKey = "first_key"; - const expectedOtherKey = "漢語"; - const expectedUint = 3785; - const expectedOtherValue = 1107; - - Telemetry.scalarSet(UINT_SCALAR, expectedUint); - Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, expectedKey, expectedUint); - Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, expectedOtherKey, expectedOtherValue); - - // Get a snapshot of the scalars. - const keyedScalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - - Assert.ok(!(UINT_SCALAR in keyedScalars), - UINT_SCALAR + " must not be serialized with the keyed scalars."); - Assert.ok(KEYED_UINT_SCALAR in keyedScalars, - KEYED_UINT_SCALAR + " must be serialized with the keyed scalars."); - Assert.equal(Object.keys(keyedScalars[KEYED_UINT_SCALAR]).length, 2, - "The keyed scalar must contain exactly 2 keys."); - Assert.ok(expectedKey in keyedScalars[KEYED_UINT_SCALAR], - KEYED_UINT_SCALAR + " must contain the expected keys."); - Assert.ok(expectedOtherKey in keyedScalars[KEYED_UINT_SCALAR], - KEYED_UINT_SCALAR + " must contain the expected keys."); - Assert.ok(Number.isInteger(keyedScalars[KEYED_UINT_SCALAR][expectedKey]), - KEYED_UINT_SCALAR + "." + expectedKey + " must be a finite integer."); - Assert.equal(keyedScalars[KEYED_UINT_SCALAR][expectedKey], expectedUint, - KEYED_UINT_SCALAR + "." + expectedKey + " must have the correct value."); - Assert.equal(keyedScalars[KEYED_UINT_SCALAR][expectedOtherKey], expectedOtherValue, - KEYED_UINT_SCALAR + "." + expectedOtherKey + " must have the correct value."); -}); - -add_task(function* test_nonexistingScalar() { - const NON_EXISTING_SCALAR = "telemetry.test.non_existing"; - - Telemetry.clearScalars(); - - // Make sure we throw on any operation for non-existing scalars. - Assert.throws(() => Telemetry.scalarAdd(NON_EXISTING_SCALAR, 11715), - /NS_ERROR_ILLEGAL_VALUE/, - "Adding to a non existing scalar must throw."); - Assert.throws(() => Telemetry.scalarSet(NON_EXISTING_SCALAR, 11715), - /NS_ERROR_ILLEGAL_VALUE/, - "Setting a non existing scalar must throw."); - Assert.throws(() => Telemetry.scalarSetMaximum(NON_EXISTING_SCALAR, 11715), - /NS_ERROR_ILLEGAL_VALUE/, - "Setting the maximum of a non existing scalar must throw."); - - // Make sure we throw on any operation for non-existing scalars. - Assert.throws(() => Telemetry.keyedScalarAdd(NON_EXISTING_SCALAR, "some_key", 11715), - /NS_ERROR_ILLEGAL_VALUE/, - "Adding to a non existing keyed scalar must throw."); - Assert.throws(() => Telemetry.keyedScalarSet(NON_EXISTING_SCALAR, "some_key", 11715), - /NS_ERROR_ILLEGAL_VALUE/, - "Setting a non existing keyed scalar must throw."); - Assert.throws(() => Telemetry.keyedScalarSetMaximum(NON_EXISTING_SCALAR, "some_key", 11715), - /NS_ERROR_ILLEGAL_VALUE/, - "Setting the maximum of a non keyed existing scalar must throw."); - - // Get a snapshot of the scalars. - const scalars = - Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - - Assert.ok(!(NON_EXISTING_SCALAR in scalars), "The non existing scalar must not be persisted."); - - const keyedScalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - - Assert.ok(!(NON_EXISTING_SCALAR in keyedScalars), - "The non existing keyed scalar must not be persisted."); -}); - -add_task(function* test_expiredScalar() { - const EXPIRED_SCALAR = "telemetry.test.expired"; - const EXPIRED_KEYED_SCALAR = "telemetry.test.keyed_expired"; - const UNEXPIRED_SCALAR = "telemetry.test.unexpired"; - - Telemetry.clearScalars(); - - // Try to set the expired scalar to some value. We will not be recording the value, - // but we shouldn't throw. - Telemetry.scalarAdd(EXPIRED_SCALAR, 11715); - Telemetry.scalarSet(EXPIRED_SCALAR, 11715); - Telemetry.scalarSetMaximum(EXPIRED_SCALAR, 11715); - Telemetry.keyedScalarAdd(EXPIRED_KEYED_SCALAR, "some_key", 11715); - Telemetry.keyedScalarSet(EXPIRED_KEYED_SCALAR, "some_key", 11715); - Telemetry.keyedScalarSetMaximum(EXPIRED_KEYED_SCALAR, "some_key", 11715); - - // The unexpired scalar has an expiration version, but far away in the future. - const expectedValue = 11716; - Telemetry.scalarSet(UNEXPIRED_SCALAR, expectedValue); - - // Get a snapshot of the scalars. - const scalars = - Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - const keyedScalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - - Assert.ok(!(EXPIRED_SCALAR in scalars), "The expired scalar must not be persisted."); - Assert.equal(scalars[UNEXPIRED_SCALAR], expectedValue, - "The unexpired scalar must be persisted with the correct value."); - Assert.ok(!(EXPIRED_KEYED_SCALAR in keyedScalars), - "The expired keyed scalar must not be persisted."); -}); - -add_task(function* test_unsignedIntScalar() { - let checkScalar = (expectedValue) => { - const scalars = - Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - Assert.equal(scalars[UINT_SCALAR], expectedValue, - UINT_SCALAR + " must contain the expected value."); - }; - - Telemetry.clearScalars(); - - // Let's start with an accumulation without a prior set. - Telemetry.scalarAdd(UINT_SCALAR, 1); - Telemetry.scalarAdd(UINT_SCALAR, 2); - // Do we get what we expect? - checkScalar(3); - - // Let's test setting the scalar to a value. - Telemetry.scalarSet(UINT_SCALAR, 3785); - checkScalar(3785); - Telemetry.scalarAdd(UINT_SCALAR, 1); - checkScalar(3786); - - // Does setMaximum work? - Telemetry.scalarSet(UINT_SCALAR, 2); - checkScalar(2); - Telemetry.scalarSetMaximum(UINT_SCALAR, 5); - checkScalar(5); - // The value of the probe should still be 5, as the previous value - // is greater than the one we want to set. - Telemetry.scalarSetMaximum(UINT_SCALAR, 3); - checkScalar(5); - - // Check that non-integer numbers get truncated and set. - Telemetry.scalarSet(UINT_SCALAR, 3.785); - checkScalar(3); - - // Setting or adding a negative number must report an error through - // the console and drop the change (shouldn't throw). - Telemetry.scalarAdd(UINT_SCALAR, -5); - Telemetry.scalarSet(UINT_SCALAR, -5); - Telemetry.scalarSetMaximum(UINT_SCALAR, -1); - checkScalar(3); - - // What happens if we try to set a value of a different type? - Telemetry.scalarSet(UINT_SCALAR, 1); - Assert.throws(() => Telemetry.scalarSet(UINT_SCALAR, "unexpected value"), - /NS_ERROR_ILLEGAL_VALUE/, - "Setting the scalar to an unexpected value type must throw."); - Assert.throws(() => Telemetry.scalarAdd(UINT_SCALAR, "unexpected value"), - /NS_ERROR_ILLEGAL_VALUE/, - "Adding an unexpected value type must throw."); - Assert.throws(() => Telemetry.scalarSetMaximum(UINT_SCALAR, "unexpected value"), - /NS_ERROR_ILLEGAL_VALUE/, - "Setting the scalar to an unexpected value type must throw."); - // The stored value must not be compromised. - checkScalar(1); -}); - -add_task(function* test_stringScalar() { - let checkExpectedString = (expectedString) => { - const scalars = - Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - Assert.equal(scalars[STRING_SCALAR], expectedString, - STRING_SCALAR + " must contain the expected string value."); - }; - - Telemetry.clearScalars(); - - // Let's check simple strings... - let expected = "test string"; - Telemetry.scalarSet(STRING_SCALAR, expected); - checkExpectedString(expected); - expected = "漢語"; - Telemetry.scalarSet(STRING_SCALAR, expected); - checkExpectedString(expected); - - // We have some unsupported operations for strings. - Assert.throws(() => Telemetry.scalarAdd(STRING_SCALAR, 1), - /NS_ERROR_NOT_AVAILABLE/, - "Using an unsupported operation must throw."); - Assert.throws(() => Telemetry.scalarAdd(STRING_SCALAR, "string value"), - /NS_ERROR_NOT_AVAILABLE/, - "Using an unsupported operation must throw."); - Assert.throws(() => Telemetry.scalarSetMaximum(STRING_SCALAR, 1), - /NS_ERROR_NOT_AVAILABLE/, - "Using an unsupported operation must throw."); - Assert.throws(() => Telemetry.scalarSetMaximum(STRING_SCALAR, "string value"), - /NS_ERROR_NOT_AVAILABLE/, - "Using an unsupported operation must throw."); - Assert.throws(() => Telemetry.scalarSet(STRING_SCALAR, 1), - /NS_ERROR_ILLEGAL_VALUE/, - "The string scalar must throw if we're not setting a string."); - - // Try to set the scalar to a string longer than the maximum length limit. - const LONG_STRING = "browser.qaxfiuosnzmhlg.rpvxicawolhtvmbkpnludhedobxvkjwqyeyvmv"; - Telemetry.scalarSet(STRING_SCALAR, LONG_STRING); - checkExpectedString(LONG_STRING.substr(0, 50)); -}); - -add_task(function* test_booleanScalar() { - let checkExpectedBool = (expectedBoolean) => { - const scalars = - Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - Assert.equal(scalars[BOOLEAN_SCALAR], expectedBoolean, - BOOLEAN_SCALAR + " must contain the expected boolean value."); - }; - - Telemetry.clearScalars(); - - // Set a test boolean value. - let expected = false; - Telemetry.scalarSet(BOOLEAN_SCALAR, expected); - checkExpectedBool(expected); - expected = true; - Telemetry.scalarSet(BOOLEAN_SCALAR, expected); - checkExpectedBool(expected); - - // Check that setting a numeric value implicitly converts to boolean. - Telemetry.scalarSet(BOOLEAN_SCALAR, 1); - checkExpectedBool(true); - Telemetry.scalarSet(BOOLEAN_SCALAR, 0); - checkExpectedBool(false); - Telemetry.scalarSet(BOOLEAN_SCALAR, 1.0); - checkExpectedBool(true); - Telemetry.scalarSet(BOOLEAN_SCALAR, 0.0); - checkExpectedBool(false); - - // Check that unsupported operations for booleans throw. - Assert.throws(() => Telemetry.scalarAdd(BOOLEAN_SCALAR, 1), - /NS_ERROR_NOT_AVAILABLE/, - "Using an unsupported operation must throw."); - Assert.throws(() => Telemetry.scalarAdd(BOOLEAN_SCALAR, "string value"), - /NS_ERROR_NOT_AVAILABLE/, - "Using an unsupported operation must throw."); - Assert.throws(() => Telemetry.scalarSetMaximum(BOOLEAN_SCALAR, 1), - /NS_ERROR_NOT_AVAILABLE/, - "Using an unsupported operation must throw."); - Assert.throws(() => Telemetry.scalarSetMaximum(BOOLEAN_SCALAR, "string value"), - /NS_ERROR_NOT_AVAILABLE/, - "Using an unsupported operation must throw."); - Assert.throws(() => Telemetry.scalarSet(BOOLEAN_SCALAR, "true"), - /NS_ERROR_ILLEGAL_VALUE/, - "The boolean scalar must throw if we're not setting a boolean."); -}); - -add_task(function* test_scalarRecording() { - const OPTIN_SCALAR = "telemetry.test.release_optin"; - const OPTOUT_SCALAR = "telemetry.test.release_optout"; - - let checkValue = (scalarName, expectedValue) => { - const scalars = - Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - Assert.equal(scalars[scalarName], expectedValue, - scalarName + " must contain the expected value."); - }; - - let checkNotSerialized = (scalarName) => { - const scalars = - Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - Assert.ok(!(scalarName in scalars), scalarName + " was not recorded."); - }; - - Telemetry.canRecordBase = false; - Telemetry.canRecordExtended = false; - Telemetry.clearScalars(); - - // Check that no scalar is recorded if both base and extended recording are off. - Telemetry.scalarSet(OPTOUT_SCALAR, 3); - Telemetry.scalarSet(OPTIN_SCALAR, 3); - checkNotSerialized(OPTOUT_SCALAR); - checkNotSerialized(OPTIN_SCALAR); - - // Check that opt-out scalars are recorded, while opt-in are not. - Telemetry.canRecordBase = true; - Telemetry.scalarSet(OPTOUT_SCALAR, 3); - Telemetry.scalarSet(OPTIN_SCALAR, 3); - checkValue(OPTOUT_SCALAR, 3); - checkNotSerialized(OPTIN_SCALAR); - - // Check that both opt-out and opt-in scalars are recorded. - Telemetry.canRecordExtended = true; - Telemetry.scalarSet(OPTOUT_SCALAR, 5); - Telemetry.scalarSet(OPTIN_SCALAR, 6); - checkValue(OPTOUT_SCALAR, 5); - checkValue(OPTIN_SCALAR, 6); -}); - -add_task(function* test_keyedScalarRecording() { - const OPTIN_SCALAR = "telemetry.test.keyed_release_optin"; - const OPTOUT_SCALAR = "telemetry.test.keyed_release_optout"; - const testKey = "policy_key"; - - let checkValue = (scalarName, expectedValue) => { - const scalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - Assert.equal(scalars[scalarName][testKey], expectedValue, - scalarName + " must contain the expected value."); - }; - - let checkNotSerialized = (scalarName) => { - const scalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - Assert.ok(!(scalarName in scalars), scalarName + " was not recorded."); - }; - - Telemetry.canRecordBase = false; - Telemetry.canRecordExtended = false; - Telemetry.clearScalars(); - - // Check that no scalar is recorded if both base and extended recording are off. - Telemetry.keyedScalarSet(OPTOUT_SCALAR, testKey, 3); - Telemetry.keyedScalarSet(OPTIN_SCALAR, testKey, 3); - checkNotSerialized(OPTOUT_SCALAR); - checkNotSerialized(OPTIN_SCALAR); - - // Check that opt-out scalars are recorded, while opt-in are not. - Telemetry.canRecordBase = true; - Telemetry.keyedScalarSet(OPTOUT_SCALAR, testKey, 3); - Telemetry.keyedScalarSet(OPTIN_SCALAR, testKey, 3); - checkValue(OPTOUT_SCALAR, 3); - checkNotSerialized(OPTIN_SCALAR); - - // Check that both opt-out and opt-in scalars are recorded. - Telemetry.canRecordExtended = true; - Telemetry.keyedScalarSet(OPTOUT_SCALAR, testKey, 5); - Telemetry.keyedScalarSet(OPTIN_SCALAR, testKey, 6); - checkValue(OPTOUT_SCALAR, 5); - checkValue(OPTIN_SCALAR, 6); -}); - -add_task(function* test_subsession() { - Telemetry.clearScalars(); - - // Set the scalars to a known value. - Telemetry.scalarSet(UINT_SCALAR, 3785); - Telemetry.scalarSet(STRING_SCALAR, "some value"); - Telemetry.scalarSet(BOOLEAN_SCALAR, false); - Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, "some_random_key", 12); - - // Get a snapshot and reset the subsession. The value we set must be there. - let scalars = - Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true); - let keyedScalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true); - - Assert.equal(scalars[UINT_SCALAR], 3785, - UINT_SCALAR + " must contain the expected value."); - Assert.equal(scalars[STRING_SCALAR], "some value", - STRING_SCALAR + " must contain the expected value."); - Assert.equal(scalars[BOOLEAN_SCALAR], false, - BOOLEAN_SCALAR + " must contain the expected value."); - Assert.equal(keyedScalars[KEYED_UINT_SCALAR]["some_random_key"], 12, - KEYED_UINT_SCALAR + " must contain the expected value."); - - // Get a new snapshot and reset the subsession again. Since no new value - // was set, the scalars should not be reported. - scalars = - Telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true); - keyedScalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true); - - Assert.ok(!(UINT_SCALAR in scalars), UINT_SCALAR + " must be empty and not reported."); - Assert.ok(!(STRING_SCALAR in scalars), STRING_SCALAR + " must be empty and not reported."); - Assert.ok(!(BOOLEAN_SCALAR in scalars), BOOLEAN_SCALAR + " must be empty and not reported."); - Assert.ok(!(KEYED_UINT_SCALAR in keyedScalars), KEYED_UINT_SCALAR + " must be empty and not reported."); -}); - -add_task(function* test_keyed_uint() { - Telemetry.clearScalars(); - - const KEYS = [ "a_key", "another_key", "third_key" ]; - let expectedValues = [ 1, 1, 1 ]; - - // Set all the keys to a baseline value. - for (let key of KEYS) { - Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, key, 1); - } - - // Increment only one key. - Telemetry.keyedScalarAdd(KEYED_UINT_SCALAR, KEYS[1], 1); - expectedValues[1]++; - - // Use SetMaximum on the third key. - Telemetry.keyedScalarSetMaximum(KEYED_UINT_SCALAR, KEYS[2], 37); - expectedValues[2] = 37; - - // Get a snapshot of the scalars and make sure the keys contain - // the correct values. - const keyedScalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - - for (let k = 0; k < 3; k++) { - const keyName = KEYS[k]; - Assert.equal(keyedScalars[KEYED_UINT_SCALAR][keyName], expectedValues[k], - KEYED_UINT_SCALAR + "." + keyName + " must contain the correct value."); - } - - // Are we still throwing when doing unsupported things on uint keyed scalars? - // Just test one single unsupported operation, the other are covered in the plain - // unsigned scalar test. - Assert.throws(() => Telemetry.scalarSet(KEYED_UINT_SCALAR, "new_key", "unexpected value"), - /NS_ERROR_ILLEGAL_VALUE/, - "Setting the scalar to an unexpected value type must throw."); -}); - -add_task(function* test_keyed_boolean() { - Telemetry.clearScalars(); - - const KEYED_BOOLEAN_TYPE = "telemetry.test.keyed_boolean_kind"; - const first_key = "first_key"; - const second_key = "second_key"; - - // Set the initial values. - Telemetry.keyedScalarSet(KEYED_BOOLEAN_TYPE, first_key, true); - Telemetry.keyedScalarSet(KEYED_BOOLEAN_TYPE, second_key, false); - - // Get a snapshot of the scalars and make sure the keys contain - // the correct values. - let keyedScalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - Assert.equal(keyedScalars[KEYED_BOOLEAN_TYPE][first_key], true, - "The key must contain the expected value."); - Assert.equal(keyedScalars[KEYED_BOOLEAN_TYPE][second_key], false, - "The key must contain the expected value."); - - // Now flip the values and make sure we get the expected values back. - Telemetry.keyedScalarSet(KEYED_BOOLEAN_TYPE, first_key, false); - Telemetry.keyedScalarSet(KEYED_BOOLEAN_TYPE, second_key, true); - - keyedScalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - Assert.equal(keyedScalars[KEYED_BOOLEAN_TYPE][first_key], false, - "The key must contain the expected value."); - Assert.equal(keyedScalars[KEYED_BOOLEAN_TYPE][second_key], true, - "The key must contain the expected value."); - - // Are we still throwing when doing unsupported things on a boolean keyed scalars? - // Just test one single unsupported operation, the other are covered in the plain - // boolean scalar test. - Assert.throws(() => Telemetry.keyedScalarAdd(KEYED_BOOLEAN_TYPE, "somehey", 1), - /NS_ERROR_NOT_AVAILABLE/, - "Using an unsupported operation must throw."); -}); - -add_task(function* test_keyed_keys_length() { - Telemetry.clearScalars(); - - const LONG_KEY_STRING = - "browser.qaxfiuosnzmhlg.rpvxicawolhtvmbkpnludhedobxvkjwqyeyvmv.somemoresowereach70chars"; - const NORMAL_KEY = "a_key"; - - // Set the value for a key within the length limits. - Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, NORMAL_KEY, 1); - - // Now try to set and modify the value for a very long key. - Assert.throws(() => Telemetry.keyedScalarAdd(KEYED_UINT_SCALAR, LONG_KEY_STRING, 10), - /NS_ERROR_ILLEGAL_VALUE/, - "Using keys longer than 70 characters must throw."); - Assert.throws(() => Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, LONG_KEY_STRING, 1), - /NS_ERROR_ILLEGAL_VALUE/, - "Using keys longer than 70 characters must throw."); - Assert.throws(() => Telemetry.keyedScalarSetMaximum(KEYED_UINT_SCALAR, LONG_KEY_STRING, 10), - /NS_ERROR_ILLEGAL_VALUE/, - "Using keys longer than 70 characters must throw."); - - // Make sure the key with the right length contains the expected value. - let keyedScalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - Assert.equal(Object.keys(keyedScalars[KEYED_UINT_SCALAR]).length, 1, - "The keyed scalar must contain exactly 1 key."); - Assert.ok(NORMAL_KEY in keyedScalars[KEYED_UINT_SCALAR], - "The keyed scalar must contain the expected key."); - Assert.equal(keyedScalars[KEYED_UINT_SCALAR][NORMAL_KEY], 1, - "The key must contain the expected value."); - Assert.ok(!(LONG_KEY_STRING in keyedScalars[KEYED_UINT_SCALAR]), - "The data for the long key should not have been recorded."); -}); - -add_task(function* test_keyed_max_keys() { - Telemetry.clearScalars(); - - // Generate the names for the first 100 keys. - let keyNamesSet = new Set(); - for (let k = 0; k < 100; k++) { - keyNamesSet.add("key_" + k); - } - - // Add 100 keys to an histogram and set their initial value. - let valueToSet = 0; - keyNamesSet.forEach(keyName => { - Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, keyName, valueToSet++); - }); - - // Perform some operations on the 101th key. This should throw, as - // we're not allowed to have more than 100 keys. - const LAST_KEY_NAME = "overflowing_key"; - Assert.throws(() => Telemetry.keyedScalarAdd(KEYED_UINT_SCALAR, LAST_KEY_NAME, 10), - /NS_ERROR_FAILURE/, - "Using more than 100 keys must throw."); - Assert.throws(() => Telemetry.keyedScalarSet(KEYED_UINT_SCALAR, LAST_KEY_NAME, 1), - /NS_ERROR_FAILURE/, - "Using more than 100 keys must throw."); - Assert.throws(() => Telemetry.keyedScalarSetMaximum(KEYED_UINT_SCALAR, LAST_KEY_NAME, 10), - /NS_ERROR_FAILURE/, - "Using more than 100 keys must throw."); - - // Make sure all the keys except the last one are available and have the correct - // values. - let keyedScalars = - Telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - - // Check that the keyed scalar only contain the first 100 keys. - const reportedKeysSet = new Set(Object.keys(keyedScalars[KEYED_UINT_SCALAR])); - Assert.ok([...keyNamesSet].filter(x => reportedKeysSet.has(x)) && - [...reportedKeysSet].filter(x => keyNamesSet.has(x)), - "The keyed scalar must contain all the 100 keys, and drop the others."); - - // Check that all the keys recorded the expected values. - let expectedValue = 0; - keyNamesSet.forEach(keyName => { - Assert.equal(keyedScalars[KEYED_UINT_SCALAR][keyName], expectedValue++, - "The key must contain the expected value."); - }); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js b/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js deleted file mode 100644 index 88ff8cf44..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js +++ /dev/null @@ -1,427 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ - -// This tests the public Telemetry API for submitting pings. - -"use strict"; - -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetrySession.jsm", this); -Cu.import("resource://gre/modules/TelemetrySend.jsm", this); -Cu.import("resource://gre/modules/TelemetryUtils.jsm", this); -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://gre/modules/Preferences.jsm", this); -Cu.import("resource://gre/modules/osfile.jsm", this); - -const PREF_TELEMETRY_SERVER = "toolkit.telemetry.server"; - -const MS_IN_A_MINUTE = 60 * 1000; - -function countPingTypes(pings) { - let countByType = new Map(); - for (let p of pings) { - countByType.set(p.type, 1 + (countByType.get(p.type) || 0)); - } - return countByType; -} - -function setPingLastModified(id, timestamp) { - const path = OS.Path.join(TelemetryStorage.pingDirectoryPath, id); - return OS.File.setDates(path, null, timestamp); -} - -// Mock out the send timer activity. -function waitForTimer() { - return new Promise(resolve => { - fakePingSendTimer((callback, timeout) => { - resolve([callback, timeout]); - }, () => {}); - }); -} - -// Allow easy faking of readable ping ids. -// This helps with debugging issues with e.g. ordering in the send logic. -function fakePingId(type, number) { - const HEAD = "93bd0011-2c8f-4e1c-bee0-"; - const TAIL = "000000000000"; - const N = String(number); - const id = HEAD + type + TAIL.slice(type.length, - N.length) + N; - fakeGeneratePingId(() => id); - return id; -} - -var checkPingsSaved = Task.async(function* (pingIds) { - let allFound = true; - for (let id of pingIds) { - const path = OS.Path.join(TelemetryStorage.pingDirectoryPath, id); - let exists = false; - try { - exists = yield OS.File.exists(path); - } catch (ex) {} - - if (!exists) { - dump("checkPingsSaved - failed to find ping: " + path + "\n"); - allFound = false; - } - } - - return allFound; -}); - -function histogramValueCount(h) { - return h.counts.reduce((a, b) => a + b); -} - -add_task(function* test_setup() { - // Trigger a proper telemetry init. - do_get_profile(true); - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); -}); - -// Test the ping sending logic. -add_task(function* test_sendPendingPings() { - const TYPE_PREFIX = "test-sendPendingPings-"; - const TEST_TYPE_A = TYPE_PREFIX + "A"; - const TEST_TYPE_B = TYPE_PREFIX + "B"; - - const TYPE_A_COUNT = 20; - const TYPE_B_COUNT = 5; - - let histSuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); - let histSendTimeSuccess = Telemetry.getHistogramById("TELEMETRY_SEND_SUCCESS"); - let histSendTimeFail = Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE"); - histSuccess.clear(); - histSendTimeSuccess.clear(); - histSendTimeFail.clear(); - - // Fake a current date. - let now = TelemetryUtils.truncateToDays(new Date()); - now = fakeNow(futureDate(now, 10 * 60 * MS_IN_A_MINUTE)); - - // Enable test-mode for TelemetrySend, otherwise we won't store pending pings - // before the module is fully initialized later. - TelemetrySend.setTestModeEnabled(true); - - // Submit some pings without the server and telemetry started yet. - for (let i = 0; i < TYPE_A_COUNT; ++i) { - fakePingId("a", i); - const id = yield TelemetryController.submitExternalPing(TEST_TYPE_A, {}); - yield setPingLastModified(id, now.getTime() + (i * 1000)); - } - - Assert.equal(TelemetrySend.pendingPingCount, TYPE_A_COUNT, - "Should have correct pending ping count"); - - // Submit some more pings of a different type. - now = fakeNow(futureDate(now, 5 * MS_IN_A_MINUTE)); - for (let i = 0; i < TYPE_B_COUNT; ++i) { - fakePingId("b", i); - const id = yield TelemetryController.submitExternalPing(TEST_TYPE_B, {}); - yield setPingLastModified(id, now.getTime() + (i * 1000)); - } - - Assert.equal(TelemetrySend.pendingPingCount, TYPE_A_COUNT + TYPE_B_COUNT, - "Should have correct pending ping count"); - - Assert.deepEqual(histSuccess.snapshot().counts, [0, 0, 0], - "Should not have recorded any sending in histograms yet."); - Assert.equal(histSendTimeSuccess.snapshot().sum, 0, - "Should not have recorded any sending in histograms yet."); - Assert.equal(histSendTimeFail.snapshot().sum, 0, - "Should not have recorded any sending in histograms yet."); - - // Now enable sending to the ping server. - now = fakeNow(futureDate(now, MS_IN_A_MINUTE)); - PingServer.start(); - Preferences.set(PREF_TELEMETRY_SERVER, "http://localhost:" + PingServer.port); - - let timerPromise = waitForTimer(); - yield TelemetryController.testReset(); - let [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - Assert.ok(!!pingSendTimerCallback, "Should have a timer callback"); - - // We should have received 10 pings from the first send batch: - // 5 of type B and 5 of type A, as sending is newest-first. - // The other pings should be delayed by the 10-pings-per-minute limit. - let pings = yield PingServer.promiseNextPings(10); - Assert.equal(TelemetrySend.pendingPingCount, TYPE_A_COUNT - 5, - "Should have correct pending ping count"); - PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now")); - let countByType = countPingTypes(pings); - - Assert.equal(countByType.get(TEST_TYPE_B), TYPE_B_COUNT, - "Should have received the correct amount of type B pings"); - Assert.equal(countByType.get(TEST_TYPE_A), 10 - TYPE_B_COUNT, - "Should have received the correct amount of type A pings"); - - Assert.deepEqual(histSuccess.snapshot().counts, [0, 10, 0], - "Should have recorded sending success in histograms."); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 10, - "Should have recorded successful send times in histograms."); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0, - "Should not have recorded any failed sending in histograms yet."); - - // As we hit the ping send limit and still have pending pings, a send tick should - // be scheduled in a minute. - Assert.ok(!!pingSendTimerCallback, "Timer callback should be set"); - Assert.equal(pingSendTimeout, MS_IN_A_MINUTE, "Send tick timeout should be correct"); - - // Trigger the next tick - we should receive the next 10 type A pings. - PingServer.resetPingHandler(); - now = fakeNow(futureDate(now, pingSendTimeout)); - timerPromise = waitForTimer(); - pingSendTimerCallback(); - [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - - pings = yield PingServer.promiseNextPings(10); - PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now")); - countByType = countPingTypes(pings); - - Assert.equal(countByType.get(TEST_TYPE_A), 10, "Should have received the correct amount of type A pings"); - - // We hit the ping send limit again and still have pending pings, a send tick should - // be scheduled in a minute. - Assert.equal(pingSendTimeout, MS_IN_A_MINUTE, "Send tick timeout should be correct"); - - // Trigger the next tick - we should receive the remaining type A pings. - PingServer.resetPingHandler(); - now = fakeNow(futureDate(now, pingSendTimeout)); - yield pingSendTimerCallback(); - - pings = yield PingServer.promiseNextPings(5); - PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now")); - countByType = countPingTypes(pings); - - Assert.equal(countByType.get(TEST_TYPE_A), 5, "Should have received the correct amount of type A pings"); - - yield TelemetrySend.testWaitOnOutgoingPings(); - PingServer.resetPingHandler(); -}); - -add_task(function* test_sendDateHeader() { - fakeNow(new Date(Date.UTC(2011, 1, 1, 11, 0, 0))); - yield TelemetrySend.reset(); - - let pingId = yield TelemetryController.submitExternalPing("test-send-date-header", {}); - let req = yield PingServer.promiseNextRequest(); - let ping = decodeRequestPayload(req); - Assert.equal(req.getHeader("Date"), "Tue, 01 Feb 2011 11:00:00 GMT", - "Telemetry should send the correct Date header with requests."); - Assert.equal(ping.id, pingId, "Should have received the correct ping id."); -}); - -// Test the backoff timeout behavior after send failures. -add_task(function* test_backoffTimeout() { - const TYPE_PREFIX = "test-backoffTimeout-"; - const TEST_TYPE_C = TYPE_PREFIX + "C"; - const TEST_TYPE_D = TYPE_PREFIX + "D"; - const TEST_TYPE_E = TYPE_PREFIX + "E"; - - let histSuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); - let histSendTimeSuccess = Telemetry.getHistogramById("TELEMETRY_SEND_SUCCESS"); - let histSendTimeFail = Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE"); - - // Failing a ping send now should trigger backoff behavior. - let now = fakeNow(2010, 1, 1, 11, 0, 0); - yield TelemetrySend.reset(); - PingServer.stop(); - - histSuccess.clear(); - histSendTimeSuccess.clear(); - histSendTimeFail.clear(); - - fakePingId("c", 0); - now = fakeNow(futureDate(now, MS_IN_A_MINUTE)); - let sendAttempts = 0; - let timerPromise = waitForTimer(); - yield TelemetryController.submitExternalPing(TEST_TYPE_C, {}); - let [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - Assert.equal(TelemetrySend.pendingPingCount, 1, "Should have one pending ping."); - ++sendAttempts; - - const MAX_BACKOFF_TIMEOUT = 120 * MS_IN_A_MINUTE; - for (let timeout = 2 * MS_IN_A_MINUTE; timeout <= MAX_BACKOFF_TIMEOUT; timeout *= 2) { - Assert.ok(!!pingSendTimerCallback, "Should have received a timer callback"); - Assert.equal(pingSendTimeout, timeout, "Send tick timeout should be correct"); - - let callback = pingSendTimerCallback; - now = fakeNow(futureDate(now, pingSendTimeout)); - timerPromise = waitForTimer(); - yield callback(); - [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - ++sendAttempts; - } - - timerPromise = waitForTimer(); - yield pingSendTimerCallback(); - [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - Assert.equal(pingSendTimeout, MAX_BACKOFF_TIMEOUT, "Tick timeout should be capped"); - ++sendAttempts; - - Assert.deepEqual(histSuccess.snapshot().counts, [sendAttempts, 0, 0], - "Should have recorded sending failure in histograms."); - Assert.equal(histSendTimeSuccess.snapshot().sum, 0, - "Should not have recorded any sending success in histograms yet."); - Assert.greater(histSendTimeFail.snapshot().sum, 0, - "Should have recorded send failure times in histograms."); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), sendAttempts, - "Should have recorded send failure times in histograms."); - - // Submitting a new ping should reset the backoff behavior. - fakePingId("d", 0); - now = fakeNow(futureDate(now, MS_IN_A_MINUTE)); - timerPromise = waitForTimer(); - yield TelemetryController.submitExternalPing(TEST_TYPE_D, {}); - [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - Assert.equal(pingSendTimeout, 2 * MS_IN_A_MINUTE, "Send tick timeout should be correct"); - sendAttempts += 2; - - // With the server running again, we should send out the pending pings immediately - // when a new ping is submitted. - PingServer.start(); - TelemetrySend.setServer("http://localhost:" + PingServer.port); - fakePingId("e", 0); - now = fakeNow(futureDate(now, MS_IN_A_MINUTE)); - timerPromise = waitForTimer(); - yield TelemetryController.submitExternalPing(TEST_TYPE_E, {}); - - let pings = yield PingServer.promiseNextPings(3); - let countByType = countPingTypes(pings); - - Assert.equal(countByType.get(TEST_TYPE_C), 1, "Should have received the correct amount of type C pings"); - Assert.equal(countByType.get(TEST_TYPE_D), 1, "Should have received the correct amount of type D pings"); - Assert.equal(countByType.get(TEST_TYPE_E), 1, "Should have received the correct amount of type E pings"); - - yield TelemetrySend.testWaitOnOutgoingPings(); - Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings left"); - - Assert.deepEqual(histSuccess.snapshot().counts, [sendAttempts, 3, 0], - "Should have recorded sending failure in histograms."); - Assert.greater(histSendTimeSuccess.snapshot().sum, 0, - "Should have recorded sending success in histograms."); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 3, - "Should have recorded sending success in histograms."); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), sendAttempts, - "Should have recorded send failure times in histograms."); -}); - -add_task(function* test_discardBigPings() { - const TEST_PING_TYPE = "test-ping-type"; - - let histSizeExceeded = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_SEND"); - let histDiscardedSize = Telemetry.getHistogramById("TELEMETRY_DISCARDED_SEND_PINGS_SIZE_MB"); - let histSuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); - let histSendTimeSuccess = Telemetry.getHistogramById("TELEMETRY_SEND_SUCCESS"); - let histSendTimeFail = Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE"); - for (let h of [histSizeExceeded, histDiscardedSize, histSuccess, histSendTimeSuccess, histSendTimeFail]) { - h.clear(); - } - - // Generate a 2MB string and create an oversized payload. - const OVERSIZED_PAYLOAD = {"data": generateRandomString(2 * 1024 * 1024)}; - - // Reset the histograms. - Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_SEND").clear(); - Telemetry.getHistogramById("TELEMETRY_DISCARDED_SEND_PINGS_SIZE_MB").clear(); - - // Submit a ping of a normal size and check that we don't count it in the histogram. - yield TelemetryController.submitExternalPing(TEST_PING_TYPE, { test: "test" }); - yield TelemetrySend.testWaitOnOutgoingPings(); - - Assert.equal(histSizeExceeded.snapshot().sum, 0, "Telemetry must report no oversized ping submitted."); - Assert.equal(histDiscardedSize.snapshot().sum, 0, "Telemetry must report no oversized pings."); - Assert.deepEqual(histSuccess.snapshot().counts, [0, 1, 0], "Should have recorded sending success."); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 1, "Should have recorded send success time."); - Assert.greater(histSendTimeSuccess.snapshot().sum, 0, "Should have recorded send success time."); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0, "Should not have recorded send failure time."); - - // Submit an oversized ping and check that it gets discarded. - yield TelemetryController.submitExternalPing(TEST_PING_TYPE, OVERSIZED_PAYLOAD); - yield TelemetrySend.testWaitOnOutgoingPings(); - - Assert.equal(histSizeExceeded.snapshot().sum, 1, "Telemetry must report 1 oversized ping submitted."); - Assert.equal(histDiscardedSize.snapshot().counts[2], 1, "Telemetry must report a 2MB, oversized, ping submitted."); - Assert.deepEqual(histSuccess.snapshot().counts, [0, 1, 0], "Should have recorded sending success."); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 1, "Should have recorded send success time."); - Assert.greater(histSendTimeSuccess.snapshot().sum, 0, "Should have recorded send success time."); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0, "Should not have recorded send failure time."); -}); - -add_task(function* test_evictedOnServerErrors() { - const TEST_TYPE = "test-evicted"; - - yield TelemetrySend.reset(); - - let histEvicted = Telemetry.getHistogramById("TELEMETRY_PING_EVICTED_FOR_SERVER_ERRORS"); - let histSuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); - let histSendTimeSuccess = Telemetry.getHistogramById("TELEMETRY_SEND_SUCCESS"); - let histSendTimeFail = Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE"); - for (let h of [histEvicted, histSuccess, histSendTimeSuccess, histSendTimeFail]) { - h.clear(); - } - - // Write a custom ping handler which will return 403. This will trigger ping eviction - // on client side. - PingServer.registerPingHandler((req, res) => { - res.setStatusLine(null, 403, "Forbidden"); - res.processAsync(); - res.finish(); - }); - - // Clear the histogram and submit a ping. - let pingId = yield TelemetryController.submitExternalPing(TEST_TYPE, {}); - yield TelemetrySend.testWaitOnOutgoingPings(); - - Assert.equal(histEvicted.snapshot().sum, 1, - "Telemetry must report a ping evicted due to server errors"); - Assert.deepEqual(histSuccess.snapshot().counts, [0, 1, 0]); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 1); - Assert.greater(histSendTimeSuccess.snapshot().sum, 0); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0); - - // The ping should not be persisted. - yield Assert.rejects(TelemetryStorage.loadPendingPing(pingId), "The ping must not be persisted."); - - // Reset the ping handler and submit a new ping. - PingServer.resetPingHandler(); - pingId = yield TelemetryController.submitExternalPing(TEST_TYPE, {}); - - let ping = yield PingServer.promiseNextPings(1); - Assert.equal(ping[0].id, pingId, "The correct ping must be received"); - - // We should not have updated the error histogram. - yield TelemetrySend.testWaitOnOutgoingPings(); - Assert.equal(histEvicted.snapshot().sum, 1, "Telemetry must report only one ping evicted due to server errors"); - Assert.deepEqual(histSuccess.snapshot().counts, [0, 2, 0]); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 2); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0); -}); - -// Test that the current, non-persisted pending pings are properly saved on shutdown. -add_task(function* test_persistCurrentPingsOnShutdown() { - const TEST_TYPE = "test-persistCurrentPingsOnShutdown"; - const PING_COUNT = 5; - yield TelemetrySend.reset(); - PingServer.stop(); - Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings yet"); - - // Submit new pings that shouldn't be persisted yet. - let ids = []; - for (let i=0; i<5; ++i) { - ids.push(fakePingId("f", i)); - TelemetryController.submitExternalPing(TEST_TYPE, {}); - } - - Assert.equal(TelemetrySend.pendingPingCount, PING_COUNT, "Should have the correct pending ping count"); - - // Triggering a shutdown should persist the pings. - yield TelemetrySend.shutdown(); - Assert.ok((yield checkPingsSaved(ids)), "All pending pings should have been persisted"); - - // After a restart the pings should have been found when scanning. - yield TelemetrySend.reset(); - Assert.equal(TelemetrySend.pendingPingCount, PING_COUNT, "Should have the correct pending ping count"); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js deleted file mode 100644 index 221b6bcab..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js +++ /dev/null @@ -1,547 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ - -/** - * This test case populates the profile with some fake stored - * pings, and checks that pending pings are immediatlely sent - * after delayed init. - */ - -"use strict" - -Cu.import("resource://gre/modules/osfile.jsm", this); -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://gre/modules/Promise.jsm", this); -Cu.import("resource://gre/modules/TelemetryStorage.jsm", this); -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetrySend.jsm", this); -Cu.import("resource://gre/modules/Task.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -var {OS: {File, Path, Constants}} = Cu.import("resource://gre/modules/osfile.jsm", {}); - -// We increment TelemetryStorage's MAX_PING_FILE_AGE and -// OVERDUE_PING_FILE_AGE by 1 minute so that our test pings exceed -// those points in time, even taking into account file system imprecision. -const ONE_MINUTE_MS = 60 * 1000; -const OVERDUE_PING_FILE_AGE = TelemetrySend.OVERDUE_PING_FILE_AGE + ONE_MINUTE_MS; - -const PING_SAVE_FOLDER = "saved-telemetry-pings"; -const PING_TIMEOUT_LENGTH = 5000; -const OVERDUE_PINGS = 6; -const OLD_FORMAT_PINGS = 4; -const RECENT_PINGS = 4; - -const TOTAL_EXPECTED_PINGS = OVERDUE_PINGS + RECENT_PINGS + OLD_FORMAT_PINGS; - -const PREF_FHR_UPLOAD = "datareporting.healthreport.uploadEnabled"; - -var gCreatedPings = 0; -var gSeenPings = 0; - -/** - * Creates some Telemetry pings for the and saves them to disk. Each ping gets a - * unique ID based on an incrementor. - * - * @param {Array} aPingInfos An array of ping type objects. Each entry must be an - * object containing a "num" field for the number of pings to create and - * an "age" field. The latter representing the age in milliseconds to offset - * from now. A value of 10 would make the ping 10ms older than now, for - * example. - * @returns Promise - * @resolve an Array with the created pings ids. - */ -var createSavedPings = Task.async(function* (aPingInfos) { - let pingIds = []; - let now = Date.now(); - - for (let type in aPingInfos) { - let num = aPingInfos[type].num; - let age = now - (aPingInfos[type].age || 0); - for (let i = 0; i < num; ++i) { - let pingId = yield TelemetryController.addPendingPing("test-ping", {}, { overwrite: true }); - if (aPingInfos[type].age) { - // savePing writes to the file synchronously, so we're good to - // modify the lastModifedTime now. - let filePath = getSavePathForPingId(pingId); - yield File.setDates(filePath, null, age); - } - gCreatedPings++; - pingIds.push(pingId); - } - } - - return pingIds; -}); - -/** - * Deletes locally saved pings if they exist. - * - * @param aPingIds an Array of ping ids to delete. - * @returns Promise - */ -var clearPings = Task.async(function* (aPingIds) { - for (let pingId of aPingIds) { - yield TelemetryStorage.removePendingPing(pingId); - } -}); - -/** - * Fakes the pending pings storage quota. - * @param {Integer} aPendingQuota The new quota, in bytes. - */ -function fakePendingPingsQuota(aPendingQuota) { - let storage = Cu.import("resource://gre/modules/TelemetryStorage.jsm"); - storage.Policy.getPendingPingsQuota = () => aPendingQuota; -} - -/** - * Returns a handle for the file that a ping should be - * stored in locally. - * - * @returns path - */ -function getSavePathForPingId(aPingId) { - return Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, aPingId); -} - -/** - * Check if the number of Telemetry pings received by the HttpServer is not equal - * to aExpectedNum. - * - * @param aExpectedNum the number of pings we expect to receive. - */ -function assertReceivedPings(aExpectedNum) { - do_check_eq(gSeenPings, aExpectedNum); -} - -/** - * Throws if any pings with the id in aPingIds is saved locally. - * - * @param aPingIds an Array of pings ids to check. - * @returns Promise - */ -var assertNotSaved = Task.async(function* (aPingIds) { - let saved = 0; - for (let id of aPingIds) { - let filePath = getSavePathForPingId(id); - if (yield File.exists(filePath)) { - saved++; - } - } - if (saved > 0) { - do_throw("Found " + saved + " unexpected saved pings."); - } -}); - -/** - * Our handler function for the HttpServer that simply - * increments the gSeenPings global when it successfully - * receives and decodes a Telemetry payload. - * - * @param aRequest the HTTP request sent from HttpServer. - */ -function pingHandler(aRequest) { - gSeenPings++; -} - -add_task(function* test_setup() { - PingServer.start(); - PingServer.registerPingHandler(pingHandler); - do_get_profile(); - loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - - Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); - Services.prefs.setCharPref(TelemetryController.Constants.PREF_SERVER, - "http://localhost:" + PingServer.port); -}); - -/** - * Setup the tests by making sure the ping storage directory is available, otherwise - * |TelemetryController.testSaveDirectoryToFile| could fail. - */ -add_task(function* setupEnvironment() { - // The following tests assume this pref to be true by default. - Services.prefs.setBoolPref(PREF_FHR_UPLOAD, true); - - yield TelemetryController.testSetup(); - - let directory = TelemetryStorage.pingDirectoryPath; - yield File.makeDir(directory, { ignoreExisting: true, unixMode: OS.Constants.S_IRWXU }); - - yield TelemetryStorage.testClearPendingPings(); -}); - -/** - * Test that really recent pings are sent on Telemetry initialization. - */ -add_task(function* test_recent_pings_sent() { - let pingTypes = [{ num: RECENT_PINGS }]; - yield createSavedPings(pingTypes); - - yield TelemetryController.testReset(); - yield TelemetrySend.testWaitOnOutgoingPings(); - assertReceivedPings(RECENT_PINGS); - - yield TelemetryStorage.testClearPendingPings(); -}); - -/** - * Create an overdue ping in the old format and try to send it. - */ -add_task(function* test_overdue_old_format() { - // A test ping in the old, standard format. - const PING_OLD_FORMAT = { - slug: "1234567abcd", - reason: "test-ping", - payload: { - info: { - reason: "test-ping", - OS: "XPCShell", - appID: "SomeId", - appVersion: "1.0", - appName: "XPCShell", - appBuildID: "123456789", - appUpdateChannel: "Test", - platformBuildID: "987654321", - }, - }, - }; - - // A ping with no info section, but with a slug. - const PING_NO_INFO = { - slug: "1234-no-info-ping", - reason: "test-ping", - payload: {} - }; - - // A ping with no payload. - const PING_NO_PAYLOAD = { - slug: "5678-no-payload", - reason: "test-ping", - }; - - // A ping with no info and no slug. - const PING_NO_SLUG = { - reason: "test-ping", - payload: {} - }; - - const PING_FILES_PATHS = [ - getSavePathForPingId(PING_OLD_FORMAT.slug), - getSavePathForPingId(PING_NO_INFO.slug), - getSavePathForPingId(PING_NO_PAYLOAD.slug), - getSavePathForPingId("no-slug-file"), - ]; - - // Write the ping to file and make it overdue. - yield TelemetryStorage.savePing(PING_OLD_FORMAT, true); - yield TelemetryStorage.savePing(PING_NO_INFO, true); - yield TelemetryStorage.savePing(PING_NO_PAYLOAD, true); - yield TelemetryStorage.savePingToFile(PING_NO_SLUG, PING_FILES_PATHS[3], true); - - for (let f in PING_FILES_PATHS) { - yield File.setDates(PING_FILES_PATHS[f], null, Date.now() - OVERDUE_PING_FILE_AGE); - } - - gSeenPings = 0; - yield TelemetryController.testReset(); - yield TelemetrySend.testWaitOnOutgoingPings(); - assertReceivedPings(OLD_FORMAT_PINGS); - - // |TelemetryStorage.cleanup| doesn't know how to remove a ping with no slug or id, - // so remove it manually so that the next test doesn't fail. - yield OS.File.remove(PING_FILES_PATHS[3]); - - yield TelemetryStorage.testClearPendingPings(); -}); - -add_task(function* test_corrupted_pending_pings() { - const TEST_TYPE = "test_corrupted"; - - Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_READ").clear(); - Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_PARSE").clear(); - - // Save a pending ping and get its id. - let pendingPingId = yield TelemetryController.addPendingPing(TEST_TYPE, {}, {}); - - // Try to load it: there should be no error. - yield TelemetryStorage.loadPendingPing(pendingPingId); - - let h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_READ").snapshot(); - Assert.equal(h.sum, 0, "Telemetry must not report a pending ping load failure"); - h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_PARSE").snapshot(); - Assert.equal(h.sum, 0, "Telemetry must not report a pending ping parse failure"); - - // Delete it from the disk, so that its id will be kept in the cache but it will - // fail loading the file. - yield OS.File.remove(getSavePathForPingId(pendingPingId)); - - // Try to load a pending ping which isn't there anymore. - yield Assert.rejects(TelemetryStorage.loadPendingPing(pendingPingId), - "Telemetry must fail loading a ping which isn't there"); - - h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_READ").snapshot(); - Assert.equal(h.sum, 1, "Telemetry must report a pending ping load failure"); - h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_PARSE").snapshot(); - Assert.equal(h.sum, 0, "Telemetry must not report a pending ping parse failure"); - - // Save a new ping, so that it gets in the pending pings cache. - pendingPingId = yield TelemetryController.addPendingPing(TEST_TYPE, {}, {}); - // Overwrite it with a corrupted JSON file and then try to load it. - const INVALID_JSON = "{ invalid,JSON { {1}"; - yield OS.File.writeAtomic(getSavePathForPingId(pendingPingId), INVALID_JSON, { encoding: "utf-8" }); - - // Try to load the ping with the corrupted JSON content. - yield Assert.rejects(TelemetryStorage.loadPendingPing(pendingPingId), - "Telemetry must fail loading a corrupted ping"); - - h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_READ").snapshot(); - Assert.equal(h.sum, 1, "Telemetry must report a pending ping load failure"); - h = Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_PARSE").snapshot(); - Assert.equal(h.sum, 1, "Telemetry must report a pending ping parse failure"); - - let exists = yield OS.File.exists(getSavePathForPingId(pendingPingId)); - Assert.ok(!exists, "The unparseable ping should have been removed"); - - yield TelemetryStorage.testClearPendingPings(); -}); - -/** - * Create some recent and overdue pings and verify that they get sent. - */ -add_task(function* test_overdue_pings_trigger_send() { - let pingTypes = [ - { num: RECENT_PINGS }, - { num: OVERDUE_PINGS, age: OVERDUE_PING_FILE_AGE }, - ]; - let pings = yield createSavedPings(pingTypes); - let recentPings = pings.slice(0, RECENT_PINGS); - let overduePings = pings.slice(-OVERDUE_PINGS); - - yield TelemetryController.testReset(); - yield TelemetrySend.testWaitOnOutgoingPings(); - assertReceivedPings(TOTAL_EXPECTED_PINGS); - - yield assertNotSaved(recentPings); - yield assertNotSaved(overduePings); - - Assert.equal(TelemetrySend.overduePingsCount, overduePings.length, - "Should have tracked the correct amount of overdue pings"); - - yield TelemetryStorage.testClearPendingPings(); -}); - -/** - * Create a ping in the old format, send it, and make sure the request URL contains - * the correct version query parameter. - */ -add_task(function* test_overdue_old_format() { - // A test ping in the old, standard format. - const PING_OLD_FORMAT = { - slug: "1234567abcd", - reason: "test-ping", - payload: { - info: { - reason: "test-ping", - OS: "XPCShell", - appID: "SomeId", - appVersion: "1.0", - appName: "XPCShell", - appBuildID: "123456789", - appUpdateChannel: "Test", - platformBuildID: "987654321", - }, - }, - }; - - const filePath = - Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, PING_OLD_FORMAT.slug); - - // Write the ping to file and make it overdue. - yield TelemetryStorage.savePing(PING_OLD_FORMAT, true); - yield File.setDates(filePath, null, Date.now() - OVERDUE_PING_FILE_AGE); - - let receivedPings = 0; - // Register a new prefix handler to validate the URL. - PingServer.registerPingHandler(request => { - // Check that we have a version query parameter in the URL. - Assert.notEqual(request.queryString, ""); - - // Make sure the version in the query string matches the old ping format version. - let params = request.queryString.split("&"); - Assert.ok(params.find(p => p == "v=1")); - - receivedPings++; - }); - - yield TelemetryController.testReset(); - yield TelemetrySend.testWaitOnOutgoingPings(); - Assert.equal(receivedPings, 1, "We must receive a ping in the old format."); - - yield TelemetryStorage.testClearPendingPings(); - PingServer.resetPingHandler(); -}); - -add_task(function* test_pendingPingsQuota() { - const PING_TYPE = "foo"; - - // Disable upload so pings don't get sent and removed from the pending pings directory. - Services.prefs.setBoolPref(PREF_FHR_UPLOAD, false); - - // Remove all the pending pings then startup and wait for the cleanup task to complete. - // There should be nothing to remove. - yield TelemetryStorage.testClearPendingPings(); - yield TelemetryController.testReset(); - yield TelemetrySend.testWaitOnOutgoingPings(); - yield TelemetryStorage.testPendingQuotaTaskPromise(); - - // Remove the pending deletion ping generated when flipping FHR upload off. - yield TelemetryStorage.testClearPendingPings(); - - let expectedPrunedPings = []; - let expectedNotPrunedPings = []; - - let checkPendingPings = Task.async(function*() { - // Check that the pruned pings are not on disk anymore. - for (let prunedPingId of expectedPrunedPings) { - yield Assert.rejects(TelemetryStorage.loadPendingPing(prunedPingId), - "Ping " + prunedPingId + " should have been pruned."); - const pingPath = getSavePathForPingId(prunedPingId); - Assert.ok(!(yield OS.File.exists(pingPath)), "The ping should not be on the disk anymore."); - } - - // Check that the expected pings are there. - for (let expectedPingId of expectedNotPrunedPings) { - Assert.ok((yield TelemetryStorage.loadPendingPing(expectedPingId)), - "Ping" + expectedPingId + " should be among the pending pings."); - } - }); - - let pendingPingsInfo = []; - let pingsSizeInBytes = 0; - - // Create 10 pings to test the pending pings quota. - for (let days = 1; days < 11; days++) { - const date = fakeNow(2010, 1, days, 1, 1, 0); - const pingId = yield TelemetryController.addPendingPing(PING_TYPE, {}, {}); - - // Find the size of the ping. - const pingFilePath = getSavePathForPingId(pingId); - const pingSize = (yield OS.File.stat(pingFilePath)).size; - // Add the info at the beginning of the array, so that most recent pings come first. - pendingPingsInfo.unshift({id: pingId, size: pingSize, timestamp: date.getTime() }); - - // Set the last modification date. - yield OS.File.setDates(pingFilePath, null, date.getTime()); - - // Add it to the pending ping directory size. - pingsSizeInBytes += pingSize; - } - - // We need to test the pending pings size before we hit the quota, otherwise a special - // value is recorded. - Telemetry.getHistogramById("TELEMETRY_PENDING_PINGS_SIZE_MB").clear(); - Telemetry.getHistogramById("TELEMETRY_PENDING_PINGS_EVICTED_OVER_QUOTA").clear(); - Telemetry.getHistogramById("TELEMETRY_PENDING_EVICTING_OVER_QUOTA_MS").clear(); - - yield TelemetryController.testReset(); - yield TelemetryStorage.testPendingQuotaTaskPromise(); - - // Check that the correct values for quota probes are reported when no quota is hit. - let h = Telemetry.getHistogramById("TELEMETRY_PENDING_PINGS_SIZE_MB").snapshot(); - Assert.equal(h.sum, Math.round(pingsSizeInBytes / 1024 / 1024), - "Telemetry must report the correct pending pings directory size."); - h = Telemetry.getHistogramById("TELEMETRY_PENDING_PINGS_EVICTED_OVER_QUOTA").snapshot(); - Assert.equal(h.sum, 0, "Telemetry must report 0 evictions if quota is not hit."); - h = Telemetry.getHistogramById("TELEMETRY_PENDING_EVICTING_OVER_QUOTA_MS").snapshot(); - Assert.equal(h.sum, 0, "Telemetry must report a null elapsed time if quota is not hit."); - - // Set the quota to 80% of the space. - const testQuotaInBytes = pingsSizeInBytes * 0.8; - fakePendingPingsQuota(testQuotaInBytes); - - // The storage prunes pending pings until we reach 90% of the requested storage quota. - // Based on that, find how many pings should be kept. - const safeQuotaSize = Math.round(testQuotaInBytes * 0.9); - let sizeInBytes = 0; - let pingsWithinQuota = []; - let pingsOutsideQuota = []; - - for (let pingInfo of pendingPingsInfo) { - sizeInBytes += pingInfo.size; - if (sizeInBytes >= safeQuotaSize) { - pingsOutsideQuota.push(pingInfo.id); - continue; - } - pingsWithinQuota.push(pingInfo.id); - } - - expectedNotPrunedPings = pingsWithinQuota; - expectedPrunedPings = pingsOutsideQuota; - - // Reset TelemetryController to start the pending pings cleanup. - yield TelemetryController.testReset(); - yield TelemetryStorage.testPendingQuotaTaskPromise(); - yield checkPendingPings(); - - h = Telemetry.getHistogramById("TELEMETRY_PENDING_PINGS_EVICTED_OVER_QUOTA").snapshot(); - Assert.equal(h.sum, pingsOutsideQuota.length, - "Telemetry must correctly report the over quota pings evicted from the pending pings directory."); - h = Telemetry.getHistogramById("TELEMETRY_PENDING_PINGS_SIZE_MB").snapshot(); - Assert.equal(h.sum, 17, "Pending pings quota was hit, a special size must be reported."); - - // Trigger a cleanup again and make sure we're not removing anything. - yield TelemetryController.testReset(); - yield TelemetryStorage.testPendingQuotaTaskPromise(); - yield checkPendingPings(); - - const OVERSIZED_PING_ID = "9b21ec8f-f762-4d28-a2c1-44e1c4694f24"; - // Create a pending oversized ping. - const OVERSIZED_PING = { - id: OVERSIZED_PING_ID, - type: PING_TYPE, - creationDate: (new Date()).toISOString(), - // Generate a 2MB string to use as the ping payload. - payload: generateRandomString(2 * 1024 * 1024), - }; - yield TelemetryStorage.savePendingPing(OVERSIZED_PING); - - // Reset the histograms. - Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").clear(); - Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB").clear(); - - // Try to manually load the oversized ping. - yield Assert.rejects(TelemetryStorage.loadPendingPing(OVERSIZED_PING_ID), - "The oversized ping should have been pruned."); - Assert.ok(!(yield OS.File.exists(getSavePathForPingId(OVERSIZED_PING_ID))), - "The ping should not be on the disk anymore."); - - // Make sure we're correctly updating the related histograms. - h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").snapshot(); - Assert.equal(h.sum, 1, "Telemetry must report 1 oversized ping in the pending pings directory."); - h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB").snapshot(); - Assert.equal(h.counts[2], 1, "Telemetry must report a 2MB, oversized, ping."); - - // Save the ping again to check if it gets pruned when scanning the pings directory. - yield TelemetryStorage.savePendingPing(OVERSIZED_PING); - expectedPrunedPings.push(OVERSIZED_PING_ID); - - // Scan the pending pings directory. - yield TelemetryController.testReset(); - yield TelemetryStorage.testPendingQuotaTaskPromise(); - yield checkPendingPings(); - - // Make sure we're correctly updating the related histograms. - h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").snapshot(); - Assert.equal(h.sum, 2, "Telemetry must report 1 oversized ping in the pending pings directory."); - h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB").snapshot(); - Assert.equal(h.counts[2], 2, "Telemetry must report two 2MB, oversized, pings."); - - Services.prefs.setBoolPref(PREF_FHR_UPLOAD, true); -}); - -add_task(function* teardown() { - yield PingServer.stop(); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js deleted file mode 100644 index 698133162..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js +++ /dev/null @@ -1,2029 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ -/* This testcase triggers two telemetry pings. - * - * Telemetry code keeps histograms of past telemetry pings. The first - * ping populates these histograms. One of those histograms is then - * checked in the second request. - */ - -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/ClientID.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/LightweightThemeManager.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetrySession.jsm", this); -Cu.import("resource://gre/modules/TelemetryStorage.jsm", this); -Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this); -Cu.import("resource://gre/modules/TelemetrySend.jsm", this); -Cu.import("resource://gre/modules/Task.jsm", this); -Cu.import("resource://gre/modules/Promise.jsm", this); -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/osfile.jsm", this); - -const PING_FORMAT_VERSION = 4; -const PING_TYPE_MAIN = "main"; -const PING_TYPE_SAVED_SESSION = "saved-session"; - -const REASON_ABORTED_SESSION = "aborted-session"; -const REASON_SAVED_SESSION = "saved-session"; -const REASON_SHUTDOWN = "shutdown"; -const REASON_TEST_PING = "test-ping"; -const REASON_DAILY = "daily"; -const REASON_ENVIRONMENT_CHANGE = "environment-change"; - -const PLATFORM_VERSION = "1.9.2"; -const APP_VERSION = "1"; -const APP_ID = "xpcshell@tests.mozilla.org"; -const APP_NAME = "XPCShell"; - -const IGNORE_HISTOGRAM_TO_CLONE = "MEMORY_HEAP_ALLOCATED"; -const IGNORE_CLONED_HISTOGRAM = "test::ignore_me_also"; -const ADDON_NAME = "Telemetry test addon"; -const ADDON_HISTOGRAM = "addon-histogram"; -// Add some unicode characters here to ensure that sending them works correctly. -const SHUTDOWN_TIME = 10000; -const FAILED_PROFILE_LOCK_ATTEMPTS = 2; - -// Constants from prio.h for nsIFileOutputStream.init -const PR_WRONLY = 0x2; -const PR_CREATE_FILE = 0x8; -const PR_TRUNCATE = 0x20; -const RW_OWNER = parseInt("0600", 8); - -const NUMBER_OF_THREADS_TO_LAUNCH = 30; -var gNumberOfThreadsLaunched = 0; - -const MS_IN_ONE_HOUR = 60 * 60 * 1000; -const MS_IN_ONE_DAY = 24 * MS_IN_ONE_HOUR; - -const PREF_BRANCH = "toolkit.telemetry."; -const PREF_SERVER = PREF_BRANCH + "server"; -const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; - -const DATAREPORTING_DIR = "datareporting"; -const ABORTED_PING_FILE_NAME = "aborted-session-ping"; -const ABORTED_SESSION_UPDATE_INTERVAL_MS = 5 * 60 * 1000; - -XPCOMUtils.defineLazyGetter(this, "DATAREPORTING_PATH", function() { - return OS.Path.join(OS.Constants.Path.profileDir, DATAREPORTING_DIR); -}); - -var gClientID = null; -var gMonotonicNow = 0; - -function generateUUID() { - let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString(); - // strip {} - return str.substring(1, str.length - 1); -} - -function truncateDateToDays(date) { - return new Date(date.getFullYear(), - date.getMonth(), - date.getDate(), - 0, 0, 0, 0); -} - -function sendPing() { - TelemetrySession.gatherStartup(); - if (PingServer.started) { - TelemetrySend.setServer("http://localhost:" + PingServer.port); - return TelemetrySession.testPing(); - } - TelemetrySend.setServer("http://doesnotexist"); - return TelemetrySession.testPing(); -} - -function fakeGenerateUUID(sessionFunc, subsessionFunc) { - let session = Cu.import("resource://gre/modules/TelemetrySession.jsm"); - session.Policy.generateSessionUUID = sessionFunc; - session.Policy.generateSubsessionUUID = subsessionFunc; -} - -function fakeIdleNotification(topic) { - let session = Cu.import("resource://gre/modules/TelemetrySession.jsm"); - return session.TelemetryScheduler.observe(null, topic, null); -} - -function setupTestData() { - - Services.startup.interrupted = true; - Telemetry.registerAddonHistogram(ADDON_NAME, ADDON_HISTOGRAM, - Telemetry.HISTOGRAM_LINEAR, - 1, 5, 6); - let h1 = Telemetry.getAddonHistogram(ADDON_NAME, ADDON_HISTOGRAM); - h1.add(1); - let h2 = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT"); - h2.add(); - - let k1 = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT"); - k1.add("a"); - k1.add("a"); - k1.add("b"); -} - -function getSavedPingFile(basename) { - let tmpDir = Services.dirsvc.get("ProfD", Ci.nsIFile); - let pingFile = tmpDir.clone(); - pingFile.append(basename); - if (pingFile.exists()) { - pingFile.remove(true); - } - do_register_cleanup(function () { - try { - pingFile.remove(true); - } catch (e) { - } - }); - return pingFile; -} - -function checkPingFormat(aPing, aType, aHasClientId, aHasEnvironment) { - const MANDATORY_PING_FIELDS = [ - "type", "id", "creationDate", "version", "application", "payload" - ]; - - const APPLICATION_TEST_DATA = { - buildId: gAppInfo.appBuildID, - name: APP_NAME, - version: APP_VERSION, - vendor: "Mozilla", - platformVersion: PLATFORM_VERSION, - xpcomAbi: "noarch-spidermonkey", - }; - - // Check that the ping contains all the mandatory fields. - for (let f of MANDATORY_PING_FIELDS) { - Assert.ok(f in aPing, f + "must be available."); - } - - Assert.equal(aPing.type, aType, "The ping must have the correct type."); - Assert.equal(aPing.version, PING_FORMAT_VERSION, "The ping must have the correct version."); - - // Test the application section. - for (let f in APPLICATION_TEST_DATA) { - Assert.equal(aPing.application[f], APPLICATION_TEST_DATA[f], - f + " must have the correct value."); - } - - // We can't check the values for channel and architecture. Just make - // sure they are in. - Assert.ok("architecture" in aPing.application, - "The application section must have an architecture field."); - Assert.ok("channel" in aPing.application, - "The application section must have a channel field."); - - // Check the clientId and environment fields, as needed. - Assert.equal("clientId" in aPing, aHasClientId); - Assert.equal("environment" in aPing, aHasEnvironment); -} - -function checkPayloadInfo(data) { - const ALLOWED_REASONS = [ - "environment-change", "shutdown", "daily", "saved-session", "test-ping" - ]; - let numberCheck = arg => { return (typeof arg == "number"); }; - let positiveNumberCheck = arg => { return numberCheck(arg) && (arg >= 0); }; - let stringCheck = arg => { return (typeof arg == "string") && (arg != ""); }; - let revisionCheck = arg => { - return (Services.appinfo.isOfficial) ? stringCheck(arg) : (typeof arg == "string"); - }; - let uuidCheck = arg => { - return UUID_REGEX.test(arg); - }; - let isoDateCheck = arg => { - // We expect use of this version of the ISO format: - // 2015-04-12T18:51:19.1+00:00 - const isoDateRegEx = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+[+-]\d{2}:\d{2}$/; - return stringCheck(arg) && !Number.isNaN(Date.parse(arg)) && - isoDateRegEx.test(arg); - }; - - const EXPECTED_INFO_FIELDS_TYPES = { - reason: stringCheck, - revision: revisionCheck, - timezoneOffset: numberCheck, - sessionId: uuidCheck, - subsessionId: uuidCheck, - // Special cases: previousSessionId and previousSubsessionId are null on first run. - previousSessionId: (arg) => { return (arg) ? uuidCheck(arg) : true; }, - previousSubsessionId: (arg) => { return (arg) ? uuidCheck(arg) : true; }, - subsessionCounter: positiveNumberCheck, - profileSubsessionCounter: positiveNumberCheck, - sessionStartDate: isoDateCheck, - subsessionStartDate: isoDateCheck, - subsessionLength: positiveNumberCheck, - }; - - for (let f in EXPECTED_INFO_FIELDS_TYPES) { - Assert.ok(f in data, f + " must be available."); - - let checkFunc = EXPECTED_INFO_FIELDS_TYPES[f]; - Assert.ok(checkFunc(data[f]), - f + " must have the correct type and valid data " + data[f]); - } - - // Previous buildId is not mandatory. - if (data.previousBuildId) { - Assert.ok(stringCheck(data.previousBuildId)); - } - - Assert.ok(ALLOWED_REASONS.find(r => r == data.reason), - "Payload must contain an allowed reason."); - - Assert.ok(Date.parse(data.subsessionStartDate) >= Date.parse(data.sessionStartDate)); - Assert.ok(data.profileSubsessionCounter >= data.subsessionCounter); - Assert.ok(data.timezoneOffset >= -12*60, "The timezone must be in a valid range."); - Assert.ok(data.timezoneOffset <= 12*60, "The timezone must be in a valid range."); -} - -function checkScalars(processes) { - // Check that the scalars section is available in the ping payload. - const parentProcess = processes.parent; - Assert.ok("scalars" in parentProcess, "The scalars section must be available in the parent process."); - Assert.ok("keyedScalars" in parentProcess, "The keyedScalars section must be available in the parent process."); - Assert.equal(typeof parentProcess.scalars, "object", "The scalars entry must be an object."); - Assert.equal(typeof parentProcess.keyedScalars, "object", "The keyedScalars entry must be an object."); - - let checkScalar = function(scalar) { - // Check if the value is of a supported type. - const valueType = typeof(scalar); - switch (valueType) { - case "string": - Assert.ok(scalar.length <= 50, - "String values can't have more than 50 characters"); - break; - case "number": - Assert.ok(scalar >= 0, - "We only support unsigned integer values in scalars."); - break; - case "boolean": - Assert.ok(true, - "Boolean scalar found."); - break; - default: - Assert.ok(false, - name + " contains an unsupported value type (" + valueType + ")"); - } - } - - // Check that we have valid scalar entries. - const scalars = parentProcess.scalars; - for (let name in scalars) { - Assert.equal(typeof name, "string", "Scalar names must be strings."); - checkScalar(scalar[name]); - } - - // Check that we have valid keyed scalar entries. - const keyedScalars = parentProcess.keyedScalars; - for (let name in keyedScalars) { - Assert.equal(typeof name, "string", "Scalar names must be strings."); - Assert.ok(Object.keys(keyedScalars[name]).length, - "The reported keyed scalars must contain at least 1 key."); - for (let key in keyedScalars[name]) { - Assert.equal(typeof key, "string", "Keyed scalar keys must be strings."); - Assert.ok(key.length <= 70, "Keyed scalar keys can't have more than 70 characters."); - checkScalar(scalar[name][key]); - } - } -} - -function checkEvents(processes) { - // Check that the events section is available in the ping payload. - const parent = processes.parent; - Assert.ok("events" in parent, "The events section must be available in the parent process."); - - // Check that the events section has the right format. - Assert.ok(Array.isArray(parent.events), "The events entry must be an array."); - for (let [ts, category, method, object, value, extra] of parent.events) { - Assert.equal(typeof(ts), "number", "Timestamp field should be a number."); - Assert.greaterOrEqual(ts, 0, "Timestamp should be >= 0."); - - Assert.equal(typeof(category), "string", "Category should have the right type."); - Assert.lessOrEqual(category.length, 100, "Category should have the right string length."); - - Assert.equal(typeof(method), "string", "Method should have the right type."); - Assert.lessOrEqual(method.length, 40, "Method should have the right string length."); - - Assert.equal(typeof(object), "string", "Object should have the right type."); - Assert.lessOrEqual(object.length, 40, "Object should have the right string length."); - - Assert.ok(value === null || typeof(value) === "string", - "Value should be null or a string."); - if (value) { - Assert.lessOrEqual(value.length, 100, "Value should have the right string length."); - } - - Assert.ok(extra === null || typeof(extra) === "object", - "Extra should be null or an object."); - if (extra) { - let keys = Object.keys(extra); - let keyTypes = keys.map(k => typeof(k)); - Assert.lessOrEqual(keys.length, 20, "Should not have too many extra keys."); - Assert.ok(keyTypes.every(t => t === "string"), - "All extra keys should be strings."); - Assert.ok(keys.every(k => k.length <= 20), - "All extra keys should have the right string length."); - - let values = Object.values(extra); - let valueTypes = values.map(v => typeof(v)); - Assert.ok(valueTypes.every(t => t === "string"), - "All extra values should be strings."); - Assert.ok(values.every(v => v.length <= 100), - "All extra values should have the right string length."); - } - } -} - -function checkPayload(payload, reason, successfulPings, savedPings) { - Assert.ok("info" in payload, "Payload must contain an info section."); - checkPayloadInfo(payload.info); - - Assert.ok(payload.simpleMeasurements.totalTime >= 0); - Assert.ok(payload.simpleMeasurements.uptime >= 0); - Assert.equal(payload.simpleMeasurements.startupInterrupted, 1); - Assert.equal(payload.simpleMeasurements.shutdownDuration, SHUTDOWN_TIME); - Assert.equal(payload.simpleMeasurements.savedPings, savedPings); - Assert.ok("maximalNumberOfConcurrentThreads" in payload.simpleMeasurements); - Assert.ok(payload.simpleMeasurements.maximalNumberOfConcurrentThreads >= gNumberOfThreadsLaunched); - - let activeTicks = payload.simpleMeasurements.activeTicks; - Assert.ok(activeTicks >= 0); - - Assert.equal(payload.simpleMeasurements.failedProfileLockCount, - FAILED_PROFILE_LOCK_ATTEMPTS); - let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile); - let failedProfileLocksFile = profileDirectory.clone(); - failedProfileLocksFile.append("Telemetry.FailedProfileLocks.txt"); - Assert.ok(!failedProfileLocksFile.exists()); - - - let isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes); - if (isWindows) { - Assert.ok(payload.simpleMeasurements.startupSessionRestoreReadBytes > 0); - Assert.ok(payload.simpleMeasurements.startupSessionRestoreWriteBytes > 0); - } - - const TELEMETRY_SEND_SUCCESS = "TELEMETRY_SEND_SUCCESS"; - const TELEMETRY_SUCCESS = "TELEMETRY_SUCCESS"; - const TELEMETRY_TEST_FLAG = "TELEMETRY_TEST_FLAG"; - const TELEMETRY_TEST_COUNT = "TELEMETRY_TEST_COUNT"; - const TELEMETRY_TEST_KEYED_FLAG = "TELEMETRY_TEST_KEYED_FLAG"; - const TELEMETRY_TEST_KEYED_COUNT = "TELEMETRY_TEST_KEYED_COUNT"; - - if (successfulPings > 0) { - Assert.ok(TELEMETRY_SEND_SUCCESS in payload.histograms); - } - Assert.ok(TELEMETRY_TEST_FLAG in payload.histograms); - Assert.ok(TELEMETRY_TEST_COUNT in payload.histograms); - - Assert.ok(!(IGNORE_CLONED_HISTOGRAM in payload.histograms)); - - // Flag histograms should automagically spring to life. - const expected_flag = { - range: [1, 2], - bucket_count: 3, - histogram_type: 3, - values: {0:1, 1:0}, - sum: 0 - }; - let flag = payload.histograms[TELEMETRY_TEST_FLAG]; - Assert.equal(uneval(flag), uneval(expected_flag)); - - // We should have a test count. - const expected_count = { - range: [1, 2], - bucket_count: 3, - histogram_type: 4, - values: {0:1, 1:0}, - sum: 1, - }; - let count = payload.histograms[TELEMETRY_TEST_COUNT]; - Assert.equal(uneval(count), uneval(expected_count)); - - // There should be one successful report from the previous telemetry ping. - if (successfulPings > 0) { - const expected_tc = { - range: [1, 2], - bucket_count: 3, - histogram_type: 2, - values: {0:2, 1:successfulPings, 2:0}, - sum: successfulPings - }; - let tc = payload.histograms[TELEMETRY_SUCCESS]; - Assert.equal(uneval(tc), uneval(expected_tc)); - } - - // The ping should include data from memory reporters. We can't check that - // this data is correct, because we can't control the values returned by the - // memory reporters. But we can at least check that the data is there. - // - // It's important to check for the presence of reporters with a mix of units, - // because TelemetryController has separate logic for each one. But we can't - // currently check UNITS_COUNT_CUMULATIVE or UNITS_PERCENTAGE because - // Telemetry doesn't touch a memory reporter with these units that's - // available on all platforms. - - Assert.ok('MEMORY_JS_GC_HEAP' in payload.histograms); // UNITS_BYTES - Assert.ok('MEMORY_JS_COMPARTMENTS_SYSTEM' in payload.histograms); // UNITS_COUNT - - // We should have included addon histograms. - Assert.ok("addonHistograms" in payload); - Assert.ok(ADDON_NAME in payload.addonHistograms); - Assert.ok(ADDON_HISTOGRAM in payload.addonHistograms[ADDON_NAME]); - - Assert.ok(("mainThread" in payload.slowSQL) && - ("otherThreads" in payload.slowSQL)); - - Assert.ok(("IceCandidatesStats" in payload.webrtc) && - ("webrtc" in payload.webrtc.IceCandidatesStats)); - - // Check keyed histogram payload. - - Assert.ok("keyedHistograms" in payload); - let keyedHistograms = payload.keyedHistograms; - Assert.ok(!(TELEMETRY_TEST_KEYED_FLAG in keyedHistograms)); - Assert.ok(TELEMETRY_TEST_KEYED_COUNT in keyedHistograms); - - const expected_keyed_count = { - "a": { - range: [1, 2], - bucket_count: 3, - histogram_type: 4, - values: {0:2, 1:0}, - sum: 2, - }, - "b": { - range: [1, 2], - bucket_count: 3, - histogram_type: 4, - values: {0:1, 1:0}, - sum: 1, - }, - }; - Assert.deepEqual(expected_keyed_count, keyedHistograms[TELEMETRY_TEST_KEYED_COUNT]); - - Assert.ok("processes" in payload, "The payload must have a processes section."); - Assert.ok("parent" in payload.processes, "There must be at least a parent process."); - checkScalars(payload.processes); - checkEvents(payload.processes); -} - -function writeStringToFile(file, contents) { - let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"] - .createInstance(Ci.nsIFileOutputStream); - ostream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, - RW_OWNER, ostream.DEFER_OPEN); - ostream.write(contents, contents.length); - ostream.QueryInterface(Ci.nsISafeOutputStream).finish(); - ostream.close(); -} - -function write_fake_shutdown_file() { - let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile); - let file = profileDirectory.clone(); - file.append("Telemetry.ShutdownTime.txt"); - let contents = "" + SHUTDOWN_TIME; - writeStringToFile(file, contents); -} - -function write_fake_failedprofilelocks_file() { - let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile); - let file = profileDirectory.clone(); - file.append("Telemetry.FailedProfileLocks.txt"); - let contents = "" + FAILED_PROFILE_LOCK_ATTEMPTS; - writeStringToFile(file, contents); -} - -add_task(function* test_setup() { - // Addon manager needs a profile directory - do_get_profile(); - loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION); - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - - Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); - Services.prefs.setBoolPref(PREF_FHR_UPLOAD_ENABLED, true); - - // Make it look like we've previously failed to lock a profile a couple times. - write_fake_failedprofilelocks_file(); - - // Make it look like we've shutdown before. - write_fake_shutdown_file(); - - let currentMaxNumberOfThreads = Telemetry.maximalNumberOfConcurrentThreads; - do_check_true(currentMaxNumberOfThreads > 0); - - // Try to augment the maximal number of threads currently launched - let threads = []; - try { - for (let i = 0; i < currentMaxNumberOfThreads + 10; ++i) { - threads.push(Services.tm.newThread(0)); - } - } catch (ex) { - // If memory is too low, it is possible that not all threads will be launched. - } - gNumberOfThreadsLaunched = threads.length; - - do_check_true(Telemetry.maximalNumberOfConcurrentThreads >= gNumberOfThreadsLaunched); - - do_register_cleanup(function() { - threads.forEach(function(thread) { - thread.shutdown(); - }); - }); - - yield new Promise(resolve => - Telemetry.asyncFetchTelemetryData(wrapWithExceptionHandler(resolve))); -}); - -add_task(function* asyncSetup() { - yield TelemetryController.testSetup(); - // Load the client ID from the client ID provider to check for pings sanity. - gClientID = yield ClientID.getClientID(); -}); - -// Ensures that expired histograms are not part of the payload. -add_task(function* test_expiredHistogram() { - - let dummy = Telemetry.getHistogramById("TELEMETRY_TEST_EXPIRED"); - - dummy.add(1); - - do_check_eq(TelemetrySession.getPayload()["histograms"]["TELEMETRY_TEST_EXPIRED"], undefined); -}); - -// Sends a ping to a non existing server. If we remove this test, we won't get -// all the histograms we need in the main ping. -add_task(function* test_noServerPing() { - yield sendPing(); - // We need two pings in order to make sure STARTUP_MEMORY_STORAGE_SQLIE histograms - // are initialised. See bug 1131585. - yield sendPing(); - // Allowing Telemetry to persist unsent pings as pending. If omitted may cause - // problems to the consequent tests. - yield TelemetryController.testShutdown(); -}); - -// Checks that a sent ping is correctly received by a dummy http server. -add_task(function* test_simplePing() { - yield TelemetryStorage.testClearPendingPings(); - PingServer.start(); - Preferences.set(PREF_SERVER, "http://localhost:" + PingServer.port); - - let now = new Date(2020, 1, 1, 12, 0, 0); - let expectedDate = new Date(2020, 1, 1, 0, 0, 0); - fakeNow(now); - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 5000); - - const expectedSessionUUID = "bd314d15-95bf-4356-b682-b6c4a8942202"; - const expectedSubsessionUUID = "3e2e5f6c-74ba-4e4d-a93f-a48af238a8c7"; - fakeGenerateUUID(() => expectedSessionUUID, () => expectedSubsessionUUID); - yield TelemetryController.testReset(); - - // Session and subsession start dates are faked during TelemetrySession setup. We can - // now fake the session duration. - const SESSION_DURATION_IN_MINUTES = 15; - fakeNow(new Date(2020, 1, 1, 12, SESSION_DURATION_IN_MINUTES, 0)); - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + SESSION_DURATION_IN_MINUTES * 60 * 1000); - - yield sendPing(); - let ping = yield PingServer.promiseNextPing(); - - checkPingFormat(ping, PING_TYPE_MAIN, true, true); - - // Check that we get the data we expect. - let payload = ping.payload; - Assert.equal(payload.info.sessionId, expectedSessionUUID); - Assert.equal(payload.info.subsessionId, expectedSubsessionUUID); - let sessionStartDate = new Date(payload.info.sessionStartDate); - Assert.equal(sessionStartDate.toISOString(), expectedDate.toISOString()); - let subsessionStartDate = new Date(payload.info.subsessionStartDate); - Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString()); - Assert.equal(payload.info.subsessionLength, SESSION_DURATION_IN_MINUTES * 60); - - // Restore the UUID generator so we don't mess with other tests. - fakeGenerateUUID(generateUUID, generateUUID); -}); - -// Saves the current session histograms, reloads them, performs a ping -// and checks that the dummy http server received both the previously -// saved ping and the new one. -add_task(function* test_saveLoadPing() { - // Let's start out with a defined state. - yield TelemetryStorage.testClearPendingPings(); - yield TelemetryController.testReset(); - PingServer.clearRequests(); - - // Setup test data and trigger pings. - setupTestData(); - yield TelemetrySession.testSavePendingPing(); - yield sendPing(); - - // Get requests received by dummy server. - const requests = yield PingServer.promiseNextRequests(2); - - for (let req of requests) { - Assert.equal(req.getHeader("content-type"), "application/json; charset=UTF-8", - "The request must have the correct content-type."); - } - - // We decode both requests to check for the |reason|. - let pings = Array.from(requests, decodeRequestPayload); - - // Check we have the correct two requests. Ordering is not guaranteed. The ping type - // is encoded in the URL. - if (pings[0].type != PING_TYPE_MAIN) { - pings.reverse(); - } - - checkPingFormat(pings[0], PING_TYPE_MAIN, true, true); - checkPayload(pings[0].payload, REASON_TEST_PING, 0, 1); - checkPingFormat(pings[1], PING_TYPE_SAVED_SESSION, true, true); - checkPayload(pings[1].payload, REASON_SAVED_SESSION, 0, 0); -}); - -add_task(function* test_checkSubsessionScalars() { - if (gIsAndroid) { - // We don't support subsessions yet on Android. - return; - } - - // Clear the scalars. - Telemetry.clearScalars(); - yield TelemetryController.testReset(); - - // Set some scalars. - const UINT_SCALAR = "telemetry.test.unsigned_int_kind"; - const STRING_SCALAR = "telemetry.test.string_kind"; - let expectedUint = 37; - let expectedString = "Test value. Yay."; - Telemetry.scalarSet(UINT_SCALAR, expectedUint); - Telemetry.scalarSet(STRING_SCALAR, expectedString); - - // Check that scalars are not available in classic pings but are in subsession - // pings. Also clear the subsession. - let classic = TelemetrySession.getPayload(); - let subsession = TelemetrySession.getPayload("environment-change", true); - - const TEST_SCALARS = [ UINT_SCALAR, STRING_SCALAR ]; - for (let name of TEST_SCALARS) { - // Scalar must be reported in subsession pings (e.g. main). - Assert.ok(name in subsession.processes.parent.scalars, - name + " must be reported in a subsession ping."); - } - // No scalar must be reported in classic pings (e.g. saved-session). - Assert.ok(Object.keys(classic.processes.parent.scalars).length == 0, - "Scalars must not be reported in a classic ping."); - - // And make sure that we're getting the right values in the - // subsession ping. - Assert.equal(subsession.processes.parent.scalars[UINT_SCALAR], expectedUint, - UINT_SCALAR + " must contain the expected value."); - Assert.equal(subsession.processes.parent.scalars[STRING_SCALAR], expectedString, - STRING_SCALAR + " must contain the expected value."); - - // Since we cleared the subsession in the last getPayload(), check that - // breaking subsessions clears the scalars. - subsession = TelemetrySession.getPayload("environment-change"); - for (let name of TEST_SCALARS) { - Assert.ok(!(name in subsession.processes.parent.scalars), - name + " must be cleared with the new subsession."); - } - - // Check if setting the scalars again works as expected. - expectedUint = 85; - expectedString = "A creative different value"; - Telemetry.scalarSet(UINT_SCALAR, expectedUint); - Telemetry.scalarSet(STRING_SCALAR, expectedString); - subsession = TelemetrySession.getPayload("environment-change"); - Assert.equal(subsession.processes.parent.scalars[UINT_SCALAR], expectedUint, - UINT_SCALAR + " must contain the expected value."); - Assert.equal(subsession.processes.parent.scalars[STRING_SCALAR], expectedString, - STRING_SCALAR + " must contain the expected value."); -}); - -add_task(function* test_checkSubsessionEvents() { - if (gIsAndroid) { - // We don't support subsessions yet on Android. - return; - } - - // Clear the events. - Telemetry.clearEvents(); - yield TelemetryController.testReset(); - - // Record some events. - let expected = [ - ["telemetry.test", "test1", "object1", "a", null], - ["telemetry.test", "test1", "object1", null, {key1: "value"}], - ]; - for (let event of expected) { - Telemetry.recordEvent(...event); - } - - // Strip off trailing null values to match the serialized events. - for (let e of expected) { - while ((e.length >= 3) && (e[e.length - 1] === null)) { - e.pop(); - } - } - - // Check that events are not available in classic pings but are in subsession - // pings. Also clear the subsession. - let classic = TelemetrySession.getPayload(); - let subsession = TelemetrySession.getPayload("environment-change", true); - - Assert.ok("events" in classic.processes.parent, "Should have an events field in classic payload."); - Assert.ok("events" in subsession.processes.parent, "Should have an events field in subsession payload."); - - // They should be empty in the classic payload. - Assert.deepEqual(classic.processes.parent.events, [], "Events in classic payload should be empty."); - - // In the subsession payload, they should contain the recorded test events. - let events = subsession.processes.parent.events.filter(e => e[1] === "telemetry.test"); - Assert.equal(events.length, expected.length, "Should have the right amount of events in the payload."); - for (let i = 0; i < expected.length; ++i) { - Assert.deepEqual(events[i].slice(1), expected[i], - "Should have the right event data in the ping."); - } - - // As we cleared the subsession above, the events entry should now be empty. - subsession = TelemetrySession.getPayload("environment-change", false); - Assert.ok("events" in subsession.processes.parent, "Should have an events field in subsession payload."); - events = subsession.processes.parent.events.filter(e => e[1] === "telemetry.test"); - Assert.equal(events.length, 0, "Should have no test events in the subsession payload now."); -}); - -add_task(function* test_checkSubsessionHistograms() { - if (gIsAndroid) { - // We don't support subsessions yet on Android. - return; - } - - let now = new Date(2020, 1, 1, 12, 0, 0); - let expectedDate = new Date(2020, 1, 1, 0, 0, 0); - fakeNow(now); - yield TelemetryController.testReset(); - - const COUNT_ID = "TELEMETRY_TEST_COUNT"; - const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT"; - const count = Telemetry.getHistogramById(COUNT_ID); - const keyed = Telemetry.getKeyedHistogramById(KEYED_ID); - const registeredIds = - new Set(Telemetry.registeredHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, [])); - - const stableHistograms = new Set([ - "TELEMETRY_TEST_FLAG", - "TELEMETRY_TEST_COUNT", - "TELEMETRY_TEST_RELEASE_OPTOUT", - "TELEMETRY_TEST_RELEASE_OPTIN", - "STARTUP_CRASH_DETECTED", - ]); - - const stableKeyedHistograms = new Set([ - "TELEMETRY_TEST_KEYED_FLAG", - "TELEMETRY_TEST_KEYED_COUNT", - "TELEMETRY_TEST_KEYED_RELEASE_OPTIN", - "TELEMETRY_TEST_KEYED_RELEASE_OPTOUT", - ]); - - // Compare the two sets of histograms. - // The "subsession" histograms should match the registered - // "classic" histograms. However, histograms can change - // between us collecting the different payloads, so we only - // check for deep equality on known stable histograms. - checkHistograms = (classic, subsession) => { - for (let id of Object.keys(classic)) { - if (!registeredIds.has(id)) { - continue; - } - - Assert.ok(id in subsession); - if (stableHistograms.has(id)) { - Assert.deepEqual(classic[id], - subsession[id]); - } else { - Assert.equal(classic[id].histogram_type, - subsession[id].histogram_type); - } - } - }; - - // Same as above, except for keyed histograms. - checkKeyedHistograms = (classic, subsession) => { - for (let id of Object.keys(classic)) { - if (!registeredIds.has(id)) { - continue; - } - - Assert.ok(id in subsession); - if (stableKeyedHistograms.has(id)) { - Assert.deepEqual(classic[id], - subsession[id]); - } - } - }; - - // Both classic and subsession payload histograms should start the same. - // The payloads should be identical for now except for the reason. - count.clear(); - keyed.clear(); - let classic = TelemetrySession.getPayload(); - let subsession = TelemetrySession.getPayload("environment-change"); - - Assert.equal(classic.info.reason, "gather-payload"); - Assert.equal(subsession.info.reason, "environment-change"); - Assert.ok(!(COUNT_ID in classic.histograms)); - Assert.ok(!(COUNT_ID in subsession.histograms)); - Assert.ok(!(KEYED_ID in classic.keyedHistograms)); - Assert.ok(!(KEYED_ID in subsession.keyedHistograms)); - - checkHistograms(classic.histograms, subsession.histograms); - checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms); - - // Adding values should get picked up in both. - count.add(1); - keyed.add("a", 1); - keyed.add("b", 1); - classic = TelemetrySession.getPayload(); - subsession = TelemetrySession.getPayload("environment-change"); - - Assert.ok(COUNT_ID in classic.histograms); - Assert.ok(COUNT_ID in subsession.histograms); - Assert.ok(KEYED_ID in classic.keyedHistograms); - Assert.ok(KEYED_ID in subsession.keyedHistograms); - Assert.equal(classic.histograms[COUNT_ID].sum, 1); - Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1); - Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1); - - checkHistograms(classic.histograms, subsession.histograms); - checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms); - - // Values should still reset properly. - count.clear(); - keyed.clear(); - classic = TelemetrySession.getPayload(); - subsession = TelemetrySession.getPayload("environment-change"); - - Assert.ok(!(COUNT_ID in classic.histograms)); - Assert.ok(!(COUNT_ID in subsession.histograms)); - Assert.ok(!(KEYED_ID in classic.keyedHistograms)); - Assert.ok(!(KEYED_ID in subsession.keyedHistograms)); - - checkHistograms(classic.histograms, subsession.histograms); - checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms); - - // Adding values should get picked up in both. - count.add(1); - keyed.add("a", 1); - keyed.add("b", 1); - classic = TelemetrySession.getPayload(); - subsession = TelemetrySession.getPayload("environment-change"); - - Assert.ok(COUNT_ID in classic.histograms); - Assert.ok(COUNT_ID in subsession.histograms); - Assert.ok(KEYED_ID in classic.keyedHistograms); - Assert.ok(KEYED_ID in subsession.keyedHistograms); - Assert.equal(classic.histograms[COUNT_ID].sum, 1); - Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1); - Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1); - - checkHistograms(classic.histograms, subsession.histograms); - checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms); - - // We should be able to reset only the subsession histograms. - // First check that "snapshot and clear" still returns the old state... - classic = TelemetrySession.getPayload(); - subsession = TelemetrySession.getPayload("environment-change", true); - - let subsessionStartDate = new Date(classic.info.subsessionStartDate); - Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString()); - subsessionStartDate = new Date(subsession.info.subsessionStartDate); - Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString()); - checkHistograms(classic.histograms, subsession.histograms); - checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms); - - // ... then check that the next snapshot shows the subsession - // histograms got reset. - classic = TelemetrySession.getPayload(); - subsession = TelemetrySession.getPayload("environment-change"); - - Assert.ok(COUNT_ID in classic.histograms); - Assert.ok(COUNT_ID in subsession.histograms); - Assert.equal(classic.histograms[COUNT_ID].sum, 1); - Assert.equal(subsession.histograms[COUNT_ID].sum, 0); - - Assert.ok(KEYED_ID in classic.keyedHistograms); - Assert.ok(!(KEYED_ID in subsession.keyedHistograms)); - Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1); - Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1); - - // Adding values should get picked up in both again. - count.add(1); - keyed.add("a", 1); - keyed.add("b", 1); - classic = TelemetrySession.getPayload(); - subsession = TelemetrySession.getPayload("environment-change"); - - Assert.ok(COUNT_ID in classic.histograms); - Assert.ok(COUNT_ID in subsession.histograms); - Assert.equal(classic.histograms[COUNT_ID].sum, 2); - Assert.equal(subsession.histograms[COUNT_ID].sum, 1); - - Assert.ok(KEYED_ID in classic.keyedHistograms); - Assert.ok(KEYED_ID in subsession.keyedHistograms); - Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 2); - Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 2); - Assert.equal(subsession.keyedHistograms[KEYED_ID]["a"].sum, 1); - Assert.equal(subsession.keyedHistograms[KEYED_ID]["b"].sum, 1); -}); - -add_task(function* test_checkSubsessionData() { - if (gIsAndroid) { - // We don't support subsessions yet on Android. - return; - } - - // Keep track of the active ticks count if the session recorder is available. - let sessionRecorder = TelemetryController.getSessionRecorder(); - let activeTicksAtSubsessionStart = sessionRecorder.activeTicks; - let expectedActiveTicks = activeTicksAtSubsessionStart; - - incrementActiveTicks = () => { - sessionRecorder.incrementActiveTicks(); - ++expectedActiveTicks; - } - - yield TelemetryController.testReset(); - - // Both classic and subsession payload data should be the same on the first subsession. - incrementActiveTicks(); - let classic = TelemetrySession.getPayload(); - let subsession = TelemetrySession.getPayload("environment-change"); - Assert.equal(classic.simpleMeasurements.activeTicks, expectedActiveTicks, - "Classic pings must count active ticks since the beginning of the session."); - Assert.equal(subsession.simpleMeasurements.activeTicks, expectedActiveTicks, - "Subsessions must count active ticks as classic pings on the first subsession."); - - // Start a new subsession and check that the active ticks are correctly reported. - incrementActiveTicks(); - activeTicksAtSubsessionStart = sessionRecorder.activeTicks; - classic = TelemetrySession.getPayload(); - subsession = TelemetrySession.getPayload("environment-change", true); - Assert.equal(classic.simpleMeasurements.activeTicks, expectedActiveTicks, - "Classic pings must count active ticks since the beginning of the session."); - Assert.equal(subsession.simpleMeasurements.activeTicks, expectedActiveTicks, - "Pings must not loose the tick count when starting a new subsession."); - - // Get a new subsession payload without clearing the subsession. - incrementActiveTicks(); - classic = TelemetrySession.getPayload(); - subsession = TelemetrySession.getPayload("environment-change"); - Assert.equal(classic.simpleMeasurements.activeTicks, expectedActiveTicks, - "Classic pings must count active ticks since the beginning of the session."); - Assert.equal(subsession.simpleMeasurements.activeTicks, - expectedActiveTicks - activeTicksAtSubsessionStart, - "Subsessions must count active ticks since the last new subsession."); -}); - -add_task(function* test_dailyCollection() { - if (gIsAndroid) { - // We don't do daily collections yet on Android. - return; - } - - let now = new Date(2030, 1, 1, 12, 0, 0); - let nowDay = new Date(2030, 1, 1, 0, 0, 0); - let schedulerTickCallback = null; - - PingServer.clearRequests(); - - fakeNow(now); - - // Fake scheduler functions to control daily collection flow in tests. - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - - // Init and check timer. - yield TelemetryStorage.testClearPendingPings(); - yield TelemetryController.testSetup(); - TelemetrySend.setServer("http://localhost:" + PingServer.port); - - // Set histograms to expected state. - const COUNT_ID = "TELEMETRY_TEST_COUNT"; - const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT"; - const count = Telemetry.getHistogramById(COUNT_ID); - const keyed = Telemetry.getKeyedHistogramById(KEYED_ID); - - count.clear(); - keyed.clear(); - count.add(1); - keyed.add("a", 1); - keyed.add("b", 1); - keyed.add("b", 1); - - // Make sure the daily ping gets triggered. - let expectedDate = nowDay; - now = futureDate(nowDay, MS_IN_ONE_DAY); - fakeNow(now); - - Assert.ok(!!schedulerTickCallback); - // Run a scheduler tick: it should trigger the daily ping. - yield schedulerTickCallback(); - - // Collect the daily ping. - let ping = yield PingServer.promiseNextPing(); - Assert.ok(!!ping); - - Assert.equal(ping.type, PING_TYPE_MAIN); - Assert.equal(ping.payload.info.reason, REASON_DAILY); - let subsessionStartDate = new Date(ping.payload.info.subsessionStartDate); - Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString()); - - Assert.equal(ping.payload.histograms[COUNT_ID].sum, 1); - Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["a"].sum, 1); - Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["b"].sum, 2); - - // The daily ping is rescheduled for "tomorrow". - expectedDate = futureDate(expectedDate, MS_IN_ONE_DAY); - now = futureDate(now, MS_IN_ONE_DAY); - fakeNow(now); - - // Run a scheduler tick. Trigger and collect another ping. The histograms should be reset. - yield schedulerTickCallback(); - - ping = yield PingServer.promiseNextPing(); - Assert.ok(!!ping); - - Assert.equal(ping.type, PING_TYPE_MAIN); - Assert.equal(ping.payload.info.reason, REASON_DAILY); - subsessionStartDate = new Date(ping.payload.info.subsessionStartDate); - Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString()); - - Assert.equal(ping.payload.histograms[COUNT_ID].sum, 0); - Assert.ok(!(KEYED_ID in ping.payload.keyedHistograms)); - - // Trigger and collect another daily ping, with the histograms being set again. - count.add(1); - keyed.add("a", 1); - keyed.add("b", 1); - - // The daily ping is rescheduled for "tomorrow". - expectedDate = futureDate(expectedDate, MS_IN_ONE_DAY); - now = futureDate(now, MS_IN_ONE_DAY); - fakeNow(now); - - yield schedulerTickCallback(); - ping = yield PingServer.promiseNextPing(); - Assert.ok(!!ping); - - Assert.equal(ping.type, PING_TYPE_MAIN); - Assert.equal(ping.payload.info.reason, REASON_DAILY); - subsessionStartDate = new Date(ping.payload.info.subsessionStartDate); - Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString()); - - Assert.equal(ping.payload.histograms[COUNT_ID].sum, 1); - Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["a"].sum, 1); - Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["b"].sum, 1); - - // Shutdown to cleanup the aborted-session if it gets created. - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_dailyDuplication() { - if (gIsAndroid) { - // We don't do daily collections yet on Android. - return; - } - - yield TelemetrySend.reset(); - yield TelemetryStorage.testClearPendingPings(); - PingServer.clearRequests(); - - let schedulerTickCallback = null; - let now = new Date(2030, 1, 1, 0, 0, 0); - fakeNow(now); - // Fake scheduler functions to control daily collection flow in tests. - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryController.testReset(); - - // Make sure the daily ping gets triggered at midnight. - // We need to make sure that we trigger this after the period where we wait for - // the user to become idle. - let firstDailyDue = new Date(2030, 1, 2, 0, 0, 0); - fakeNow(firstDailyDue); - - // Run a scheduler tick: it should trigger the daily ping. - Assert.ok(!!schedulerTickCallback); - yield schedulerTickCallback(); - - // Get the first daily ping. - let ping = yield PingServer.promiseNextPing(); - Assert.ok(!!ping); - - Assert.equal(ping.type, PING_TYPE_MAIN); - Assert.equal(ping.payload.info.reason, REASON_DAILY); - - // We don't expect to receive any other daily ping in this test, so assert if we do. - PingServer.registerPingHandler((req, res) => { - Assert.ok(false, "No more daily pings should be sent/received in this test."); - }); - - // Set the current time to a bit after midnight. - let secondDailyDue = new Date(firstDailyDue); - secondDailyDue.setHours(0); - secondDailyDue.setMinutes(15); - fakeNow(secondDailyDue); - - // Run a scheduler tick: it should NOT trigger the daily ping. - Assert.ok(!!schedulerTickCallback); - yield schedulerTickCallback(); - - // Shutdown to cleanup the aborted-session if it gets created. - PingServer.resetPingHandler(); - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_dailyOverdue() { - if (gIsAndroid) { - // We don't do daily collections yet on Android. - return; - } - - let schedulerTickCallback = null; - let now = new Date(2030, 1, 1, 11, 0, 0); - fakeNow(now); - // Fake scheduler functions to control daily collection flow in tests. - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryStorage.testClearPendingPings(); - yield TelemetryController.testReset(); - - // Skip one hour ahead: nothing should be due. - now.setHours(now.getHours() + 1); - fakeNow(now); - - // Assert if we receive something! - PingServer.registerPingHandler((req, res) => { - Assert.ok(false, "No daily ping should be received if not overdue!."); - }); - - // This tick should not trigger any daily ping. - Assert.ok(!!schedulerTickCallback); - yield schedulerTickCallback(); - - // Restore the non asserting ping handler. - PingServer.resetPingHandler(); - PingServer.clearRequests(); - - // Simulate an overdue ping: we're not close to midnight, but the last daily ping - // time is too long ago. - let dailyOverdue = new Date(2030, 1, 2, 13, 0, 0); - fakeNow(dailyOverdue); - - // Run a scheduler tick: it should trigger the daily ping. - Assert.ok(!!schedulerTickCallback); - yield schedulerTickCallback(); - - // Get the first daily ping. - let ping = yield PingServer.promiseNextPing(); - Assert.ok(!!ping); - - Assert.equal(ping.type, PING_TYPE_MAIN); - Assert.equal(ping.payload.info.reason, REASON_DAILY); - - // Shutdown to cleanup the aborted-session if it gets created. - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_environmentChange() { - if (gIsAndroid) { - // We don't split subsessions on environment changes yet on Android. - return; - } - - yield TelemetryStorage.testClearPendingPings(); - PingServer.clearRequests(); - - let now = fakeNow(2040, 1, 1, 12, 0, 0); - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 10 * MILLISECONDS_PER_MINUTE); - - const PREF_TEST = "toolkit.telemetry.test.pref1"; - Preferences.reset(PREF_TEST); - - const PREFS_TO_WATCH = new Map([ - [PREF_TEST, {what: TelemetryEnvironment.RECORD_PREF_VALUE}], - ]); - - // Setup. - yield TelemetryController.testReset(); - TelemetrySend.setServer("http://localhost:" + PingServer.port); - TelemetryEnvironment.testWatchPreferences(PREFS_TO_WATCH); - - // Set histograms to expected state. - const COUNT_ID = "TELEMETRY_TEST_COUNT"; - const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT"; - const count = Telemetry.getHistogramById(COUNT_ID); - const keyed = Telemetry.getKeyedHistogramById(KEYED_ID); - - count.clear(); - keyed.clear(); - count.add(1); - keyed.add("a", 1); - keyed.add("b", 1); - - // Trigger and collect environment-change ping. - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 10 * MILLISECONDS_PER_MINUTE); - let startDay = truncateDateToDays(now); - now = fakeNow(futureDate(now, 10 * MILLISECONDS_PER_MINUTE)); - - Preferences.set(PREF_TEST, 1); - let ping = yield PingServer.promiseNextPing(); - Assert.ok(!!ping); - - Assert.equal(ping.type, PING_TYPE_MAIN); - Assert.equal(ping.environment.settings.userPrefs[PREF_TEST], undefined); - Assert.equal(ping.payload.info.reason, REASON_ENVIRONMENT_CHANGE); - let subsessionStartDate = new Date(ping.payload.info.subsessionStartDate); - Assert.equal(subsessionStartDate.toISOString(), startDay.toISOString()); - - Assert.equal(ping.payload.histograms[COUNT_ID].sum, 1); - Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["a"].sum, 1); - - // Trigger and collect another ping. The histograms should be reset. - startDay = truncateDateToDays(now); - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 10 * MILLISECONDS_PER_MINUTE); - now = fakeNow(futureDate(now, 10 * MILLISECONDS_PER_MINUTE)); - - Preferences.set(PREF_TEST, 2); - ping = yield PingServer.promiseNextPing(); - Assert.ok(!!ping); - - Assert.equal(ping.type, PING_TYPE_MAIN); - Assert.equal(ping.environment.settings.userPrefs[PREF_TEST], 1); - Assert.equal(ping.payload.info.reason, REASON_ENVIRONMENT_CHANGE); - subsessionStartDate = new Date(ping.payload.info.subsessionStartDate); - Assert.equal(subsessionStartDate.toISOString(), startDay.toISOString()); - - Assert.equal(ping.payload.histograms[COUNT_ID].sum, 0); - Assert.ok(!(KEYED_ID in ping.payload.keyedHistograms)); -}); - -add_task(function* test_savedPingsOnShutdown() { - // On desktop, we expect both "saved-session" and "shutdown" pings. We only expect - // the former on Android. - const expectedPingCount = (gIsAndroid) ? 1 : 2; - // Assure that we store the ping properly when saving sessions on shutdown. - // We make the TelemetryController shutdown to trigger a session save. - const dir = TelemetryStorage.pingDirectoryPath; - yield OS.File.removeDir(dir, {ignoreAbsent: true}); - yield OS.File.makeDir(dir); - yield TelemetryController.testShutdown(); - - PingServer.clearRequests(); - yield TelemetryController.testReset(); - - const pings = yield PingServer.promiseNextPings(expectedPingCount); - - for (let ping of pings) { - Assert.ok("type" in ping); - - let expectedReason = - (ping.type == PING_TYPE_SAVED_SESSION) ? REASON_SAVED_SESSION : REASON_SHUTDOWN; - - checkPingFormat(ping, ping.type, true, true); - Assert.equal(ping.payload.info.reason, expectedReason); - Assert.equal(ping.clientId, gClientID); - } -}); - -add_task(function* test_savedSessionData() { - // Create the directory which will contain the data file, if it doesn't already - // exist. - yield OS.File.makeDir(DATAREPORTING_PATH); - getHistogram("TELEMETRY_SESSIONDATA_FAILED_LOAD").clear(); - getHistogram("TELEMETRY_SESSIONDATA_FAILED_PARSE").clear(); - getHistogram("TELEMETRY_SESSIONDATA_FAILED_VALIDATION").clear(); - - // Write test data to the session data file. - const dataFilePath = OS.Path.join(DATAREPORTING_PATH, "session-state.json"); - const sessionState = { - sessionId: null, - subsessionId: null, - profileSubsessionCounter: 3785, - }; - yield CommonUtils.writeJSON(sessionState, dataFilePath); - - const PREF_TEST = "toolkit.telemetry.test.pref1"; - Preferences.reset(PREF_TEST); - const PREFS_TO_WATCH = new Map([ - [PREF_TEST, {what: TelemetryEnvironment.RECORD_PREF_VALUE}], - ]); - - // We expect one new subsession when starting TelemetrySession and one after triggering - // an environment change. - const expectedSubsessions = sessionState.profileSubsessionCounter + 2; - const expectedSessionUUID = "ff602e52-47a1-b7e8-4c1a-ffffffffc87a"; - const expectedSubsessionUUID = "009fd1ad-b85e-4817-b3e5-000000003785"; - fakeGenerateUUID(() => expectedSessionUUID, () => expectedSubsessionUUID); - - if (gIsAndroid) { - // We don't support subsessions yet on Android, so skip the next checks. - return; - } - - // Start TelemetrySession so that it loads the session data file. - yield TelemetryController.testReset(); - Assert.equal(0, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_LOAD").sum); - Assert.equal(0, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_PARSE").sum); - Assert.equal(0, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_VALIDATION").sum); - - // Watch a test preference, trigger and environment change and wait for it to propagate. - // _watchPreferences triggers a subsession notification - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 10 * MILLISECONDS_PER_MINUTE); - fakeNow(new Date(2050, 1, 1, 12, 0, 0)); - TelemetryEnvironment.testWatchPreferences(PREFS_TO_WATCH); - let changePromise = new Promise(resolve => - TelemetryEnvironment.registerChangeListener("test_fake_change", resolve)); - Preferences.set(PREF_TEST, 1); - yield changePromise; - TelemetryEnvironment.unregisterChangeListener("test_fake_change"); - - let payload = TelemetrySession.getPayload(); - Assert.equal(payload.info.profileSubsessionCounter, expectedSubsessions); - yield TelemetryController.testShutdown(); - - // Restore the UUID generator so we don't mess with other tests. - fakeGenerateUUID(generateUUID, generateUUID); - - // Load back the serialised session data. - let data = yield CommonUtils.readJSON(dataFilePath); - Assert.equal(data.profileSubsessionCounter, expectedSubsessions); - Assert.equal(data.sessionId, expectedSessionUUID); - Assert.equal(data.subsessionId, expectedSubsessionUUID); -}); - -add_task(function* test_sessionData_ShortSession() { - if (gIsAndroid) { - // We don't support subsessions yet on Android, so skip the next checks. - return; - } - - const SESSION_STATE_PATH = OS.Path.join(DATAREPORTING_PATH, "session-state.json"); - - // Shut down Telemetry and remove the session state file. - yield TelemetryController.testReset(); - yield OS.File.remove(SESSION_STATE_PATH, { ignoreAbsent: true }); - getHistogram("TELEMETRY_SESSIONDATA_FAILED_LOAD").clear(); - getHistogram("TELEMETRY_SESSIONDATA_FAILED_PARSE").clear(); - getHistogram("TELEMETRY_SESSIONDATA_FAILED_VALIDATION").clear(); - - const expectedSessionUUID = "ff602e52-47a1-b7e8-4c1a-ffffffffc87a"; - const expectedSubsessionUUID = "009fd1ad-b85e-4817-b3e5-000000003785"; - fakeGenerateUUID(() => expectedSessionUUID, () => expectedSubsessionUUID); - - // We intentionally don't wait for the setup to complete and shut down to simulate - // short sessions. We expect the profile subsession counter to be 1. - TelemetryController.testReset(); - yield TelemetryController.testShutdown(); - - Assert.equal(1, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_LOAD").sum); - Assert.equal(0, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_PARSE").sum); - Assert.equal(0, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_VALIDATION").sum); - - // Restore the UUID generation functions. - fakeGenerateUUID(generateUUID, generateUUID); - - // Start TelemetryController so that it loads the session data file. We expect the profile - // subsession counter to be incremented by 1 again. - yield TelemetryController.testReset(); - - // We expect 2 profile subsession counter updates. - let payload = TelemetrySession.getPayload(); - Assert.equal(payload.info.profileSubsessionCounter, 2); - Assert.equal(payload.info.previousSessionId, expectedSessionUUID); - Assert.equal(payload.info.previousSubsessionId, expectedSubsessionUUID); - Assert.equal(1, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_LOAD").sum); - Assert.equal(0, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_PARSE").sum); - Assert.equal(0, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_VALIDATION").sum); -}); - -add_task(function* test_invalidSessionData() { - // Create the directory which will contain the data file, if it doesn't already - // exist. - yield OS.File.makeDir(DATAREPORTING_PATH); - getHistogram("TELEMETRY_SESSIONDATA_FAILED_LOAD").clear(); - getHistogram("TELEMETRY_SESSIONDATA_FAILED_PARSE").clear(); - getHistogram("TELEMETRY_SESSIONDATA_FAILED_VALIDATION").clear(); - - // Write test data to the session data file. This should fail to parse. - const dataFilePath = OS.Path.join(DATAREPORTING_PATH, "session-state.json"); - const unparseableData = "{asdf:@äü"; - OS.File.writeAtomic(dataFilePath, unparseableData, - {encoding: "utf-8", tmpPath: dataFilePath + ".tmp"}); - - // Start TelemetryController so that it loads the session data file. - yield TelemetryController.testReset(); - - // The session data file should not load. Only expect the current subsession. - Assert.equal(0, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_LOAD").sum); - Assert.equal(1, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_PARSE").sum); - Assert.equal(0, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_VALIDATION").sum); - - // Write test data to the session data file. This should fail validation. - const sessionState = { - profileSubsessionCounter: "not-a-number?", - someOtherField: 12, - }; - yield CommonUtils.writeJSON(sessionState, dataFilePath); - - // The session data file should not load. Only expect the current subsession. - const expectedSubsessions = 1; - const expectedSessionUUID = "ff602e52-47a1-b7e8-4c1a-ffffffffc87a"; - const expectedSubsessionUUID = "009fd1ad-b85e-4817-b3e5-000000003785"; - fakeGenerateUUID(() => expectedSessionUUID, () => expectedSubsessionUUID); - - // Start TelemetryController so that it loads the session data file. - yield TelemetryController.testReset(); - - let payload = TelemetrySession.getPayload(); - Assert.equal(payload.info.profileSubsessionCounter, expectedSubsessions); - Assert.equal(0, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_LOAD").sum); - Assert.equal(1, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_PARSE").sum); - Assert.equal(1, getSnapshot("TELEMETRY_SESSIONDATA_FAILED_VALIDATION").sum); - - yield TelemetryController.testShutdown(); - - // Restore the UUID generator so we don't mess with other tests. - fakeGenerateUUID(generateUUID, generateUUID); - - // Load back the serialised session data. - let data = yield CommonUtils.readJSON(dataFilePath); - Assert.equal(data.profileSubsessionCounter, expectedSubsessions); - Assert.equal(data.sessionId, expectedSessionUUID); - Assert.equal(data.subsessionId, expectedSubsessionUUID); -}); - -add_task(function* test_abortedSession() { - if (gIsAndroid || gIsGonk) { - // We don't have the aborted session ping here. - return; - } - - const ABORTED_FILE = OS.Path.join(DATAREPORTING_PATH, ABORTED_PING_FILE_NAME); - - // Make sure the aborted sessions directory does not exist to test its creation. - yield OS.File.removeDir(DATAREPORTING_PATH, { ignoreAbsent: true }); - - let schedulerTickCallback = null; - let now = new Date(2040, 1, 1, 0, 0, 0); - fakeNow(now); - // Fake scheduler functions to control aborted-session flow in tests. - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryController.testReset(); - - Assert.ok((yield OS.File.exists(DATAREPORTING_PATH)), - "Telemetry must create the aborted session directory when starting."); - - // Fake now again so that the scheduled aborted-session save takes place. - now = futureDate(now, ABORTED_SESSION_UPDATE_INTERVAL_MS); - fakeNow(now); - // The first aborted session checkpoint must take place right after the initialisation. - Assert.ok(!!schedulerTickCallback); - // Execute one scheduler tick. - yield schedulerTickCallback(); - // Check that the aborted session is due at the correct time. - Assert.ok((yield OS.File.exists(ABORTED_FILE)), - "There must be an aborted session ping."); - - // This ping is not yet in the pending pings folder, so we can't access it using - // TelemetryStorage.popPendingPings(). - let pingContent = yield OS.File.read(ABORTED_FILE, { encoding: "utf-8" }); - let abortedSessionPing = JSON.parse(pingContent); - - // Validate the ping. - checkPingFormat(abortedSessionPing, PING_TYPE_MAIN, true, true); - Assert.equal(abortedSessionPing.payload.info.reason, REASON_ABORTED_SESSION); - - // Trigger a another aborted-session ping and check that it overwrites the previous one. - now = futureDate(now, ABORTED_SESSION_UPDATE_INTERVAL_MS); - fakeNow(now); - yield schedulerTickCallback(); - - pingContent = yield OS.File.read(ABORTED_FILE, { encoding: "utf-8" }); - let updatedAbortedSessionPing = JSON.parse(pingContent); - checkPingFormat(updatedAbortedSessionPing, PING_TYPE_MAIN, true, true); - Assert.equal(updatedAbortedSessionPing.payload.info.reason, REASON_ABORTED_SESSION); - Assert.notEqual(abortedSessionPing.id, updatedAbortedSessionPing.id); - Assert.notEqual(abortedSessionPing.creationDate, updatedAbortedSessionPing.creationDate); - - yield TelemetryController.testShutdown(); - Assert.ok(!(yield OS.File.exists(ABORTED_FILE)), - "No aborted session ping must be available after a shutdown."); - - // Write the ping to the aborted-session file. TelemetrySession will add it to the - // saved pings directory when it starts. - yield TelemetryStorage.savePingToFile(abortedSessionPing, ABORTED_FILE, false); - Assert.ok((yield OS.File.exists(ABORTED_FILE)), - "The aborted session ping must exist in the aborted session ping directory."); - - yield TelemetryStorage.testClearPendingPings(); - PingServer.clearRequests(); - yield TelemetryController.testReset(); - - Assert.ok(!(yield OS.File.exists(ABORTED_FILE)), - "The aborted session ping must be removed from the aborted session ping directory."); - - // Restarting Telemetry again to trigger sending pings in TelemetrySend. - yield TelemetryController.testReset(); - - // We should have received an aborted-session ping. - const receivedPing = yield PingServer.promiseNextPing(); - Assert.equal(receivedPing.type, PING_TYPE_MAIN, "Should have the correct type"); - Assert.equal(receivedPing.payload.info.reason, REASON_ABORTED_SESSION, "Ping should have the correct reason"); - - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_abortedSession_Shutdown() { - if (gIsAndroid || gIsGonk) { - // We don't have the aborted session ping here. - return; - } - - const ABORTED_FILE = OS.Path.join(DATAREPORTING_PATH, ABORTED_PING_FILE_NAME); - - let schedulerTickCallback = null; - let now = fakeNow(2040, 1, 1, 0, 0, 0); - // Fake scheduler functions to control aborted-session flow in tests. - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryController.testReset(); - - Assert.ok((yield OS.File.exists(DATAREPORTING_PATH)), - "Telemetry must create the aborted session directory when starting."); - - // Fake now again so that the scheduled aborted-session save takes place. - fakeNow(futureDate(now, ABORTED_SESSION_UPDATE_INTERVAL_MS)); - // The first aborted session checkpoint must take place right after the initialisation. - Assert.ok(!!schedulerTickCallback); - // Execute one scheduler tick. - yield schedulerTickCallback(); - // Check that the aborted session is due at the correct time. - Assert.ok((yield OS.File.exists(ABORTED_FILE)), "There must be an aborted session ping."); - - // Remove the aborted session file and then shut down to make sure exceptions (e.g file - // not found) do not compromise the shutdown. - yield OS.File.remove(ABORTED_FILE); - - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_abortedDailyCoalescing() { - if (gIsAndroid || gIsGonk) { - // We don't have the aborted session or the daily ping here. - return; - } - - const ABORTED_FILE = OS.Path.join(DATAREPORTING_PATH, ABORTED_PING_FILE_NAME); - - // Make sure the aborted sessions directory does not exist to test its creation. - yield OS.File.removeDir(DATAREPORTING_PATH, { ignoreAbsent: true }); - - let schedulerTickCallback = null; - PingServer.clearRequests(); - - let nowDate = new Date(2009, 10, 18, 0, 0, 0); - fakeNow(nowDate); - - // Fake scheduler functions to control aborted-session flow in tests. - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryStorage.testClearPendingPings(); - PingServer.clearRequests(); - yield TelemetryController.testReset(); - - Assert.ok((yield OS.File.exists(DATAREPORTING_PATH)), - "Telemetry must create the aborted session directory when starting."); - - // Delay the callback around midnight so that the aborted-session ping gets merged with the - // daily ping. - let dailyDueDate = futureDate(nowDate, MS_IN_ONE_DAY); - fakeNow(dailyDueDate); - // Trigger both the daily ping and the saved-session. - Assert.ok(!!schedulerTickCallback); - // Execute one scheduler tick. - yield schedulerTickCallback(); - - // Wait for the daily ping. - let dailyPing = yield PingServer.promiseNextPing(); - Assert.equal(dailyPing.payload.info.reason, REASON_DAILY); - - // Check that an aborted session ping was also written to disk. - Assert.ok((yield OS.File.exists(ABORTED_FILE)), - "There must be an aborted session ping."); - - // Read aborted session ping and check that the session/subsession ids equal the - // ones in the daily ping. - let pingContent = yield OS.File.read(ABORTED_FILE, { encoding: "utf-8" }); - let abortedSessionPing = JSON.parse(pingContent); - Assert.equal(abortedSessionPing.payload.info.sessionId, dailyPing.payload.info.sessionId); - Assert.equal(abortedSessionPing.payload.info.subsessionId, dailyPing.payload.info.subsessionId); - - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_schedulerComputerSleep() { - if (gIsAndroid || gIsGonk) { - // We don't have the aborted session or the daily ping here. - return; - } - - const ABORTED_FILE = OS.Path.join(DATAREPORTING_PATH, ABORTED_PING_FILE_NAME); - - yield TelemetryStorage.testClearPendingPings(); - yield TelemetryController.testReset(); - PingServer.clearRequests(); - - // Remove any aborted-session ping from the previous tests. - yield OS.File.removeDir(DATAREPORTING_PATH, { ignoreAbsent: true }); - - // Set a fake current date and start Telemetry. - let nowDate = fakeNow(2009, 10, 18, 0, 0, 0); - let schedulerTickCallback = null; - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryController.testReset(); - - // Set the current time 3 days in the future at midnight, before running the callback. - nowDate = fakeNow(futureDate(nowDate, 3 * MS_IN_ONE_DAY)); - Assert.ok(!!schedulerTickCallback); - // Execute one scheduler tick. - yield schedulerTickCallback(); - - let dailyPing = yield PingServer.promiseNextPing(); - Assert.equal(dailyPing.payload.info.reason, REASON_DAILY, - "The wake notification should have triggered a daily ping."); - Assert.equal(dailyPing.creationDate, nowDate.toISOString(), - "The daily ping date should be correct."); - - Assert.ok((yield OS.File.exists(ABORTED_FILE)), - "There must be an aborted session ping."); - - // Now also test if we are sending a daily ping if we wake up on the next - // day even when the timer doesn't trigger. - // This can happen due to timeouts not running out during sleep times, - // see bug 1262386, bug 1204823 et al. - // Note that we don't get wake notifications on Linux due to bug 758848. - nowDate = fakeNow(futureDate(nowDate, 1 * MS_IN_ONE_DAY)); - - // We emulate the mentioned timeout behavior by sending the wake notification - // instead of triggering the timeout callback. - // This should trigger a daily ping, because we passed midnight. - Services.obs.notifyObservers(null, "wake_notification", null); - - dailyPing = yield PingServer.promiseNextPing(); - Assert.equal(dailyPing.payload.info.reason, REASON_DAILY, - "The wake notification should have triggered a daily ping."); - Assert.equal(dailyPing.creationDate, nowDate.toISOString(), - "The daily ping date should be correct."); - - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_schedulerEnvironmentReschedules() { - if (gIsAndroid || gIsGonk) { - // We don't have the aborted session or the daily ping here. - return; - } - - // Reset the test preference. - const PREF_TEST = "toolkit.telemetry.test.pref1"; - Preferences.reset(PREF_TEST); - const PREFS_TO_WATCH = new Map([ - [PREF_TEST, {what: TelemetryEnvironment.RECORD_PREF_VALUE}], - ]); - - yield TelemetryStorage.testClearPendingPings(); - PingServer.clearRequests(); - yield TelemetryController.testReset(); - - // Set a fake current date and start Telemetry. - let nowDate = fakeNow(2060, 10, 18, 0, 0, 0); - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 10 * MILLISECONDS_PER_MINUTE); - let schedulerTickCallback = null; - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryController.testReset(); - TelemetryEnvironment.testWatchPreferences(PREFS_TO_WATCH); - - // Set the current time at midnight. - fakeNow(futureDate(nowDate, MS_IN_ONE_DAY)); - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 10 * MILLISECONDS_PER_MINUTE); - - // Trigger the environment change. - Preferences.set(PREF_TEST, 1); - - // Wait for the environment-changed ping. - yield PingServer.promiseNextPing(); - - // We don't expect to receive any daily ping in this test, so assert if we do. - PingServer.registerPingHandler((req, res) => { - Assert.ok(false, "No ping should be sent/received in this test."); - }); - - // Execute one scheduler tick. It should not trigger a daily ping. - Assert.ok(!!schedulerTickCallback); - yield schedulerTickCallback(); - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_schedulerNothingDue() { - if (gIsAndroid || gIsGonk) { - // We don't have the aborted session or the daily ping here. - return; - } - - const ABORTED_FILE = OS.Path.join(DATAREPORTING_PATH, ABORTED_PING_FILE_NAME); - - // Remove any aborted-session ping from the previous tests. - yield OS.File.removeDir(DATAREPORTING_PATH, { ignoreAbsent: true }); - yield TelemetryStorage.testClearPendingPings(); - yield TelemetryController.testReset(); - - // We don't expect to receive any ping in this test, so assert if we do. - PingServer.registerPingHandler((req, res) => { - Assert.ok(false, "No ping should be sent/received in this test."); - }); - - // Set a current date/time away from midnight, so that the daily ping doesn't get - // sent. - let nowDate = new Date(2009, 10, 18, 11, 0, 0); - fakeNow(nowDate); - let schedulerTickCallback = null; - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryController.testReset(); - - // Delay the callback execution to a time when no ping should be due. - let nothingDueDate = futureDate(nowDate, ABORTED_SESSION_UPDATE_INTERVAL_MS / 2); - fakeNow(nothingDueDate); - Assert.ok(!!schedulerTickCallback); - // Execute one scheduler tick. - yield schedulerTickCallback(); - - // Check that no aborted session ping was written to disk. - Assert.ok(!(yield OS.File.exists(ABORTED_FILE))); - - yield TelemetryController.testShutdown(); - PingServer.resetPingHandler(); -}); - -add_task(function* test_pingExtendedStats() { - const EXTENDED_PAYLOAD_FIELDS = [ - "chromeHangs", "threadHangStats", "log", "slowSQL", "fileIOReports", "lateWrites", - "addonHistograms", "addonDetails", "UIMeasurements", "webrtc" - ]; - - // Reset telemetry and disable sending extended statistics. - yield TelemetryStorage.testClearPendingPings(); - PingServer.clearRequests(); - yield TelemetryController.testReset(); - Telemetry.canRecordExtended = false; - - yield sendPing(); - - let ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, PING_TYPE_MAIN, true, true); - - // Check that the payload does not contain extended statistics fields. - for (let f in EXTENDED_PAYLOAD_FIELDS) { - Assert.ok(!(EXTENDED_PAYLOAD_FIELDS[f] in ping.payload), - EXTENDED_PAYLOAD_FIELDS[f] + " must not be in the payload if the extended set is off."); - } - - // We check this one separately so that we can reuse EXTENDED_PAYLOAD_FIELDS below, since - // slowSQLStartup might not be there. - Assert.ok(!("slowSQLStartup" in ping.payload), - "slowSQLStartup must not be sent if the extended set is off"); - - Assert.ok(!("addonManager" in ping.payload.simpleMeasurements), - "addonManager must not be sent if the extended set is off."); - Assert.ok(!("UITelemetry" in ping.payload.simpleMeasurements), - "UITelemetry must not be sent if the extended set is off."); - - // Restore the preference. - Telemetry.canRecordExtended = true; - - // Send a new ping that should contain the extended data. - yield sendPing(); - ping = yield PingServer.promiseNextPing(); - checkPingFormat(ping, PING_TYPE_MAIN, true, true); - - // Check that the payload now contains extended statistics fields. - for (let f in EXTENDED_PAYLOAD_FIELDS) { - Assert.ok(EXTENDED_PAYLOAD_FIELDS[f] in ping.payload, - EXTENDED_PAYLOAD_FIELDS[f] + " must be in the payload if the extended set is on."); - } - - Assert.ok("addonManager" in ping.payload.simpleMeasurements, - "addonManager must be sent if the extended set is on."); - Assert.ok("UITelemetry" in ping.payload.simpleMeasurements, - "UITelemetry must be sent if the extended set is on."); -}); - -add_task(function* test_schedulerUserIdle() { - if (gIsAndroid || gIsGonk) { - // We don't have the aborted session or the daily ping here. - return; - } - - const SCHEDULER_TICK_INTERVAL_MS = 5 * 60 * 1000; - const SCHEDULER_TICK_IDLE_INTERVAL_MS = 60 * 60 * 1000; - - let now = new Date(2010, 1, 1, 11, 0, 0); - fakeNow(now); - - let schedulerTimeout = 0; - fakeSchedulerTimer((callback, timeout) => { - schedulerTimeout = timeout; - }, () => {}); - yield TelemetryController.testReset(); - yield TelemetryStorage.testClearPendingPings(); - PingServer.clearRequests(); - - // When not idle, the scheduler should have a 5 minutes tick interval. - Assert.equal(schedulerTimeout, SCHEDULER_TICK_INTERVAL_MS); - - // Send an "idle" notification to the scheduler. - fakeIdleNotification("idle"); - - // When idle, the scheduler should have a 1hr tick interval. - Assert.equal(schedulerTimeout, SCHEDULER_TICK_IDLE_INTERVAL_MS); - - // Send an "active" notification to the scheduler. - fakeIdleNotification("active"); - - // When user is back active, the scheduler tick should be 5 minutes again. - Assert.equal(schedulerTimeout, SCHEDULER_TICK_INTERVAL_MS); - - // We should not miss midnight when going to idle. - now.setHours(23); - now.setMinutes(50); - fakeNow(now); - fakeIdleNotification("idle"); - Assert.equal(schedulerTimeout, 10 * 60 * 1000); - - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_DailyDueAndIdle() { - if (gIsAndroid || gIsGonk) { - // We don't have the aborted session or the daily ping here. - return; - } - - yield TelemetryStorage.testClearPendingPings(); - PingServer.clearRequests(); - - let receivedPingRequest = null; - // Register a ping handler that will assert when receiving multiple daily pings. - PingServer.registerPingHandler(req => { - Assert.ok(!receivedPingRequest, "Telemetry must only send one daily ping."); - receivedPingRequest = req; - }); - - // Faking scheduler timer has to happen before resetting TelemetryController - // to be effective. - let schedulerTickCallback = null; - let now = new Date(2030, 1, 1, 0, 0, 0); - fakeNow(now); - // Fake scheduler functions to control daily collection flow in tests. - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryController.testReset(); - - // Trigger the daily ping. - let firstDailyDue = new Date(2030, 1, 2, 0, 0, 0); - fakeNow(firstDailyDue); - - // Run a scheduler tick: it should trigger the daily ping. - Assert.ok(!!schedulerTickCallback); - let tickPromise = schedulerTickCallback(); - - // Send an idle and then an active user notification. - fakeIdleNotification("idle"); - fakeIdleNotification("active"); - - // Wait on the tick promise. - yield tickPromise; - - yield TelemetrySend.testWaitOnOutgoingPings(); - - // Decode the ping contained in the request and check that's a daily ping. - Assert.ok(receivedPingRequest, "Telemetry must send one daily ping."); - const receivedPing = decodeRequestPayload(receivedPingRequest); - checkPingFormat(receivedPing, PING_TYPE_MAIN, true, true); - Assert.equal(receivedPing.payload.info.reason, REASON_DAILY); - - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_userIdleAndSchedlerTick() { - if (gIsAndroid || gIsGonk) { - // We don't have the aborted session or the daily ping here. - return; - } - - let receivedPingRequest = null; - // Register a ping handler that will assert when receiving multiple daily pings. - PingServer.registerPingHandler(req => { - Assert.ok(!receivedPingRequest, "Telemetry must only send one daily ping."); - receivedPingRequest = req; - }); - - let schedulerTickCallback = null; - let now = new Date(2030, 1, 1, 0, 0, 0); - fakeNow(now); - // Fake scheduler functions to control daily collection flow in tests. - fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {}); - yield TelemetryStorage.testClearPendingPings(); - yield TelemetryController.testReset(); - PingServer.clearRequests(); - - // Move the current date/time to midnight. - let firstDailyDue = new Date(2030, 1, 2, 0, 0, 0); - fakeNow(firstDailyDue); - - // The active notification should trigger a scheduler tick. The latter will send the - // due daily ping. - fakeIdleNotification("active"); - - // Immediately running another tick should not send a daily ping again. - Assert.ok(!!schedulerTickCallback); - yield schedulerTickCallback(); - - // A new "idle" notification should not send a new daily ping. - fakeIdleNotification("idle"); - - yield TelemetrySend.testWaitOnOutgoingPings(); - - // Decode the ping contained in the request and check that's a daily ping. - Assert.ok(receivedPingRequest, "Telemetry must send one daily ping."); - const receivedPing = decodeRequestPayload(receivedPingRequest); - checkPingFormat(receivedPing, PING_TYPE_MAIN, true, true); - Assert.equal(receivedPing.payload.info.reason, REASON_DAILY); - - PingServer.resetPingHandler(); - yield TelemetryController.testShutdown(); -}); - -add_task(function* test_changeThrottling() { - if (gIsAndroid) { - // We don't support subsessions yet on Android. - return; - } - - let getSubsessionCount = () => { - return TelemetrySession.getPayload().info.subsessionCounter; - }; - - const PREF_TEST = "toolkit.telemetry.test.pref1"; - const PREFS_TO_WATCH = new Map([ - [PREF_TEST, {what: TelemetryEnvironment.RECORD_PREF_STATE}], - ]); - Preferences.reset(PREF_TEST); - - let now = fakeNow(2050, 1, 2, 0, 0, 0); - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 10 * MILLISECONDS_PER_MINUTE); - yield TelemetryController.testReset(); - Assert.equal(getSubsessionCount(), 1); - - // Set the Environment preferences to watch. - TelemetryEnvironment.testWatchPreferences(PREFS_TO_WATCH); - - // The first pref change should not trigger a notification. - Preferences.set(PREF_TEST, 1); - Assert.equal(getSubsessionCount(), 1); - - // We should get a change notification after the 5min throttling interval. - fakeNow(futureDate(now, 5 * MILLISECONDS_PER_MINUTE + 1)); - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 5 * MILLISECONDS_PER_MINUTE + 1); - Preferences.set(PREF_TEST, 2); - Assert.equal(getSubsessionCount(), 2); - - // After that, changes should be throttled again. - now = fakeNow(futureDate(now, 1 * MILLISECONDS_PER_MINUTE)); - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 1 * MILLISECONDS_PER_MINUTE); - Preferences.set(PREF_TEST, 3); - Assert.equal(getSubsessionCount(), 2); - - // ... for 5min. - now = fakeNow(futureDate(now, 4 * MILLISECONDS_PER_MINUTE + 1)); - gMonotonicNow = fakeMonotonicNow(gMonotonicNow + 4 * MILLISECONDS_PER_MINUTE + 1); - Preferences.set(PREF_TEST, 4); - Assert.equal(getSubsessionCount(), 3); - - // Unregister the listener. - TelemetryEnvironment.unregisterChangeListener("testWatchPrefs_throttling"); -}); - -add_task(function* stopServer() { - yield PingServer.stop(); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryTimestamps.js b/toolkit/components/telemetry/tests/unit/test_TelemetryTimestamps.js deleted file mode 100644 index 75bf3157a..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryTimestamps.js +++ /dev/null @@ -1,77 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -var Cu = Components.utils; -var Cc = Components.classes; -var Ci = Components.interfaces; -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetrySession.jsm", this); -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); - -// The @mozilla/xre/app-info;1 XPCOM object provided by the xpcshell test harness doesn't -// implement the nsIXULAppInfo interface, which is needed by Services.jsm and -// TelemetrySession.jsm. updateAppInfo() creates and registers a minimal mock app-info. -Cu.import("resource://testing-common/AppInfo.jsm"); -updateAppInfo(); - -var gGlobalScope = this; - -function getSimpleMeasurementsFromTelemetryController() { - return TelemetrySession.getPayload().simpleMeasurements; -} - -add_task(function* test_setup() { - // Telemetry needs the AddonManager. - loadAddonManager(); - // Make profile available for |TelemetryController.testShutdown()|. - do_get_profile(); - - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - - yield new Promise(resolve => - Services.telemetry.asyncFetchTelemetryData(resolve)); -}); - -add_task(function* actualTest() { - yield TelemetryController.testSetup(); - - // Test the module logic - let tmp = {}; - Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", tmp); - let TelemetryTimestamps = tmp.TelemetryTimestamps; - let now = Date.now(); - TelemetryTimestamps.add("foo"); - do_check_true(TelemetryTimestamps.get().foo != null); // foo was added - do_check_true(TelemetryTimestamps.get().foo >= now); // foo has a reasonable value - - // Add timestamp with value - // Use a value far in the future since TelemetryController substracts the time of - // process initialization. - const YEAR_4000_IN_MS = 64060588800000; - TelemetryTimestamps.add("bar", YEAR_4000_IN_MS); - do_check_eq(TelemetryTimestamps.get().bar, YEAR_4000_IN_MS); // bar has the right value - - // Can't add the same timestamp twice - TelemetryTimestamps.add("bar", 2); - do_check_eq(TelemetryTimestamps.get().bar, YEAR_4000_IN_MS); // bar wasn't overwritten - - let threw = false; - try { - TelemetryTimestamps.add("baz", "this isn't a number"); - } catch (ex) { - threw = true; - } - do_check_true(threw); // adding non-number threw - do_check_null(TelemetryTimestamps.get().baz); // no baz was added - - // Test that the data gets added to the telemetry ping properly - let simpleMeasurements = getSimpleMeasurementsFromTelemetryController(); - do_check_true(simpleMeasurements != null); // got simple measurements from ping data - do_check_true(simpleMeasurements.foo > 1); // foo was included - do_check_true(simpleMeasurements.bar > 1); // bar was included - do_check_eq(undefined, simpleMeasurements.baz); // baz wasn't included since it wasn't added - - yield TelemetryController.testShutdown(); -}); diff --git a/toolkit/components/telemetry/tests/unit/test_ThreadHangStats.js b/toolkit/components/telemetry/tests/unit/test_ThreadHangStats.js deleted file mode 100644 index e8c9f868a..000000000 --- a/toolkit/components/telemetry/tests/unit/test_ThreadHangStats.js +++ /dev/null @@ -1,102 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -Cu.import("resource://gre/modules/Services.jsm"); - -function getMainThreadHangStats() { - let threads = Services.telemetry.threadHangStats; - return threads.find((thread) => (thread.name === "Gecko")); -} - -function run_test() { - let startHangs = getMainThreadHangStats(); - - // We disable hang reporting in several situations (e.g. debug builds, - // official releases). In those cases, we don't have hang stats available - // and should exit the test early. - if (!startHangs) { - ok("Hang reporting not enabled."); - return; - } - - if (Services.appinfo.OS === 'Linux' || Services.appinfo.OS === 'Android') { - // We use the rt_tgsigqueueinfo syscall on Linux which requires a - // certain kernel version. It's not an error if the system running - // the test is older than that. - let kernel = Services.sysinfo.get('kernel_version') || - Services.sysinfo.get('version'); - if (Services.vc.compare(kernel, '2.6.31') < 0) { - ok("Hang reporting not supported for old kernel."); - return; - } - } - - // Run three events in the event loop: - // the first event causes a transient hang; - // the second event causes a permanent hang; - // the third event checks results from previous events. - - do_execute_soon(() => { - // Cause a hang lasting 1 second (transient hang). - let startTime = Date.now(); - while ((Date.now() - startTime) < 1000); - }); - - do_execute_soon(() => { - // Cause a hang lasting 10 seconds (permanent hang). - let startTime = Date.now(); - while ((Date.now() - startTime) < 10000); - }); - - do_execute_soon(() => { - do_test_pending(); - - let check_results = () => { - let endHangs = getMainThreadHangStats(); - - // Because hangs are recorded asynchronously, if we don't see new hangs, - // we should wait for pending hangs to be recorded. On the other hand, - // if hang monitoring is broken, this test will time out. - if (endHangs.hangs.length === startHangs.hangs.length) { - do_timeout(100, check_results); - return; - } - - let check_histogram = (histogram) => { - equal(typeof histogram, "object"); - equal(histogram.histogram_type, 0); - equal(typeof histogram.min, "number"); - equal(typeof histogram.max, "number"); - equal(typeof histogram.sum, "number"); - ok(Array.isArray(histogram.ranges)); - ok(Array.isArray(histogram.counts)); - equal(histogram.counts.length, histogram.ranges.length); - }; - - // Make sure the hang stats structure is what we expect. - equal(typeof endHangs, "object"); - check_histogram(endHangs.activity); - - ok(Array.isArray(endHangs.hangs)); - notEqual(endHangs.hangs.length, 0); - - ok(Array.isArray(endHangs.hangs[0].stack)); - notEqual(endHangs.hangs[0].stack.length, 0); - equal(typeof endHangs.hangs[0].stack[0], "string"); - - // Make sure one of the hangs is a permanent - // hang containing a native stack. - ok(endHangs.hangs.some((hang) => ( - Array.isArray(hang.nativeStack) && - hang.nativeStack.length !== 0 && - typeof hang.nativeStack[0] === "string" - ))); - - check_histogram(endHangs.hangs[0].histogram); - - do_test_finished(); - }; - - check_results(); - }); -} diff --git a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js deleted file mode 100644 index 8dc552604..000000000 --- a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js +++ /dev/null @@ -1,883 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -const INT_MAX = 0x7FFFFFFF; - -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://gre/modules/TelemetryUtils.jsm", this); - -// Return an array of numbers from lower up to, excluding, upper -function numberRange(lower, upper) -{ - let a = []; - for (let i=lower; i<upper; ++i) { - a.push(i); - } - return a; -} - -function expect_fail(f) { - let failed = false; - try { - f(); - failed = false; - } catch (e) { - failed = true; - } - do_check_true(failed); -} - -function expect_success(f) { - let succeeded = false; - try { - f(); - succeeded = true; - } catch (e) { - succeeded = false; - } - do_check_true(succeeded); -} - -function compareHistograms(h1, h2) { - let s1 = h1.snapshot(); - let s2 = h2.snapshot(); - - do_check_eq(s1.histogram_type, s2.histogram_type); - do_check_eq(s1.min, s2.min); - do_check_eq(s1.max, s2.max); - do_check_eq(s1.sum, s2.sum); - - do_check_eq(s1.counts.length, s2.counts.length); - for (let i = 0; i < s1.counts.length; i++) - do_check_eq(s1.counts[i], s2.counts[i]); - - do_check_eq(s1.ranges.length, s2.ranges.length); - for (let i = 0; i < s1.ranges.length; i++) - do_check_eq(s1.ranges[i], s2.ranges[i]); -} - -function check_histogram(histogram_type, name, min, max, bucket_count) { - var h = Telemetry.getHistogramById(name); - var r = h.snapshot().ranges; - var sum = 0; - for (let i=0;i<r.length;i++) { - var v = r[i]; - sum += v; - h.add(v); - } - var s = h.snapshot(); - // verify properties - do_check_eq(sum, s.sum); - - // there should be exactly one element per bucket - for (let i of s.counts) { - do_check_eq(i, 1); - } - var hgrams = Telemetry.histogramSnapshots - let gh = hgrams[name] - do_check_eq(gh.histogram_type, histogram_type); - - do_check_eq(gh.min, min) - do_check_eq(gh.max, max) - - // Check that booleans work with nonboolean histograms - h.add(false); - h.add(true); - s = h.snapshot().counts; - do_check_eq(s[0], 2) - do_check_eq(s[1], 2) - - // Check that clearing works. - h.clear(); - s = h.snapshot(); - for (var i of s.counts) { - do_check_eq(i, 0); - } - do_check_eq(s.sum, 0); - - h.add(0); - h.add(1); - var c = h.snapshot().counts; - do_check_eq(c[0], 1); - do_check_eq(c[1], 1); -} - -// This MUST be the very first test of this file. -add_task({ - skip_if: () => gIsAndroid -}, -function* test_instantiate() { - const ID = "TELEMETRY_TEST_COUNT"; - let h = Telemetry.getHistogramById(ID); - - // Instantiate the subsession histogram through |add| and make sure they match. - // This MUST be the first use of "TELEMETRY_TEST_COUNT" in this file, otherwise - // |add| will not instantiate the histogram. - h.add(1); - let snapshot = h.snapshot(); - let subsession = Telemetry.snapshotSubsessionHistograms(); - Assert.equal(snapshot.sum, subsession[ID].sum, - "Histogram and subsession histogram sum must match."); - // Clear the histogram, so we don't void the assumptions from the other tests. - h.clear(); -}); - -add_task(function* test_parameterChecks() { - let kinds = [Telemetry.HISTOGRAM_EXPONENTIAL, Telemetry.HISTOGRAM_LINEAR] - let testNames = ["TELEMETRY_TEST_EXPONENTIAL", "TELEMETRY_TEST_LINEAR"] - for (let i = 0; i < kinds.length; i++) { - let histogram_type = kinds[i]; - let test_type = testNames[i]; - let [min, max, bucket_count] = [1, INT_MAX - 1, 10] - check_histogram(histogram_type, test_type, min, max, bucket_count); - } -}); - -add_task(function* test_noSerialization() { - // Instantiate the storage for this histogram and make sure it doesn't - // get reflected into JS, as it has no interesting data in it. - Telemetry.getHistogramById("NEWTAB_PAGE_PINNED_SITES_COUNT"); - do_check_false("NEWTAB_PAGE_PINNED_SITES_COUNT" in Telemetry.histogramSnapshots); -}); - -add_task(function* test_boolean_histogram() { - var h = Telemetry.getHistogramById("TELEMETRY_TEST_BOOLEAN"); - var r = h.snapshot().ranges; - // boolean histograms ignore numeric parameters - do_check_eq(uneval(r), uneval([0, 1, 2])) - for (var i=0;i<r.length;i++) { - var v = r[i]; - h.add(v); - } - h.add(true); - h.add(false); - var s = h.snapshot(); - do_check_eq(s.histogram_type, Telemetry.HISTOGRAM_BOOLEAN); - // last bucket should always be 0 since .add parameters are normalized to either 0 or 1 - do_check_eq(s.counts[2], 0); - do_check_eq(s.sum, 3); - do_check_eq(s.counts[0], 2); -}); - -add_task(function* test_flag_histogram() { - var h = Telemetry.getHistogramById("TELEMETRY_TEST_FLAG"); - var r = h.snapshot().ranges; - // Flag histograms ignore numeric parameters. - do_check_eq(uneval(r), uneval([0, 1, 2])); - // Should already have a 0 counted. - var c = h.snapshot().counts; - var s = h.snapshot().sum; - do_check_eq(uneval(c), uneval([1, 0, 0])); - do_check_eq(s, 0); - // Should switch counts. - h.add(1); - var c2 = h.snapshot().counts; - var s2 = h.snapshot().sum; - do_check_eq(uneval(c2), uneval([0, 1, 0])); - do_check_eq(s2, 1); - // Should only switch counts once. - h.add(1); - var c3 = h.snapshot().counts; - var s3 = h.snapshot().sum; - do_check_eq(uneval(c3), uneval([0, 1, 0])); - do_check_eq(s3, 1); - do_check_eq(h.snapshot().histogram_type, Telemetry.HISTOGRAM_FLAG); -}); - -add_task(function* test_count_histogram() { - let h = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT2"); - let s = h.snapshot(); - do_check_eq(uneval(s.ranges), uneval([0, 1, 2])); - do_check_eq(uneval(s.counts), uneval([0, 0, 0])); - do_check_eq(s.sum, 0); - h.add(); - s = h.snapshot(); - do_check_eq(uneval(s.counts), uneval([1, 0, 0])); - do_check_eq(s.sum, 1); - h.add(); - s = h.snapshot(); - do_check_eq(uneval(s.counts), uneval([2, 0, 0])); - do_check_eq(s.sum, 2); -}); - -add_task(function* test_categorical_histogram() -{ - let h1 = Telemetry.getHistogramById("TELEMETRY_TEST_CATEGORICAL"); - for (let v of ["CommonLabel", "Label2", "Label3", "Label3", 0, 0, 1]) { - h1.add(v); - } - for (let s of ["", "Label4", "1234"]) { - Assert.throws(() => h1.add(s)); - } - - let snapshot = h1.snapshot(); - Assert.equal(snapshot.sum, 6); - Assert.deepEqual(snapshot.ranges, [0, 1, 2, 3]); - Assert.deepEqual(snapshot.counts, [3, 2, 2, 0]); - - let h2 = Telemetry.getHistogramById("TELEMETRY_TEST_CATEGORICAL_OPTOUT"); - for (let v of ["CommonLabel", "CommonLabel", "Label4", "Label5", "Label6", 0, 1]) { - h2.add(v); - } - for (let s of ["", "Label3", "1234"]) { - Assert.throws(() => h2.add(s)); - } - - snapshot = h2.snapshot(); - Assert.equal(snapshot.sum, 7); - Assert.deepEqual(snapshot.ranges, [0, 1, 2, 3, 4]); - Assert.deepEqual(snapshot.counts, [3, 2, 1, 1, 0]); -}); - -add_task(function* test_getHistogramById() { - try { - Telemetry.getHistogramById("nonexistent"); - do_throw("This can't happen"); - } catch (e) { - - } - var h = Telemetry.getHistogramById("CYCLE_COLLECTOR"); - var s = h.snapshot(); - do_check_eq(s.histogram_type, Telemetry.HISTOGRAM_EXPONENTIAL); - do_check_eq(s.min, 1); - do_check_eq(s.max, 10000); -}); - -add_task(function* test_getSlowSQL() { - var slow = Telemetry.slowSQL; - do_check_true(("mainThread" in slow) && ("otherThreads" in slow)); -}); - -add_task(function* test_getWebrtc() { - var webrtc = Telemetry.webrtcStats; - do_check_true("IceCandidatesStats" in webrtc); - var icestats = webrtc.IceCandidatesStats; - do_check_true("webrtc" in icestats); -}); - -// Check that telemetry doesn't record in private mode -add_task(function* test_privateMode() { - var h = Telemetry.getHistogramById("TELEMETRY_TEST_BOOLEAN"); - var orig = h.snapshot(); - Telemetry.canRecordExtended = false; - h.add(1); - do_check_eq(uneval(orig), uneval(h.snapshot())); - Telemetry.canRecordExtended = true; - h.add(1); - do_check_neq(uneval(orig), uneval(h.snapshot())); -}); - -// Check that telemetry records only when it is suppose to. -add_task(function* test_histogramRecording() { - // Check that no histogram is recorded if both base and extended recording are off. - Telemetry.canRecordBase = false; - Telemetry.canRecordExtended = false; - - let h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTOUT"); - h.clear(); - let orig = h.snapshot(); - h.add(1); - Assert.equal(orig.sum, h.snapshot().sum); - - // Check that only base histograms are recorded. - Telemetry.canRecordBase = true; - h.add(1); - Assert.equal(orig.sum + 1, h.snapshot().sum, - "Histogram value should have incremented by 1 due to recording."); - - // Extended histograms should not be recorded. - h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTIN"); - orig = h.snapshot(); - h.add(1); - Assert.equal(orig.sum, h.snapshot().sum, - "Histograms should be equal after recording."); - - // Runtime created histograms should not be recorded. - h = Telemetry.getHistogramById("TELEMETRY_TEST_BOOLEAN"); - orig = h.snapshot(); - h.add(1); - Assert.equal(orig.sum, h.snapshot().sum, - "Histograms should be equal after recording."); - - // Check that extended histograms are recorded when required. - Telemetry.canRecordExtended = true; - - h.add(1); - Assert.equal(orig.sum + 1, h.snapshot().sum, - "Runtime histogram value should have incremented by 1 due to recording."); - - h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTIN"); - orig = h.snapshot(); - h.add(1); - Assert.equal(orig.sum + 1, h.snapshot().sum, - "Histogram value should have incremented by 1 due to recording."); - - // Check that base histograms are still being recorded. - h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTOUT"); - h.clear(); - orig = h.snapshot(); - h.add(1); - Assert.equal(orig.sum + 1, h.snapshot().sum, - "Histogram value should have incremented by 1 due to recording."); -}); - -add_task(function* test_addons() { - var addon_id = "testing-addon"; - var fake_addon_id = "fake-addon"; - var name1 = "testing-histogram1"; - var register = Telemetry.registerAddonHistogram; - expect_success(() => - register(addon_id, name1, Telemetry.HISTOGRAM_LINEAR, 1, 5, 6)); - // Can't register the same histogram multiple times. - expect_fail(() => - register(addon_id, name1, Telemetry.HISTOGRAM_LINEAR, 1, 5, 6)); - // Make sure we can't get at it with another name. - expect_fail(() => Telemetry.getAddonHistogram(fake_addon_id, name1)); - - // Check for reflection capabilities. - var h1 = Telemetry.getAddonHistogram(addon_id, name1); - // Verify that although we've created storage for it, we don't reflect it into JS. - var snapshots = Telemetry.addonHistogramSnapshots; - do_check_false(name1 in snapshots[addon_id]); - h1.add(1); - h1.add(3); - var s1 = h1.snapshot(); - do_check_eq(s1.histogram_type, Telemetry.HISTOGRAM_LINEAR); - do_check_eq(s1.min, 1); - do_check_eq(s1.max, 5); - do_check_eq(s1.counts[1], 1); - do_check_eq(s1.counts[3], 1); - - var name2 = "testing-histogram2"; - expect_success(() => - register(addon_id, name2, Telemetry.HISTOGRAM_LINEAR, 2, 4, 4)); - - var h2 = Telemetry.getAddonHistogram(addon_id, name2); - h2.add(2); - h2.add(3); - var s2 = h2.snapshot(); - do_check_eq(s2.histogram_type, Telemetry.HISTOGRAM_LINEAR); - do_check_eq(s2.min, 2); - do_check_eq(s2.max, 4); - do_check_eq(s2.counts[1], 1); - do_check_eq(s2.counts[2], 1); - - // Check that we can register histograms for a different addon with - // identical names. - var extra_addon = "testing-extra-addon"; - expect_success(() => - register(extra_addon, name1, Telemetry.HISTOGRAM_BOOLEAN)); - - // Check that we can register flag histograms. - var flag_addon = "testing-flag-addon"; - var flag_histogram = "flag-histogram"; - expect_success(() => - register(flag_addon, flag_histogram, Telemetry.HISTOGRAM_FLAG)); - expect_success(() => - register(flag_addon, name2, Telemetry.HISTOGRAM_LINEAR, 2, 4, 4)); - - // Check that we reflect registered addons and histograms. - snapshots = Telemetry.addonHistogramSnapshots; - do_check_true(addon_id in snapshots) - do_check_true(extra_addon in snapshots); - do_check_true(flag_addon in snapshots); - - // Check that we have data for our created histograms. - do_check_true(name1 in snapshots[addon_id]); - do_check_true(name2 in snapshots[addon_id]); - var s1_alt = snapshots[addon_id][name1]; - var s2_alt = snapshots[addon_id][name2]; - do_check_eq(s1_alt.min, s1.min); - do_check_eq(s1_alt.max, s1.max); - do_check_eq(s1_alt.histogram_type, s1.histogram_type); - do_check_eq(s2_alt.min, s2.min); - do_check_eq(s2_alt.max, s2.max); - do_check_eq(s2_alt.histogram_type, s2.histogram_type); - - // Even though we've registered it, it shouldn't show up until data is added to it. - do_check_false(name1 in snapshots[extra_addon]); - - // Flag histograms should show up automagically. - do_check_true(flag_histogram in snapshots[flag_addon]); - do_check_false(name2 in snapshots[flag_addon]); - - // Check that we can remove addon histograms. - Telemetry.unregisterAddonHistograms(addon_id); - snapshots = Telemetry.addonHistogramSnapshots; - do_check_false(addon_id in snapshots); - // Make sure other addons are unaffected. - do_check_true(extra_addon in snapshots); -}); - -add_task(function* test_expired_histogram() { - var test_expired_id = "TELEMETRY_TEST_EXPIRED"; - var dummy = Telemetry.getHistogramById(test_expired_id); - var rh = Telemetry.registeredHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, []); - Assert.ok(!!rh); - - dummy.add(1); - - do_check_eq(Telemetry.histogramSnapshots["__expired__"], undefined); - do_check_eq(Telemetry.histogramSnapshots[test_expired_id], undefined); - do_check_eq(rh[test_expired_id], undefined); -}); - -add_task(function* test_keyed_histogram() { - // Check that invalid names get rejected. - - let threw = false; - try { - Telemetry.getKeyedHistogramById("test::unknown histogram", "never", Telemetry.HISTOGRAM_BOOLEAN); - } catch (e) { - // This should throw as it is an unknown ID - threw = true; - } - Assert.ok(threw, "getKeyedHistogramById should have thrown"); -}); - -add_task(function* test_keyed_boolean_histogram() { - const KEYED_ID = "TELEMETRY_TEST_KEYED_BOOLEAN"; - let KEYS = numberRange(0, 2).map(i => "key" + (i + 1)); - KEYS.push("漢語"); - let histogramBase = { - "min": 1, - "max": 2, - "histogram_type": 2, - "sum": 1, - "ranges": [0, 1, 2], - "counts": [0, 1, 0] - }; - let testHistograms = numberRange(0, 3).map(i => JSON.parse(JSON.stringify(histogramBase))); - let testKeys = []; - let testSnapShot = {}; - - let h = Telemetry.getKeyedHistogramById(KEYED_ID); - for (let i=0; i<2; ++i) { - let key = KEYS[i]; - h.add(key, true); - testSnapShot[key] = testHistograms[i]; - testKeys.push(key); - - Assert.deepEqual(h.keys().sort(), testKeys); - Assert.deepEqual(h.snapshot(), testSnapShot); - } - - h = Telemetry.getKeyedHistogramById(KEYED_ID); - Assert.deepEqual(h.keys().sort(), testKeys); - Assert.deepEqual(h.snapshot(), testSnapShot); - - let key = KEYS[2]; - h.add(key, false); - testKeys.push(key); - testSnapShot[key] = testHistograms[2]; - testSnapShot[key].sum = 0; - testSnapShot[key].counts = [1, 0, 0]; - Assert.deepEqual(h.keys().sort(), testKeys); - Assert.deepEqual(h.snapshot(), testSnapShot); - - let allSnapshots = Telemetry.keyedHistogramSnapshots; - Assert.deepEqual(allSnapshots[KEYED_ID], testSnapShot); - - h.clear(); - Assert.deepEqual(h.keys(), []); - Assert.deepEqual(h.snapshot(), {}); -}); - -add_task(function* test_keyed_count_histogram() { - const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT"; - const KEYS = numberRange(0, 5).map(i => "key" + (i + 1)); - let histogramBase = { - "min": 1, - "max": 2, - "histogram_type": 4, - "sum": 0, - "ranges": [0, 1, 2], - "counts": [1, 0, 0] - }; - let testHistograms = numberRange(0, 5).map(i => JSON.parse(JSON.stringify(histogramBase))); - let testKeys = []; - let testSnapShot = {}; - - let h = Telemetry.getKeyedHistogramById(KEYED_ID); - for (let i=0; i<4; ++i) { - let key = KEYS[i]; - let value = i*2 + 1; - - for (let k=0; k<value; ++k) { - h.add(key); - } - testHistograms[i].counts[0] = value; - testHistograms[i].sum = value; - testSnapShot[key] = testHistograms[i]; - testKeys.push(key); - - Assert.deepEqual(h.keys().sort(), testKeys); - Assert.deepEqual(h.snapshot(key), testHistograms[i]); - Assert.deepEqual(h.snapshot(), testSnapShot); - } - - h = Telemetry.getKeyedHistogramById(KEYED_ID); - Assert.deepEqual(h.keys().sort(), testKeys); - Assert.deepEqual(h.snapshot(), testSnapShot); - - let key = KEYS[4]; - h.add(key); - testKeys.push(key); - testHistograms[4].counts[0] = 1; - testHistograms[4].sum = 1; - testSnapShot[key] = testHistograms[4]; - - Assert.deepEqual(h.keys().sort(), testKeys); - Assert.deepEqual(h.snapshot(), testSnapShot); - - let allSnapshots = Telemetry.keyedHistogramSnapshots; - Assert.deepEqual(allSnapshots[KEYED_ID], testSnapShot); - - h.clear(); - Assert.deepEqual(h.keys(), []); - Assert.deepEqual(h.snapshot(), {}); -}); - -add_task(function* test_keyed_flag_histogram() { - const KEYED_ID = "TELEMETRY_TEST_KEYED_FLAG"; - let h = Telemetry.getKeyedHistogramById(KEYED_ID); - - const KEY = "default"; - h.add(KEY, true); - - let testSnapshot = {}; - testSnapshot[KEY] = { - "min": 1, - "max": 2, - "histogram_type": 3, - "sum": 1, - "ranges": [0, 1, 2], - "counts": [0, 1, 0] - }; - - Assert.deepEqual(h.keys().sort(), [KEY]); - Assert.deepEqual(h.snapshot(), testSnapshot); - - let allSnapshots = Telemetry.keyedHistogramSnapshots; - Assert.deepEqual(allSnapshots[KEYED_ID], testSnapshot); - - h.clear(); - Assert.deepEqual(h.keys(), []); - Assert.deepEqual(h.snapshot(), {}); -}); - -add_task(function* test_keyed_histogram_recording() { - // Check that no histogram is recorded if both base and extended recording are off. - Telemetry.canRecordBase = false; - Telemetry.canRecordExtended = false; - - const TEST_KEY = "record_foo"; - let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT"); - h.clear(); - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 0); - - // Check that only base histograms are recorded. - Telemetry.canRecordBase = true; - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 1, - "The keyed histogram should record the correct value."); - - // Extended set keyed histograms should not be recorded. - h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTIN"); - h.clear(); - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 0, - "The keyed histograms should not record any data."); - - // Check that extended histograms are recorded when required. - Telemetry.canRecordExtended = true; - - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 1, - "The runtime keyed histogram should record the correct value."); - - h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTIN"); - h.clear(); - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 1, - "The keyed histogram should record the correct value."); - - // Check that base histograms are still being recorded. - h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT"); - h.clear(); - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 1); -}); - -add_task(function* test_histogram_recording_enabled() { - Telemetry.canRecordBase = true; - Telemetry.canRecordExtended = true; - - // Check that a "normal" histogram respects recording-enabled on/off - var h = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT"); - var orig = h.snapshot(); - - h.add(1); - Assert.equal(orig.sum + 1, h.snapshot().sum, - "add should record by default."); - - // Check that when recording is disabled - add is ignored - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_COUNT", false); - h.add(1); - Assert.equal(orig.sum + 1, h.snapshot().sum, - "When recording is disabled add should not record."); - - // Check that we're back to normal after recording is enabled - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_COUNT", true); - h.add(1); - Assert.equal(orig.sum + 2, h.snapshot().sum, - "When recording is re-enabled add should record."); - - // Check that we're correctly accumulating values other than 1. - h.clear(); - h.add(3); - Assert.equal(3, h.snapshot().sum, "Recording counts greater than 1 should work."); - - // Check that a histogram with recording disabled by default behaves correctly - h = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT_INIT_NO_RECORD"); - orig = h.snapshot(); - - h.add(1); - Assert.equal(orig.sum, h.snapshot().sum, - "When recording is disabled by default, add should not record by default."); - - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_COUNT_INIT_NO_RECORD", true); - h.add(1); - Assert.equal(orig.sum + 1, h.snapshot().sum, - "When recording is enabled add should record."); - - // Restore to disabled - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_COUNT_INIT_NO_RECORD", false); - h.add(1); - Assert.equal(orig.sum + 1, h.snapshot().sum, - "When recording is disabled add should not record."); -}); - -add_task(function* test_keyed_histogram_recording_enabled() { - Telemetry.canRecordBase = true; - Telemetry.canRecordExtended = true; - - // Check RecordingEnabled for keyed histograms which are recording by default - const TEST_KEY = "record_foo"; - let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT"); - - h.clear(); - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 1, - "Keyed histogram add should record by default"); - - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT", false); - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 1, - "Keyed histogram add should not record when recording is disabled"); - - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT", true); - h.clear(); - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 1, - "Keyed histogram add should record when recording is re-enabled"); - - // Check that a histogram with recording disabled by default behaves correctly - h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD"); - h.clear(); - - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 0, - "Keyed histogram add should not record by default for histograms which don't record by default"); - - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD", true); - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 1, - "Keyed histogram add should record when recording is enabled"); - - // Restore to disabled - Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD", false); - h.add(TEST_KEY, 1); - Assert.equal(h.snapshot(TEST_KEY).sum, 1, - "Keyed histogram add should not record when recording is disabled"); -}); - -add_task(function* test_datasets() { - // Check that datasets work as expected. - - const RELEASE_CHANNEL_OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT; - const RELEASE_CHANNEL_OPTIN = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN; - - // Histograms should default to the extended dataset - let h = Telemetry.getHistogramById("TELEMETRY_TEST_FLAG"); - Assert.equal(h.dataset(), RELEASE_CHANNEL_OPTIN); - h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_FLAG"); - Assert.equal(h.dataset(), RELEASE_CHANNEL_OPTIN); - - // Check test histograms with explicit dataset definitions - h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTIN"); - Assert.equal(h.dataset(), RELEASE_CHANNEL_OPTIN); - h = Telemetry.getHistogramById("TELEMETRY_TEST_RELEASE_OPTOUT"); - Assert.equal(h.dataset(), RELEASE_CHANNEL_OPTOUT); - h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTIN"); - Assert.equal(h.dataset(), RELEASE_CHANNEL_OPTIN); - h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT"); - Assert.equal(h.dataset(), RELEASE_CHANNEL_OPTOUT); - - // Check that registeredHistogram works properly - let registered = Telemetry.registeredHistograms(RELEASE_CHANNEL_OPTIN, []); - registered = new Set(registered); - Assert.ok(registered.has("TELEMETRY_TEST_FLAG")); - Assert.ok(registered.has("TELEMETRY_TEST_RELEASE_OPTIN")); - Assert.ok(registered.has("TELEMETRY_TEST_RELEASE_OPTOUT")); - registered = Telemetry.registeredHistograms(RELEASE_CHANNEL_OPTOUT, []); - registered = new Set(registered); - Assert.ok(!registered.has("TELEMETRY_TEST_FLAG")); - Assert.ok(!registered.has("TELEMETRY_TEST_RELEASE_OPTIN")); - Assert.ok(registered.has("TELEMETRY_TEST_RELEASE_OPTOUT")); - - // Check that registeredKeyedHistograms works properly - registered = Telemetry.registeredKeyedHistograms(RELEASE_CHANNEL_OPTIN, []); - registered = new Set(registered); - Assert.ok(registered.has("TELEMETRY_TEST_KEYED_FLAG")); - Assert.ok(registered.has("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT")); - registered = Telemetry.registeredKeyedHistograms(RELEASE_CHANNEL_OPTOUT, []); - registered = new Set(registered); - Assert.ok(!registered.has("TELEMETRY_TEST_KEYED_FLAG")); - Assert.ok(registered.has("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT")); -}); - -add_task({ - skip_if: () => gIsAndroid -}, -function* test_subsession() { - const ID = "TELEMETRY_TEST_COUNT"; - const FLAG = "TELEMETRY_TEST_FLAG"; - let h = Telemetry.getHistogramById(ID); - let flag = Telemetry.getHistogramById(FLAG); - - // Both original and duplicate should start out the same. - h.clear(); - let snapshot = Telemetry.histogramSnapshots; - let subsession = Telemetry.snapshotSubsessionHistograms(); - Assert.ok(!(ID in snapshot)); - Assert.ok(!(ID in subsession)); - - // They should instantiate and pick-up the count. - h.add(1); - snapshot = Telemetry.histogramSnapshots; - subsession = Telemetry.snapshotSubsessionHistograms(); - Assert.ok(ID in snapshot); - Assert.ok(ID in subsession); - Assert.equal(snapshot[ID].sum, 1); - Assert.equal(subsession[ID].sum, 1); - - // They should still reset properly. - h.clear(); - snapshot = Telemetry.histogramSnapshots; - subsession = Telemetry.snapshotSubsessionHistograms(); - Assert.ok(!(ID in snapshot)); - Assert.ok(!(ID in subsession)); - - // Both should instantiate and pick-up the count. - h.add(1); - snapshot = Telemetry.histogramSnapshots; - subsession = Telemetry.snapshotSubsessionHistograms(); - Assert.equal(snapshot[ID].sum, 1); - Assert.equal(subsession[ID].sum, 1); - - // Check that we are able to only reset the duplicate histogram. - h.clear(true); - snapshot = Telemetry.histogramSnapshots; - subsession = Telemetry.snapshotSubsessionHistograms(); - Assert.ok(ID in snapshot); - Assert.ok(ID in subsession); - Assert.equal(snapshot[ID].sum, 1); - Assert.equal(subsession[ID].sum, 0); - - // Both should register the next count. - h.add(1); - snapshot = Telemetry.histogramSnapshots; - subsession = Telemetry.snapshotSubsessionHistograms(); - Assert.equal(snapshot[ID].sum, 2); - Assert.equal(subsession[ID].sum, 1); - - // Retrieve a subsession snapshot and pass the flag to - // clear subsession histograms too. - h.clear(); - flag.clear(); - h.add(1); - flag.add(1); - snapshot = Telemetry.histogramSnapshots; - subsession = Telemetry.snapshotSubsessionHistograms(true); - Assert.ok(ID in snapshot); - Assert.ok(ID in subsession); - Assert.ok(FLAG in snapshot); - Assert.ok(FLAG in subsession); - Assert.equal(snapshot[ID].sum, 1); - Assert.equal(subsession[ID].sum, 1); - Assert.equal(snapshot[FLAG].sum, 1); - Assert.equal(subsession[FLAG].sum, 1); - - // The next subsesssion snapshot should show the histograms - // got reset. - snapshot = Telemetry.histogramSnapshots; - subsession = Telemetry.snapshotSubsessionHistograms(); - Assert.ok(ID in snapshot); - Assert.ok(ID in subsession); - Assert.ok(FLAG in snapshot); - Assert.ok(FLAG in subsession); - Assert.equal(snapshot[ID].sum, 1); - Assert.equal(subsession[ID].sum, 0); - Assert.equal(snapshot[FLAG].sum, 1); - Assert.equal(subsession[FLAG].sum, 0); -}); - -add_task({ - skip_if: () => gIsAndroid -}, -function* test_keyed_subsession() { - let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_FLAG"); - const KEY = "foo"; - - // Both original and subsession should start out the same. - h.clear(); - Assert.ok(!(KEY in h.snapshot())); - Assert.ok(!(KEY in h.subsessionSnapshot())); - Assert.equal(h.snapshot(KEY).sum, 0); - Assert.equal(h.subsessionSnapshot(KEY).sum, 0); - - // Both should register the flag. - h.add(KEY, 1); - Assert.ok(KEY in h.snapshot()); - Assert.ok(KEY in h.subsessionSnapshot()); - Assert.equal(h.snapshot(KEY).sum, 1); - Assert.equal(h.subsessionSnapshot(KEY).sum, 1); - - // Check that we are able to only reset the subsession histogram. - h.clear(true); - Assert.ok(KEY in h.snapshot()); - Assert.ok(!(KEY in h.subsessionSnapshot())); - Assert.equal(h.snapshot(KEY).sum, 1); - Assert.equal(h.subsessionSnapshot(KEY).sum, 0); - - // Setting the flag again should make both match again. - h.add(KEY, 1); - Assert.ok(KEY in h.snapshot()); - Assert.ok(KEY in h.subsessionSnapshot()); - Assert.equal(h.snapshot(KEY).sum, 1); - Assert.equal(h.subsessionSnapshot(KEY).sum, 1); - - // Check that "snapshot and clear" works properly. - let snapshot = h.snapshot(); - let subsession = h.snapshotSubsessionAndClear(); - Assert.ok(KEY in snapshot); - Assert.ok(KEY in subsession); - Assert.equal(snapshot[KEY].sum, 1); - Assert.equal(subsession[KEY].sum, 1); - - subsession = h.subsessionSnapshot(); - Assert.ok(!(KEY in subsession)); - Assert.equal(h.subsessionSnapshot(KEY).sum, 0); -}); diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.ini b/toolkit/components/telemetry/tests/unit/xpcshell.ini deleted file mode 100644 index 224516f57..000000000 --- a/toolkit/components/telemetry/tests/unit/xpcshell.ini +++ /dev/null @@ -1,61 +0,0 @@ -[DEFAULT] -head = head.js -tail = -firefox-appdir = browser -# The *.xpi files are only needed for test_TelemetryEnvironment.js, but -# xpcshell fails to install tests if we move them under the test entry. -support-files = - ../search/chrome.manifest - ../search/searchTest.jar - dictionary.xpi - experiment.xpi - extension.xpi - extension-2.xpi - engine.xml - system.xpi - restartless.xpi - theme.xpi -generated-files = - dictionary.xpi - experiment.xpi - extension.xpi - extension-2.xpi - system.xpi - restartless.xpi - theme.xpi - -[test_nsITelemetry.js] -[test_SubsessionChaining.js] -tags = addons -[test_TelemetryEnvironment.js] -skip-if = os == "android" -tags = addons -[test_PingAPI.js] -skip-if = os == "android" -[test_TelemetryFlagClear.js] -[test_TelemetryLateWrites.js] -[test_TelemetryLockCount.js] -[test_TelemetryLog.js] -[test_TelemetryController.js] -tags = addons -[test_TelemetryController_idle.js] -[test_TelemetryControllerShutdown.js] -tags = addons -[test_TelemetryControllerBuildID.js] -[test_TelemetrySendOldPings.js] -skip-if = os == "android" # Disabled due to intermittent orange on Android -tags = addons -[test_TelemetrySession.js] -tags = addons -[test_ThreadHangStats.js] -run-sequentially = Bug 1046307, test can fail intermittently when CPU load is high -[test_TelemetrySend.js] -[test_ChildHistograms.js] -skip-if = os == "android" -tags = addons -[test_TelemetryReportingPolicy.js] -tags = addons -[test_TelemetryScalars.js] -[test_TelemetryTimestamps.js] -skip-if = toolkit == 'android' -[test_TelemetryEvents.js] |