/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * This file only contains the |test_abortedSessionQueued| test. This needs * to be in a separate, stand-alone file since we're initializing Telemetry * twice, in a non-standard way to simulate incorrect shutdowns. Doing this * in other files might interfere with the other tests. */ const { TelemetryStorage } = ChromeUtils.importESModule( "resource://gre/modules/TelemetryStorage.sys.mjs" ); const DATAREPORTING_DIR = "datareporting"; const ABORTED_PING_FILE_NAME = "aborted-session-ping"; const ABORTED_SESSION_UPDATE_INTERVAL_MS = 5 * 60 * 1000; const PING_TYPE_MAIN = "main"; const REASON_ABORTED_SESSION = "aborted-session"; const TEST_PING_TYPE = "test-ping-type"; ChromeUtils.defineLazyGetter(this, "DATAREPORTING_PATH", function () { return PathUtils.join(PathUtils.profileDir, DATAREPORTING_DIR); }); function sendPing() { if (PingServer.started) { TelemetrySend.setServer("http://localhost:" + PingServer.port); } else { TelemetrySend.setServer("http://doesnotexist"); } let options = { addClientId: true, addEnvironment: true, }; return TelemetryController.submitExternalPing(TEST_PING_TYPE, {}, options); } add_task(async function test_setup() { do_get_profile(); PingServer.start(); Services.prefs.setCharPref( TelemetryUtils.Preferences.Server, "http://localhost:" + PingServer.port ); }); add_task(async function test_abortedSessionQueued() { const ABORTED_FILE = PathUtils.join( DATAREPORTING_PATH, ABORTED_PING_FILE_NAME ); // Make sure the aborted sessions directory does not exist to test its creation. await IOUtils.remove(DATAREPORTING_PATH, { ignoreAbsent: true, recursive: 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), () => {} ); await TelemetryController.testReset(); Assert.ok( await IOUtils.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. await schedulerTickCallback(); // Check that the aborted session is due at the correct time. Assert.ok( await IOUtils.exists(ABORTED_FILE), "There must be an aborted session ping." ); await TelemetryStorage.testClearPendingPings(); PingServer.clearRequests(); await TelemetryController.testReset(); Assert.ok( !(await IOUtils.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. await TelemetryController.testReset(); // We should have received an aborted-session ping. const receivedPing = await 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" ); await TelemetryController.testShutdown(); }); /* * An aborted-session ping might have been written when Telemetry upload was disabled and * the profile had a canary client ID. * These pings should not be sent out at a later point when Telemetry is enabled again. */ add_task(async function test_abortedSession_canary_clientid() { const ABORTED_FILE = PathUtils.join( DATAREPORTING_PATH, ABORTED_PING_FILE_NAME ); // Make sure the aborted sessions directory does not exist to test its creation. await IOUtils.remove(DATAREPORTING_PATH, { ignoreAbsent: true, recursive: 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), () => {} ); await TelemetryController.testReset(); Assert.ok( await IOUtils.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. await schedulerTickCallback(); // Check that the aborted session is due at the correct time. Assert.ok( await IOUtils.exists(ABORTED_FILE), "There must be an aborted session ping." ); // Set clientID in aborted-session ping to canary value let abortedPing = await IOUtils.readJSON(ABORTED_FILE); abortedPing.clientId = TelemetryUtils.knownClientID; await IOUtils.writeJSON(ABORTED_FILE, abortedPing); await TelemetryStorage.testClearPendingPings(); PingServer.clearRequests(); await TelemetryController.testReset(); Assert.ok( !(await IOUtils.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. await TelemetryController.testReset(); // Trigger a test ping, so we can verify the server received something. sendPing(); // We should have received an aborted-session ping. const receivedPing = await PingServer.promiseNextPing(); Assert.equal( receivedPing.type, TEST_PING_TYPE, "Should have received test ping" ); await TelemetryController.testShutdown(); }); add_task(async function stopServer() { await PingServer.stop(); });