summaryrefslogtreecommitdiffstats
path: root/toolkit/components/telemetry/tests/unit/test_HealthPing.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/telemetry/tests/unit/test_HealthPing.js')
-rw-r--r--toolkit/components/telemetry/tests/unit/test_HealthPing.js282
1 files changed, 282 insertions, 0 deletions
diff --git a/toolkit/components/telemetry/tests/unit/test_HealthPing.js b/toolkit/components/telemetry/tests/unit/test_HealthPing.js
new file mode 100644
index 0000000000..3ea288157e
--- /dev/null
+++ b/toolkit/components/telemetry/tests/unit/test_HealthPing.js
@@ -0,0 +1,282 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This tests the public Telemetry API for submitting Health pings.
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ TelemetryHealthPing: "resource://gre/modules/HealthPing.sys.mjs",
+});
+
+function checkHealthPingStructure(ping, expectedFailuresDict) {
+ let payload = ping.payload;
+ Assert.equal(
+ ping.type,
+ TelemetryHealthPing.HEALTH_PING_TYPE,
+ "Should have recorded a health ping."
+ );
+
+ for (let [key, value] of Object.entries(expectedFailuresDict)) {
+ Assert.deepEqual(
+ payload[key],
+ value,
+ "Should have recorded correct entry with key: " + key
+ );
+ }
+}
+
+function fakeHealthSchedulerTimer(set, clear) {
+ let { Policy } = ChromeUtils.importESModule(
+ "resource://gre/modules/HealthPing.sys.mjs"
+ );
+ Policy.setSchedulerTickTimeout = set;
+ Policy.clearSchedulerTickTimeout = clear;
+}
+
+add_setup(async function 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
+ );
+
+ await TelemetryController.testSetup();
+ PingServer.start();
+ TelemetrySend.setServer("http://localhost:" + PingServer.port);
+ Services.prefs.setStringPref(
+ TelemetryUtils.Preferences.Server,
+ "http://localhost:" + PingServer.port
+ );
+});
+
+registerCleanupFunction(async function cleanup() {
+ await PingServer.stop();
+});
+
+add_task(async function test_sendImmediately() {
+ PingServer.clearRequests();
+ TelemetryHealthPing.testReset();
+
+ await TelemetryHealthPing.recordSendFailure("testProblem");
+ let ping = await PingServer.promiseNextPing();
+ checkHealthPingStructure(ping, {
+ [TelemetryHealthPing.FailureType.SEND_FAILURE]: {
+ testProblem: 1,
+ },
+ os: TelemetryHealthPing.OsInfo,
+ reason: TelemetryHealthPing.Reason.IMMEDIATE,
+ });
+});
+
+add_task(async function test_sendOnDelay() {
+ PingServer.clearRequests();
+ TelemetryHealthPing.testReset();
+
+ // This first failure should immediately trigger a ping. After this, subsequent failures should be throttled.
+ await TelemetryHealthPing.recordSendFailure("testFailure");
+ let testPing = await PingServer.promiseNextPing();
+ Assert.equal(
+ testPing.type,
+ TelemetryHealthPing.HEALTH_PING_TYPE,
+ "Should have recorded a health ping."
+ );
+
+ // Retrieve delayed call back.
+ let pingSubmissionCallBack = null;
+ fakeHealthSchedulerTimer(
+ callBack => (pingSubmissionCallBack = callBack),
+ () => {}
+ );
+
+ // Record two failures, health ping must not be send now.
+ await TelemetryHealthPing.recordSendFailure("testFailure");
+ await TelemetryHealthPing.recordSendFailure("testFailure");
+
+ // Wait for sending delayed health ping.
+ await pingSubmissionCallBack();
+
+ let ping = await PingServer.promiseNextPing();
+ checkHealthPingStructure(ping, {
+ [TelemetryHealthPing.FailureType.SEND_FAILURE]: {
+ testFailure: 2,
+ },
+ os: TelemetryHealthPing.OsInfo,
+ reason: TelemetryHealthPing.Reason.DELAYED,
+ });
+});
+
+add_task(async function test_sendOverSizedPing() {
+ TelemetryHealthPing.testReset();
+ PingServer.clearRequests();
+ let OVER_SIZED_PING_TYPE = "over-sized-ping";
+ let overSizedData = generateRandomString(2 * 1024 * 1024);
+
+ await TelemetryController.submitExternalPing(OVER_SIZED_PING_TYPE, {
+ data: overSizedData,
+ });
+ let ping = await PingServer.promiseNextPing();
+
+ checkHealthPingStructure(ping, {
+ [TelemetryHealthPing.FailureType.DISCARDED_FOR_SIZE]: {
+ [OVER_SIZED_PING_TYPE]: 1,
+ },
+ os: TelemetryHealthPing.OsInfo,
+ reason: TelemetryHealthPing.Reason.IMMEDIATE,
+ });
+});
+
+add_task(async function test_sendOnlyTopTenDiscardedPings() {
+ TelemetryHealthPing.testReset();
+ await TelemetrySend.reset();
+ PingServer.clearRequests();
+ let PING_TYPE = "sort-discarded";
+
+ // This first failure should immediately trigger a ping. After this, subsequent failures should be throttled.
+ await TelemetryHealthPing.recordSendFailure("testFailure");
+ let testPing = await PingServer.promiseNextPing();
+ Assert.equal(
+ testPing.type,
+ TelemetryHealthPing.HEALTH_PING_TYPE,
+ "Should have recorded a health ping."
+ );
+
+ // Retrieve delayed call back.
+ let pingSubmissionCallBack = null;
+ fakeHealthSchedulerTimer(
+ callBack => (pingSubmissionCallBack = callBack),
+ () => {}
+ );
+
+ // Add failures
+ for (let i = 1; i < 12; i++) {
+ for (let j = 1; j < i; j++) {
+ TelemetryHealthPing.recordDiscardedPing(PING_TYPE + i);
+ }
+ }
+
+ await TelemetrySend.reset();
+ await pingSubmissionCallBack();
+ let ping = await PingServer.promiseNextPing();
+
+ checkHealthPingStructure(ping, {
+ os: TelemetryHealthPing.OsInfo,
+ reason: TelemetryHealthPing.Reason.DELAYED,
+ [TelemetryHealthPing.FailureType.DISCARDED_FOR_SIZE]: {
+ [PING_TYPE + 11]: 10,
+ [PING_TYPE + 10]: 9,
+ [PING_TYPE + 9]: 8,
+ [PING_TYPE + 8]: 7,
+ [PING_TYPE + 7]: 6,
+ [PING_TYPE + 6]: 5,
+ [PING_TYPE + 5]: 4,
+ [PING_TYPE + 4]: 3,
+ [PING_TYPE + 3]: 2,
+ [PING_TYPE + 2]: 1,
+ },
+ });
+});
+
+add_task(async function test_discardedForSizePending() {
+ TelemetryHealthPing.testReset();
+ PingServer.clearRequests();
+
+ const PING_TYPE = "discarded-for-size-pending";
+
+ const OVERSIZED_PING_ID = "9b21ec8f-f762-4d28-a2c1-44e1c4694f24";
+ // Create a pending oversized ping.
+ let overSizedPayload = generateRandomString(2 * 1024 * 1024);
+ 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: overSizedPayload,
+ };
+
+ // Test loadPendingPing.
+ await TelemetryStorage.savePendingPing(OVERSIZED_PING);
+ // Try to manually load the oversized ping.
+ await Assert.rejects(
+ TelemetryStorage.loadPendingPing(OVERSIZED_PING_ID),
+ /loadPendingPing - exceeded the maximum ping size/,
+ "The oversized ping should have been pruned."
+ );
+
+ let ping = await PingServer.promiseNextPing();
+ checkHealthPingStructure(ping, {
+ [TelemetryHealthPing.FailureType.DISCARDED_FOR_SIZE]: {
+ "<unknown>": 1,
+ },
+ os: TelemetryHealthPing.OsInfo,
+ reason: TelemetryHealthPing.Reason.IMMEDIATE,
+ });
+
+ // Test _scanPendingPings.
+ TelemetryHealthPing.testReset();
+ await TelemetryStorage.savePendingPing(OVERSIZED_PING);
+ await TelemetryStorage.loadPendingPingList();
+
+ ping = await PingServer.promiseNextPing();
+ checkHealthPingStructure(ping, {
+ [TelemetryHealthPing.FailureType.DISCARDED_FOR_SIZE]: {
+ "<unknown>": 1,
+ },
+ os: TelemetryHealthPing.OsInfo,
+ reason: TelemetryHealthPing.Reason.IMMEDIATE,
+ });
+});
+
+add_task(async function test_usePingSenderOnShutdown() {
+ if (
+ gIsAndroid ||
+ (AppConstants.platform == "linux" && !Services.appinfo.is64Bit)
+ ) {
+ // We don't support the pingsender on Android, yet, see bug 1335917.
+ // We also don't support the pingsender testing on Treeherder for
+ // Linux 32 bit (due to missing libraries). So skip it there too.
+ // See bug 1310703 comment 78.
+ return;
+ }
+
+ TelemetryHealthPing.testReset();
+ await TelemetrySend.reset();
+ PingServer.clearRequests();
+
+ // This first failure should immediately trigger a ping.
+ // After this, subsequent failures should be throttled.
+ await TelemetryHealthPing.recordSendFailure("testFailure");
+ await PingServer.promiseNextPing();
+
+ TelemetryHealthPing.recordSendFailure("testFailure");
+ let nextRequest = PingServer.promiseNextRequest();
+
+ await TelemetryController.testReset();
+ await TelemetryController.testShutdown();
+ let request = await nextRequest;
+ let ping = decodeRequestPayload(request);
+
+ checkHealthPingStructure(ping, {
+ [TelemetryHealthPing.FailureType.SEND_FAILURE]: {
+ testFailure: 1,
+ },
+ os: TelemetryHealthPing.OsInfo,
+ reason: TelemetryHealthPing.Reason.SHUT_DOWN,
+ });
+
+ // Check that the health ping is sent at shutdown using the pingsender.
+ Assert.equal(
+ request.getHeader("User-Agent"),
+ "pingsender/1.0",
+ "Should have received the correct user agent string."
+ );
+ Assert.equal(
+ request.getHeader("X-PingSender-Version"),
+ "1.0",
+ "Should have received the correct PingSender version string."
+ );
+});