/* 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"; ChromeUtils.import("resource://gre/modules/TelemetryController.jsm", this); ChromeUtils.import("resource://testing-common/ContentTaskUtils.jsm", this); ChromeUtils.import("resource://testing-common/MockRegistrar.jsm", this); ChromeUtils.import("resource://gre/modules/TelemetrySession.jsm", this); ChromeUtils.import("resource://gre/modules/TelemetrySend.jsm", this); ChromeUtils.import("resource://gre/modules/TelemetryStorage.jsm", this); ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm", this); ChromeUtils.import("resource://gre/modules/Services.jsm", this); ChromeUtils.import("resource://gre/modules/osfile.jsm", this); ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this); ChromeUtils.defineModuleGetter(this, "TelemetryHealthPing", "resource://gre/modules/HealthPing.jsm"); XPCOMUtils.defineLazyServiceGetter(Services, "cookies", "@mozilla.org/cookieService;1", "nsICookieService"); 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 = async function(pingIds) { let allFound = true; for (let id of pingIds) { const path = OS.Path.join(TelemetryStorage.pingDirectoryPath, id); let exists = false; try { exists = await OS.File.exists(path); } catch (ex) {} if (!exists) { dump("checkPingsSaved - failed to find ping: " + path + "\n"); allFound = false; } } return allFound; }; function histogramValueCount(h) { return Object.values(h.values).reduce((a, b) => a + b, 0); } add_task(async function test_setup() { // Trigger a proper telemetry init. do_get_profile(true); // Make sure we don't generate unexpected pings due to pref changes. await setEmptyPrefWatchlist(); Services.prefs.setBoolPref(TelemetryUtils.Preferences.HealthPingEnabled, true); TelemetryStopwatch.setTestModeEnabled(true); }); // Test the ping sending logic. add_task(async 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 = await TelemetryController.submitExternalPing(TEST_TYPE_A, {}); await 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 = await TelemetryController.submitExternalPing(TEST_TYPE_B, {}); await 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().values, {}, "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(); Services.prefs.setStringPref(TelemetryUtils.Preferences.Server, "http://localhost:" + PingServer.port); let timerPromise = waitForTimer(); await TelemetryController.testReset(); let [pingSendTimerCallback, pingSendTimeout] = await 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 = await 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().values, {0: 0, 1: 10, 2: 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] = await timerPromise; pings = await 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)); await pingSendTimerCallback(); pings = await 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"); await TelemetrySend.testWaitOnOutgoingPings(); PingServer.resetPingHandler(); // Restore the default ping id generator. fakeGeneratePingId(() => TelemetryUtils.generateUUID()); }); add_task(async function test_sendDateHeader() { fakeNow(new Date(Date.UTC(2011, 1, 1, 11, 0, 0))); await TelemetrySend.reset(); let pingId = await TelemetryController.submitExternalPing("test-send-date-header", {}); let req = await 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(async 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); await 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(); await TelemetryController.submitExternalPing(TEST_TYPE_C, {}); let [pingSendTimerCallback, pingSendTimeout] = await 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(); await callback(); [pingSendTimerCallback, pingSendTimeout] = await timerPromise; ++sendAttempts; } timerPromise = waitForTimer(); await pingSendTimerCallback(); [pingSendTimerCallback, pingSendTimeout] = await timerPromise; Assert.equal(pingSendTimeout, MAX_BACKOFF_TIMEOUT, "Tick timeout should be capped"); ++sendAttempts; Assert.deepEqual(histSuccess.snapshot().values, {0: sendAttempts, 1: 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.greaterOrEqual(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(); await TelemetryController.submitExternalPing(TEST_TYPE_D, {}); [pingSendTimerCallback, pingSendTimeout] = await 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(); await TelemetryController.submitExternalPing(TEST_TYPE_E, {}); let pings = await 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"); await TelemetrySend.testWaitOnOutgoingPings(); Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings left"); Assert.deepEqual(histSuccess.snapshot().values, {0: sendAttempts, 1: 3, 2: 0}, "Should have recorded sending failure in histograms."); Assert.greaterOrEqual(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."); // Restore the default ping id generator. fakeGeneratePingId(() => TelemetryUtils.generateUUID()); }); add_task(async 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(); } // Submit a ping of a normal size and check that we don't count it in the histogram. await TelemetryController.submitExternalPing(TEST_PING_TYPE, { test: "test" }); await TelemetrySend.testWaitOnOutgoingPings(); await PingServer.promiseNextPing(); 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().values, {0: 0, 1: 1, 2: 0}, "Should have recorded sending success."); Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 1, "Should have recorded send success time."); Assert.greaterOrEqual(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. TelemetryHealthPing.testReset(); // Ensure next ping has a 2 MB gzipped payload. fakeGzipCompressStringForNextPing(2 * 1024 * 1024); const OVERSIZED_PAYLOAD = {"data": "empty on purpose - policy takes care of size"}; await TelemetryController.submitExternalPing(TEST_PING_TYPE, OVERSIZED_PAYLOAD); await TelemetrySend.testWaitOnOutgoingPings(); let ping = await PingServer.promiseNextPing(); Assert.equal(ping.type, TelemetryHealthPing.HEALTH_PING_TYPE, "Should have received a health ping."); Assert.equal(ping.payload.reason, TelemetryHealthPing.Reason.IMMEDIATE, "Health ping should have the right reason."); Assert.deepEqual(ping.payload[TelemetryHealthPing.FailureType.DISCARDED_FOR_SIZE], {[TEST_PING_TYPE]: 1}, "Should have recorded correct type of oversized ping."); Assert.deepEqual(ping.payload.os, TelemetryHealthPing.OsInfo, "Should have correct os info."); Assert.equal(histSizeExceeded.snapshot().sum, 1, "Telemetry must report 1 oversized ping submitted."); Assert.equal(histDiscardedSize.snapshot().values[2], 1, "Telemetry must report a 2MB, oversized, ping submitted."); Assert.deepEqual(histSuccess.snapshot().values, {0: 0, 1: 2, 2: 0}, "Should have recorded sending success."); Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 2, "Should have recorded send success time."); Assert.greaterOrEqual(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(async function test_largeButWithinLimit() { const TEST_PING_TYPE = "test-ping-type"; let histSuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); histSuccess.clear(); // Next ping will have a 900KB gzip payload. fakeGzipCompressStringForNextPing(900 * 1024); const LARGE_PAYLOAD = {"data": "empty on purpose - policy takes care of size"}; await TelemetryController.submitExternalPing(TEST_PING_TYPE, LARGE_PAYLOAD); await TelemetrySend.testWaitOnOutgoingPings(); await PingServer.promiseNextRequest(); Assert.deepEqual(histSuccess.snapshot().values, {0: 0, 1: 1, 2: 0}, "Should have sent large ping."); }); add_task(async function test_evictedOnServerErrors() { const TEST_TYPE = "test-evicted"; await 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 = await TelemetryController.submitExternalPing(TEST_TYPE, {}); await TelemetrySend.testWaitOnOutgoingPings(); Assert.equal(histEvicted.snapshot().sum, 1, "Telemetry must report a ping evicted due to server errors"); Assert.deepEqual(histSuccess.snapshot().values, {0: 0, 1: 1, 2: 0}); Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 1); Assert.greaterOrEqual(histSendTimeSuccess.snapshot().sum, 0); Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0); // The ping should not be persisted. await Assert.rejects(TelemetryStorage.loadPendingPing(pingId), /TelemetryStorage.loadPendingPing - no ping with id/, "The ping must not be persisted."); // Reset the ping handler and submit a new ping. PingServer.resetPingHandler(); pingId = await TelemetryController.submitExternalPing(TEST_TYPE, {}); let ping = await PingServer.promiseNextPings(1); Assert.equal(ping[0].id, pingId, "The correct ping must be received"); // We should not have updated the error histogram. await TelemetrySend.testWaitOnOutgoingPings(); Assert.equal(histEvicted.snapshot().sum, 1, "Telemetry must report only one ping evicted due to server errors"); Assert.deepEqual(histSuccess.snapshot().values, {0: 0, 1: 2, 2: 0}); Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 2); Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0); }); add_task(async function test_tooLateToSend() { Assert.ok(true, "TEST BEGIN"); const TEST_TYPE = "test-too-late-to-send"; await TelemetrySend.reset(); PingServer.start(); PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now")); Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings yet"); TelemetrySend.testTooLateToSend(true); const id = await TelemetryController.submitExternalPing(TEST_TYPE, {}); // Triggering a shutdown should persist the pings await TelemetrySend.shutdown(); const pendingPings = TelemetryStorage.getPendingPingList(); Assert.equal(pendingPings.length, 1, "Should have a pending ping in storage"); Assert.equal(pendingPings[0].id, id, "Should have pended our test's ping"); Assert.equal(Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE_TYPE").snapshot().values[7], 1, "Should have registered the failed attempt to send"); await TelemetryStorage.reset(); Assert.equal(TelemetrySend.pendingPingCount, 0, "Should clean up after yourself"); }); // Test that the current, non-persisted pending pings are properly saved on shutdown. add_task(async function test_persistCurrentPingsOnShutdown() { const TEST_TYPE = "test-persistCurrentPingsOnShutdown"; const PING_COUNT = 5; await 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. await TelemetrySend.shutdown(); Assert.ok((await checkPingsSaved(ids)), "All pending pings should have been persisted"); // After a restart the pings should have been found when scanning. await TelemetrySend.reset(); Assert.equal(TelemetrySend.pendingPingCount, PING_COUNT, "Should have the correct pending ping count"); // Restore the default ping id generator. fakeGeneratePingId(() => TelemetryUtils.generateUUID()); }); add_task(async function test_sendCheckOverride() { const TEST_PING_TYPE = "test-sendCheckOverride"; // Clear any pending pings. await TelemetryController.testShutdown(); await TelemetryStorage.testClearPendingPings(); // Enable the ping server. PingServer.start(); Services.prefs.setStringPref(TelemetryUtils.Preferences.Server, "http://localhost:" + PingServer.port); // Start Telemetry and disable the test-mode so pings don't get // sent unless we enable the override. await TelemetryController.testReset(); // Submit a test ping and make sure it doesn't get sent. We only do // that if we're on unofficial builds: pings will always get sent otherwise. if (!Services.telemetry.isOfficialTelemetry) { TelemetrySend.setTestModeEnabled(false); PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now")); await TelemetryController.submitExternalPing(TEST_PING_TYPE, { test: "test" }); Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings"); } // Enable the override and try to send again. Services.prefs.setBoolPref(TelemetryUtils.Preferences.OverrideOfficialCheck, true); PingServer.resetPingHandler(); await TelemetrySend.reset(); await TelemetryController.submitExternalPing(TEST_PING_TYPE, { test: "test" }); // Make sure we received the ping. const ping = await PingServer.promiseNextPing(); Assert.equal(ping.type, TEST_PING_TYPE, "Must receive a ping of the expected type"); // Restore the test mode and disable the override. TelemetrySend.setTestModeEnabled(true); Services.prefs.clearUserPref(TelemetryUtils.Preferences.OverrideOfficialCheck); }); add_task(async function testCookies() { const TEST_TYPE = "test-cookies"; await TelemetrySend.reset(); PingServer.clearRequests(); let uri = Services.io.newURI("http://localhost:" + PingServer.port); Services.cookies.setCookieString(uri, null, "cookie-time=yes", null); const id = await TelemetryController.submitExternalPing(TEST_TYPE, {}); let foundit = false; while (!foundit) { var request = await PingServer.promiseNextRequest(); var ping = decodeRequestPayload(request); foundit = id === ping.id; } Assert.equal(id, ping.id, "We're testing the right ping's request, right?"); Assert.equal(false, request.hasHeader("Cookie"), "Request should not have Cookie header"); }); add_task(async function test_measurePingsSize() { const TEST_TYPE = "test-measure-ping-size"; let histSuccessPingSize = Telemetry.getHistogramById("TELEMETRY_SUCCESSFUL_SEND_PINGS_SIZE_KB"); let histFailedPingSize = Telemetry.getHistogramById("TELEMETRY_FAILED_SEND_PINGS_SIZE_KB"); for (let h of [histSuccessPingSize, histFailedPingSize]) { h.clear(); } await TelemetryController.submitExternalPing(TEST_TYPE, {}); await TelemetrySend.testWaitOnOutgoingPings(); // Check that we recorded the ping sizes correctly into histograms. Assert.equal(histogramValueCount(histSuccessPingSize.snapshot()), 1, "Should have recorded 1 successful ping into histogram."); Assert.equal(histogramValueCount(histFailedPingSize.snapshot()), 0, "Should have recorded 0 failed ping into histogram."); // Submit the same ping a second time. await TelemetryController.submitExternalPing(TEST_TYPE, {}); await TelemetrySend.testWaitOnOutgoingPings(); // Check that we recorded the ping sizes correctly into histograms. Assert.equal(histogramValueCount(histSuccessPingSize.snapshot()), 2, "Should have recorded 2 successful ping into histogram."); Assert.equal(histogramValueCount(histFailedPingSize.snapshot()), 0, "Should have recorded 0 failed ping into histogram."); // Register a custom ping handler which will return 601. PingServer.registerPingHandler((req, res) => { res.setStatusLine(null, 601, "Not Implemented"); res.processAsync(); res.finish(); }); await TelemetryController.submitExternalPing(TEST_TYPE, {}); await ContentTaskUtils.waitForCondition(() => { return histogramValueCount(histFailedPingSize.snapshot()) > 0; }); // Check that we recorded the ping sizes correctly into histograms. Assert.equal(histogramValueCount(histSuccessPingSize.snapshot()), 2, "Should have recorded 2 successful ping into histogram."); Assert.equal(histogramValueCount(histFailedPingSize.snapshot()), 1, "Should have recorded 1 failed ping into histogram."); }); add_task(async function test_pref_observer() { // This test requires the presence of the crash reporter component. let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); if (!registrar.isContractIDRegistered("@mozilla.org/toolkit/crash-reporter;1")) { return; } await TelemetrySend.setup(true); const IS_UNIFIED_TELEMETRY = Services.prefs.getBoolPref(TelemetryUtils.Preferences.Unified, false); let origTelemetryEnabled = Services.prefs.getBoolPref(TelemetryUtils.Preferences.TelemetryEnabled); let origFhrUploadEnabled = Services.prefs.getBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled); if (!IS_UNIFIED_TELEMETRY) { Services.prefs.setBoolPref(TelemetryUtils.Preferences.TelemetryEnabled, true); } Services.prefs.setBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, true); function waitAnnotateCrashReport(expectedValue, trigger) { return new Promise(function(resolve, reject) { let keys = new Set(["TelemetryClientId", "TelemetryServerURL"]); let crs = { QueryInterface: ChromeUtils.generateQI([Ci.nsICrashReporter]), annotateCrashReport(key, value) { if (!keys.delete(key)) { MockRegistrar.unregister(gMockCrs); reject(Error(`Crash report annotation with unexpected key: "${key}".`)); } if (expectedValue && value == "") { MockRegistrar.unregister(gMockCrs); reject(Error("Crash report annotation without expected value.")); } if (keys.size == 0) { MockRegistrar.unregister(gMockCrs); resolve(); } }, removeCrashReportAnnotation(key) { if (!keys.delete(key)) { MockRegistrar.unregister(gMockCrs); } if (keys.size == 0) { MockRegistrar.unregister(gMockCrs); resolve(); } }, UpdateCrashEventsDir() { }, }; let gMockCrs = MockRegistrar.register("@mozilla.org/toolkit/crash-reporter;1", crs); registerCleanupFunction(function() { MockRegistrar.unregister(gMockCrs); }); trigger(); }); } await waitAnnotateCrashReport(!IS_UNIFIED_TELEMETRY, () => Services.prefs.setBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, false)); await waitAnnotateCrashReport(true, () => Services.prefs.setBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, true)); if (!IS_UNIFIED_TELEMETRY) { Services.prefs.setBoolPref(TelemetryUtils.Preferences.TelemetryEnabled, origTelemetryEnabled); } Services.prefs.setBoolPref(TelemetryUtils.Preferences.FhrUploadEnabled, origFhrUploadEnabled); }); add_task(async function cleanup() { await PingServer.stop(); });