diff options
Diffstat (limited to 'toolkit/components/telemetry/tests/unit/test_TelemetryController.js')
-rw-r--r-- | toolkit/components/telemetry/tests/unit/test_TelemetryController.js | 507 |
1 files changed, 0 insertions, 507 deletions
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(); -}); |