summaryrefslogtreecommitdiffstats
path: root/browser/components/doh/test/unit/test_TRRRacer.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/doh/test/unit/test_TRRRacer.js')
-rw-r--r--browser/components/doh/test/unit/test_TRRRacer.js209
1 files changed, 209 insertions, 0 deletions
diff --git a/browser/components/doh/test/unit/test_TRRRacer.js b/browser/components/doh/test/unit/test_TRRRacer.js
new file mode 100644
index 0000000000..d9a0455ba0
--- /dev/null
+++ b/browser/components/doh/test/unit/test_TRRRacer.js
@@ -0,0 +1,209 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+add_task(setup);
+
+add_task(async function test_TRRRacer_cleanRun() {
+ let deferred = PromiseUtils.defer();
+ 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 = PromiseUtils.defer();
+ 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 = PromiseUtils.defer();
+ 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.");
+});