diff options
Diffstat (limited to 'browser/components/doh/test/unit/test_TRRRacer.js')
-rw-r--r-- | browser/components/doh/test/unit/test_TRRRacer.js | 209 |
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."); +}); |