Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
*/
"use strict";
add_task(setup);
add_task(async function test_TRRRacer_cleanRun() {
let deferred = Promise.withResolvers();
let racer = new TRRRacer(() => {
deferred.resolve();
deferred.resolved = true;
}, trrList);
racer.run();
await deferred.promise;
Assert.equal(racer._retryCount, 1);
let events = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
).parent;
Assert.ok(events);
events = events.filter(e => e[1] == "security.doh.trrPerformance");
Assert.equal(events.length, racer._aggregator.totalLookups);
Services.telemetry.clearEvents();
// Simulate network changes and ensure no re-runs since it's already complete.
async function testNetworkChange(captivePortal = false) {
if (captivePortal) {
Services.obs.notifyObservers(null, "captive-portal-login");
} else {
Services.obs.notifyObservers(null, "network:link-status-changed", "down");
}
Assert.ok(!racer._aggregator.aborted);
if (captivePortal) {
Services.obs.notifyObservers(null, "captive-portal-login-success");
} else {
Services.obs.notifyObservers(null, "network:link-status-changed", "up");
}
Assert.equal(racer._retryCount, 1);
ensureNoTelemetry();
if (captivePortal) {
Services.obs.notifyObservers(null, "captive-portal-login-abort");
}
}
testNetworkChange(false);
testNetworkChange(true);
});
async function test_TRRRacer_networkFlux_helper(captivePortal = false) {
let deferred = Promise.withResolvers();
let racer = new TRRRacer(() => {
deferred.resolve();
deferred.resolved = true;
}, trrList);
racer.run();
if (captivePortal) {
Services.obs.notifyObservers(null, "captive-portal-login");
} else {
Services.obs.notifyObservers(null, "network:link-status-changed", "down");
}
Assert.ok(racer._aggregator.aborted);
ensureNoTelemetry();
Assert.equal(racer._retryCount, 1);
Assert.ok(!deferred.resolved);
if (captivePortal) {
Services.obs.notifyObservers(null, "captive-portal-login-success");
} else {
Services.obs.notifyObservers(null, "network:link-status-changed", "up");
}
Assert.ok(!racer._aggregator.aborted);
await deferred.promise;
Assert.equal(racer._retryCount, 2);
let events = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
).parent;
Assert.ok(events);
events = events.filter(e => e[1] == "security.doh.trrPerformance");
Assert.equal(events.length, racer._aggregator.totalLookups);
Services.telemetry.clearEvents();
if (captivePortal) {
Services.obs.notifyObservers(null, "captive-portal-login-abort");
}
}
add_task(async function test_TRRRacer_networkFlux() {
await test_TRRRacer_networkFlux_helper(false);
await test_TRRRacer_networkFlux_helper(true);
});
async function test_TRRRacer_maxRetries_helper(captivePortal = false) {
let deferred = Promise.withResolvers();
let racer = new TRRRacer(() => {
deferred.resolve();
deferred.resolved = true;
}, trrList);
racer.run();
info("ran new racer");
// Start at i = 1 since we're already at retry #1.
for (let i = 1; i < 5; ++i) {
if (captivePortal) {
Services.obs.notifyObservers(null, "captive-portal-login");
} else {
Services.obs.notifyObservers(null, "network:link-status-changed", "down");
}
info("notified observers");
Assert.ok(racer._aggregator.aborted);
ensureNoTelemetry();
Assert.equal(racer._retryCount, i);
Assert.ok(!deferred.resolved);
if (captivePortal) {
Services.obs.notifyObservers(null, "captive-portal-login-success");
} else {
Services.obs.notifyObservers(null, "network:link-status-changed", "up");
}
}
// Simulate a "down" network event and ensure we still send telemetry
// since we've maxed out our retry count.
if (captivePortal) {
Services.obs.notifyObservers(null, "captive-portal-login");
} else {
Services.obs.notifyObservers(null, "network:link-status-changed", "down");
}
Assert.ok(!racer._aggregator.aborted);
await deferred.promise;
Assert.equal(racer._retryCount, 5);
let events = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
).parent;
Assert.ok(events);
events = events.filter(e => e[1] == "security.doh.trrPerformance");
Assert.equal(events.length, racer._aggregator.totalLookups);
Services.telemetry.clearEvents();
if (captivePortal) {
Services.obs.notifyObservers(null, "captive-portal-login-abort");
}
}
add_task(async function test_TRRRacer_maxRetries() {
await test_TRRRacer_maxRetries_helper(false);
await test_TRRRacer_maxRetries_helper(true);
});
add_task(async function test_TRRRacer_getFastestTRRFromResults() {
let results = [
{ trr: "trr1", time: 10 },
{ trr: "trr2", time: 100 },
{ trr: "trr1", time: 1000 },
{ trr: "trr2", time: 110 },
{ trr: "trr3", time: -1 },
{ trr: "trr4", time: -1 },
{ trr: "trr4", time: -1 },
{ trr: "trr4", time: 1 },
{ trr: "trr4", time: 1 },
{ trr: "trr5", time: 10 },
{ trr: "trr5", time: 20 },
{ trr: "trr5", time: 1000 },
];
let racer = new TRRRacer(undefined, trrList);
let fastest = racer._getFastestTRRFromResults(results);
// trr1's geometric mean is 100
// trr2's geometric mean is 110
// trr3 has no valid times, excluded
// trr4 has 50% invalid times, excluded
// trr5's geometric mean is ~58.5, it's the winner.
Assert.equal(fastest, "trr5");
// When no valid entries are available, undefined is the default output.
results = [
{ trr: "trr1", time: -1 },
{ trr: "trr2", time: -1 },
];
fastest = racer._getFastestTRRFromResults(results);
Assert.equal(fastest, undefined);
// When passing `returnRandomDefault = true`, verify that both TRRs are
// possible outputs. The probability that the randomization is working
// correctly and we consistently get the same output after 50 iterations is
// 0.5^50 ~= 8.9*10^-16.
let firstResult = racer._getFastestTRRFromResults(results, true);
while (racer._getFastestTRRFromResults(results, true) == firstResult) {
continue;
}
Assert.ok(true, "Both TRRs were possible outputs when all results invalid.");
});