gecko-dev/toolkit/components/telemetry/tests/unit/test_HealthPing.js
Kris Maglione e930b89c34 Bug 1514594: Part 3 - Change ChromeUtils.import API.
***
Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8

This changes the behavior of ChromeUtils.import() to return an exports object,
rather than a module global, in all cases except when `null` is passed as a
second argument, and changes the default behavior not to pollute the global
scope with the module's exports. Thus, the following code written for the old
model:

  ChromeUtils.import("resource://gre/modules/Services.jsm");

is approximately the same as the following, in the new model:

  var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

Since the two behaviors are mutually incompatible, this patch will land with a
scripted rewrite to update all existing callers to use the new model rather
than the old.
***
Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs

This was done using the followng script:

https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm
***
Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8

Differential Revision: https://phabricator.services.mozilla.com/D16747
***
Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16748
***
Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16749
***
Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs
***
Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16750

--HG--
extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895
extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
2019-01-17 10:18:31 -08:00

340 lines
11 KiB
JavaScript

/* 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.import("resource://gre/modules/TelemetryController.jsm", this);
ChromeUtils.import("resource://gre/modules/TelemetryStorage.jsm", this);
ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm", this);
ChromeUtils.import("resource://gre/modules/Preferences.jsm", this);
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
ChromeUtils.import("resource://testing-common/TelemetryArchiveTesting.jsm", this);
ChromeUtils.defineModuleGetter(this, "TelemetryHealthPing",
"resource://gre/modules/HealthPing.jsm");
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 telemetryHealthPing = ChromeUtils.import("resource://gre/modules/HealthPing.jsm", null);
telemetryHealthPing.Policy.setSchedulerTickTimeout = set;
telemetryHealthPing.Policy.clearSchedulerTickTimeout = clear;
}
async function waitForConditionWithPromise(promiseFn, timeoutMsg, tryCount = 30) {
const SINGLE_TRY_TIMEOUT = 100;
let tries = 0;
do {
try {
return await promiseFn();
} catch (ex) {}
await new Promise(resolve => do_timeout(SINGLE_TRY_TIMEOUT, resolve));
} while (++tries <= tryCount);
throw new Error(timeoutMsg);
}
function fakeSendSubmissionTimeout(timeOut) {
let telemetryHealthPing = ChromeUtils.import("resource://gre/modules/TelemetrySend.jsm", null);
telemetryHealthPing.Policy.pingSubmissionTimeout = () => timeOut;
}
add_task(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();
Preferences.set(TelemetryUtils.Preferences.HealthPingEnabled, true);
await TelemetryController.testSetup();
PingServer.start();
TelemetrySend.setServer("http://localhost:" + PingServer.port);
Preferences.set(TelemetryUtils.Preferences.Server, "http://localhost:" + PingServer.port);
});
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_healthPingOnTop() {
PingServer.clearRequests();
TelemetryHealthPing.testReset();
let PING_TYPE = "priority-ping";
// Fake now to be in throttled state.
let now = fakeNow(2050, 1, 2, 0, 0, 0);
fakeMidnightPingFuzzingDelay(60 * 1000);
for (let value of [PING_TYPE, PING_TYPE, "health", PING_TYPE]) {
TelemetryController.submitExternalPing(value, {});
}
// Now trigger sending pings again.
fakeNow(futureDate(now, 5 * 60 * 1000));
await TelemetrySend.notifyCanUpload();
let scheduler = ChromeUtils.import("resource://gre/modules/TelemetrySend.jsm", null);
scheduler.SendScheduler.triggerSendingPings(true);
let pings = await PingServer.promiseNextPings(4);
Assert.equal(pings[0].type, "health", "Should have received the health ping first.");
});
add_task(async function test_sendOnTimeout() {
TelemetryHealthPing.testReset();
await TelemetrySend.reset();
PingServer.clearRequests();
let PING_TYPE = "ping-on-timeout";
// Disable send retry to make this test more deterministic.
fakePingSendTimer(() => {}, () => {});
// Set up small ping submission timeout to always have timeout error.
fakeSendSubmissionTimeout(2);
await TelemetryController.submitExternalPing(PING_TYPE, {});
let response;
PingServer.registerPingHandler((req, res) => {
PingServer.resetPingHandler();
// We don't finish the response yet to make sure to trigger a timeout.
res.processAsync();
response = res;
});
// Wait for health ping.
let ac = new TelemetryArchiveTesting.Checker();
await ac.promiseInit();
await waitForConditionWithPromise(() => {
ac.promiseFindPing("health", []);
}, "Failed to find health ping");
if (response) {
response.finish();
}
let telemetryHealthPing = ChromeUtils.import("resource://gre/modules/TelemetrySend.jsm", null);
fakeSendSubmissionTimeout(telemetryHealthPing.PING_SUBMIT_TIMEOUT_MS);
PingServer.resetPingHandler();
TelemetrySend.notifyCanUpload();
let pings = await PingServer.promiseNextPings(2);
let healthPing = pings.find(ping => ping.type === "health");
checkHealthPingStructure(healthPing, {
[TelemetryHealthPing.FailureType.SEND_FAILURE]: {
"timeout": 1,
},
"os": TelemetryHealthPing.OsInfo,
"reason": TelemetryHealthPing.Reason.IMMEDIATE,
});
await TelemetryStorage.testClearPendingPings();
});
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" && OS.Constants.Sys.bits == 32)) {
// 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.");
});
add_task(async function cleanup() {
await PingServer.stop();
});