mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
Bug 1600109 - Fix network change handling and test telemetry. r=dragana
Differential Revision: https://phabricator.services.mozilla.com/D58196 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
c64c07e5af
commit
1ff5f9a747
@ -189,7 +189,7 @@ const stateManager = {
|
|||||||
return !doorhangerShown;
|
return !doorhangerShown;
|
||||||
},
|
},
|
||||||
|
|
||||||
async showDoorHangerAndEnableDoH() {
|
async showDoorhanger() {
|
||||||
browser.experiments.doorhanger.onDoorhangerAccept.addListener(
|
browser.experiments.doorhanger.onDoorhangerAccept.addListener(
|
||||||
rollout.doorhangerAcceptListener
|
rollout.doorhangerAcceptListener
|
||||||
);
|
);
|
||||||
@ -208,10 +208,6 @@ const stateManager = {
|
|||||||
"doorhangerButtonCancelAccessKey"
|
"doorhangerButtonCancelAccessKey"
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
// By default, enable DoH when showing the doorhanger,
|
|
||||||
// if heuristics returned no reason to not run.
|
|
||||||
await stateManager.setState("enabled");
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -246,13 +242,19 @@ const rollout = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async heuristics(evaluateReason) {
|
async heuristics(evaluateReason) {
|
||||||
|
let shouldRunHeuristics = await stateManager.shouldRunHeuristics();
|
||||||
|
|
||||||
|
if (!shouldRunHeuristics) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Run heuristics defined in heuristics.js and experiments/heuristics/api.js
|
// Run heuristics defined in heuristics.js and experiments/heuristics/api.js
|
||||||
let results;
|
let results;
|
||||||
|
|
||||||
if (await rollout.isTesting()) {
|
if (await rollout.isTesting()) {
|
||||||
results = await browser.experiments.preferences.getCharPref(
|
results = await browser.experiments.preferences.getCharPref(
|
||||||
MOCK_HEURISTICS_PREF,
|
MOCK_HEURISTICS_PREF,
|
||||||
"disable_doh"
|
`{ "test": "disable_doh" }`
|
||||||
);
|
);
|
||||||
results = JSON.parse(results);
|
results = JSON.parse(results);
|
||||||
} else {
|
} else {
|
||||||
@ -270,7 +272,14 @@ const rollout = {
|
|||||||
results.evaluateReason = evaluateReason;
|
results.evaluateReason = evaluateReason;
|
||||||
browser.experiments.heuristics.sendHeuristicsPing(decision, results);
|
browser.experiments.heuristics.sendHeuristicsPing(decision, results);
|
||||||
|
|
||||||
return decision;
|
if (decision === "disable_doh") {
|
||||||
|
await stateManager.setState("disabled");
|
||||||
|
} else {
|
||||||
|
await stateManager.setState("enabled");
|
||||||
|
if (await stateManager.shouldShowDoorhanger()) {
|
||||||
|
await stateManager.showDoorhanger();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async getSetting(name, defaultValue) {
|
async getSetting(name, defaultValue) {
|
||||||
@ -472,81 +481,67 @@ const rollout = {
|
|||||||
await this.enterprisePolicyCheck("startup", results);
|
await this.enterprisePolicyCheck("startup", results);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await stateManager.shouldRunHeuristics()) {
|
if (!(await stateManager.shouldRunHeuristics())) {
|
||||||
await this.runStartupHeuristics();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let networkStatus = (await browser.networkStatus.getLinkInfo()).status;
|
||||||
|
let captiveState = "unknown";
|
||||||
|
try {
|
||||||
|
captiveState = await browser.captivePortal.getState();
|
||||||
|
} catch (e) {
|
||||||
|
// Captive Portal Service is disabled.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (networkStatus == "up" && captiveState != "locked_portal") {
|
||||||
|
await rollout.heuristics("startup");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for network change events to run heuristics again
|
// Listen for network change events to run heuristics again
|
||||||
browser.networkStatus.onConnectionChanged.addListener(async () => {
|
browser.networkStatus.onConnectionChanged.addListener(
|
||||||
log("onConnectionChanged");
|
rollout.onConnectionChanged
|
||||||
|
);
|
||||||
let linkInfo = await browser.networkStatus.getLinkInfo();
|
|
||||||
if (linkInfo.status !== "up") {
|
|
||||||
log("Link down.");
|
|
||||||
if (rollout.networkSettledTimeout) {
|
|
||||||
log("Canceling queued heuristics run.");
|
|
||||||
clearTimeout(rollout.networkSettledTimeout);
|
|
||||||
rollout.networkSettledTimeout = null;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log("Queing a heuristics run in 60s, will cancel if network fluctuates.");
|
|
||||||
let gracePeriod = (await rollout.isTesting()) ? 0 : 60000;
|
|
||||||
rollout.networkSettledTimeout = setTimeout(async () => {
|
|
||||||
log("No network fluctuation for 60 seconds, running heuristics.");
|
|
||||||
// Only run the heuristics if user hasn't explicitly enabled/disabled DoH
|
|
||||||
let shouldRunHeuristics = await stateManager.shouldRunHeuristics();
|
|
||||||
let shouldShowDoorhanger = await stateManager.shouldShowDoorhanger();
|
|
||||||
|
|
||||||
if (!shouldRunHeuristics) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const netChangeDecision = await rollout.heuristics("netChange");
|
|
||||||
|
|
||||||
if (netChangeDecision === "disable_doh") {
|
|
||||||
await stateManager.setState("disabled");
|
|
||||||
} else if (shouldShowDoorhanger) {
|
|
||||||
await stateManager.showDoorHangerAndEnableDoH();
|
|
||||||
} else {
|
|
||||||
await stateManager.setState("enabled");
|
|
||||||
}
|
|
||||||
}, gracePeriod);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen to the captive portal when it unlocks
|
// Listen to the captive portal when it unlocks
|
||||||
browser.captivePortal.onConnectivityAvailable.addListener(async () => {
|
try {
|
||||||
log("Captive portal onConnectivityAvailable, running heuristics.");
|
browser.captivePortal.onStateChange.addListener(
|
||||||
if (rollout.networkSettledTimeout) {
|
rollout.onCaptiveStateChanged
|
||||||
log("Canceling queued heuristics run.");
|
);
|
||||||
clearTimeout(rollout.networkSettledTimeout);
|
} catch (e) {
|
||||||
rollout.networkSettledTimeout = null;
|
// Captive Portal Service is disabled.
|
||||||
}
|
}
|
||||||
|
|
||||||
let shouldRunHeuristics = await stateManager.shouldRunHeuristics();
|
|
||||||
|
|
||||||
if (!shouldRunHeuristics) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.runStartupHeuristics();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async runStartupHeuristics() {
|
async onConnectionChanged({ status }) {
|
||||||
let decision = await this.heuristics("startup");
|
log("onConnectionChanged", status);
|
||||||
let shouldShowDoorhanger = await stateManager.shouldShowDoorhanger();
|
|
||||||
if (decision === "disable_doh") {
|
|
||||||
await stateManager.setState("disabled");
|
|
||||||
|
|
||||||
// If the heuristics say to enable DoH, determine if the doorhanger
|
if (status != "up") {
|
||||||
// should be shown
|
return;
|
||||||
} else if (shouldShowDoorhanger) {
|
}
|
||||||
await stateManager.showDoorHangerAndEnableDoH();
|
|
||||||
} else {
|
let captiveState = "unknown";
|
||||||
// Doorhanger has been shown before and did not opt-out
|
try {
|
||||||
await stateManager.setState("enabled");
|
captiveState = await browser.captivePortal.getState();
|
||||||
|
} catch (e) {
|
||||||
|
// Captive Portal Service is disabled.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (captiveState == "locked_portal") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The network is up and we don't know that we're in a locked portal.
|
||||||
|
// Run heuristics. If we detect a portal later, we'll run heuristics again
|
||||||
|
// when it's unlocked. In that case, this run will likely have failed.
|
||||||
|
await rollout.heuristics("netchange");
|
||||||
|
},
|
||||||
|
|
||||||
|
async onCaptiveStateChanged({ state }) {
|
||||||
|
log("onCaptiveStateChanged", state);
|
||||||
|
// unlocked_portal means we were previously in a locked portal and then
|
||||||
|
// network access was granted.
|
||||||
|
if (state == "unlocked_portal") {
|
||||||
|
await rollout.heuristics("netchange");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -27,6 +27,7 @@ add_task(async function testCleanFlow() {
|
|||||||
await promise;
|
await promise;
|
||||||
|
|
||||||
await ensureTRRMode(2);
|
await ensureTRRMode(2);
|
||||||
|
checkHeuristicsTelemetry("enable_doh", "startup");
|
||||||
|
|
||||||
await BrowserTestUtils.waitForCondition(() => {
|
await BrowserTestUtils.waitForCondition(() => {
|
||||||
return Preferences.get(prefs.DOH_DOORHANGER_SHOWN_PREF);
|
return Preferences.get(prefs.DOH_DOORHANGER_SHOWN_PREF);
|
||||||
@ -51,24 +52,26 @@ add_task(async function testCleanFlow() {
|
|||||||
setFailingHeuristics();
|
setFailingHeuristics();
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureTRRMode(0);
|
await ensureTRRMode(0);
|
||||||
|
checkHeuristicsTelemetry("disable_doh", "netchange");
|
||||||
|
|
||||||
// Trigger another network change.
|
// Trigger another network change.
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(0);
|
await ensureNoTRRModeChange(0);
|
||||||
|
checkHeuristicsTelemetry("disable_doh", "netchange");
|
||||||
|
|
||||||
// Restart the add-on for good measure.
|
// Restart the add-on for good measure.
|
||||||
await restartAddon();
|
await restartAddon();
|
||||||
await ensureNoTRRModeChange(0);
|
await ensureNoTRRModeChange(0);
|
||||||
|
checkHeuristicsTelemetry("disable_doh", "startup");
|
||||||
|
|
||||||
// Set a passing environment and simulate a network change.
|
// Set a passing environment and simulate a network change.
|
||||||
setPassingHeuristics();
|
setPassingHeuristics();
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureTRRMode(2);
|
await ensureTRRMode(2);
|
||||||
|
checkHeuristicsTelemetry("enable_doh", "netchange");
|
||||||
|
|
||||||
// Again, repeat and check nothing changed.
|
// Again, repeat and check nothing changed.
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(2);
|
await ensureNoTRRModeChange(2);
|
||||||
|
checkHeuristicsTelemetry("enable_doh", "netchange");
|
||||||
// Clean up.
|
|
||||||
await resetPrefsAndRestartAddon();
|
|
||||||
});
|
});
|
||||||
|
@ -22,19 +22,20 @@ add_task(async function testDirtyEnable() {
|
|||||||
"Breadcrumb not saved."
|
"Breadcrumb not saved."
|
||||||
);
|
);
|
||||||
await ensureNoTRRModeChange(2);
|
await ensureNoTRRModeChange(2);
|
||||||
|
checkHeuristicsTelemetry("prefHasUserValue", "first_run");
|
||||||
|
|
||||||
// Simulate a network change.
|
// Simulate a network change.
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(2);
|
await ensureNoTRRModeChange(2);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
|
|
||||||
// Restart for good measure.
|
// Restart for good measure.
|
||||||
await restartAddon();
|
await restartAddon();
|
||||||
await ensureNoTRRModeChange(2);
|
await ensureNoTRRModeChange(2);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
|
|
||||||
// Simulate a network change.
|
// Simulate a network change.
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(2);
|
await ensureNoTRRModeChange(2);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
// Clean up.
|
|
||||||
await resetPrefsAndRestartAddon();
|
|
||||||
});
|
});
|
||||||
|
@ -21,6 +21,7 @@ add_task(async function testDoorhangerUserReject() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await ensureTRRMode(2);
|
await ensureTRRMode(2);
|
||||||
|
checkHeuristicsTelemetry("enable_doh", "startup");
|
||||||
|
|
||||||
// Click the doorhanger's "reject" button.
|
// Click the doorhanger's "reject" button.
|
||||||
let button = panel.querySelector(".popup-notification-secondary-button");
|
let button = panel.querySelector(".popup-notification-secondary-button");
|
||||||
@ -49,20 +50,21 @@ add_task(async function testDoorhangerUserReject() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await ensureTRRMode(5);
|
await ensureTRRMode(5);
|
||||||
|
checkHeuristicsTelemetry("disable_doh", "doorhangerDecline");
|
||||||
|
|
||||||
// Simulate a network change.
|
// Simulate a network change.
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(5);
|
await ensureNoTRRModeChange(5);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
|
|
||||||
// Restart the add-on for good measure.
|
// Restart the add-on for good measure.
|
||||||
await restartAddon();
|
await restartAddon();
|
||||||
await ensureNoTRRModeChange(5);
|
await ensureNoTRRModeChange(5);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
|
|
||||||
// Set failing environment and trigger another network change.
|
// Set failing environment and trigger another network change.
|
||||||
setFailingHeuristics();
|
setFailingHeuristics();
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(5);
|
await ensureNoTRRModeChange(5);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
// Clean up.
|
|
||||||
await resetPrefsAndRestartAddon();
|
|
||||||
});
|
});
|
||||||
|
@ -38,25 +38,28 @@ add_task(async function testPolicyOverride() {
|
|||||||
"Breadcrumb not saved."
|
"Breadcrumb not saved."
|
||||||
);
|
);
|
||||||
await ensureNoTRRModeChange(0);
|
await ensureNoTRRModeChange(0);
|
||||||
|
checkHeuristicsTelemetry("policy_without_doh", "first_run");
|
||||||
|
|
||||||
// Simulate a network change.
|
// Simulate a network change.
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(0);
|
await ensureNoTRRModeChange(0);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
|
|
||||||
// Restart for good measure.
|
// Restart for good measure.
|
||||||
await restartAddon();
|
await restartAddon();
|
||||||
await ensureNoTRRModeChange(0);
|
await ensureNoTRRModeChange(0);
|
||||||
|
checkHeuristicsTelemetry("policy_without_doh", "startup");
|
||||||
|
|
||||||
// Simulate a network change.
|
// Simulate a network change.
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(0);
|
await ensureNoTRRModeChange(0);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
|
|
||||||
// Clean up.
|
// Clean up.
|
||||||
await EnterprisePolicyTesting.setupPolicyEngineWithJson({
|
await EnterprisePolicyTesting.setupPolicyEngineWithJson({
|
||||||
policies: {},
|
policies: {},
|
||||||
});
|
});
|
||||||
EnterprisePolicyTesting.resetRunOnceState();
|
EnterprisePolicyTesting.resetRunOnceState();
|
||||||
await resetPrefsAndRestartAddon();
|
|
||||||
|
|
||||||
is(
|
is(
|
||||||
Services.policies.status,
|
Services.policies.status,
|
||||||
|
@ -42,6 +42,7 @@ add_task(async function testUserInterference() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await ensureTRRMode(2);
|
await ensureTRRMode(2);
|
||||||
|
checkHeuristicsTelemetry("enable_doh", "startup");
|
||||||
|
|
||||||
// Set the TRR mode pref manually and ensure we respect this.
|
// Set the TRR mode pref manually and ensure we respect this.
|
||||||
Preferences.set(prefs.TRR_MODE_PREF, 0);
|
Preferences.set(prefs.TRR_MODE_PREF, 0);
|
||||||
@ -49,6 +50,7 @@ add_task(async function testUserInterference() {
|
|||||||
// Simulate a network change.
|
// Simulate a network change.
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(0);
|
await ensureNoTRRModeChange(0);
|
||||||
|
checkHeuristicsTelemetry("disable_doh", "userModified");
|
||||||
|
|
||||||
is(
|
is(
|
||||||
Preferences.get(prefs.DOH_DISABLED_PREF, false),
|
Preferences.get(prefs.DOH_DISABLED_PREF, false),
|
||||||
@ -64,15 +66,15 @@ add_task(async function testUserInterference() {
|
|||||||
// Simulate another network change.
|
// Simulate another network change.
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(0);
|
await ensureNoTRRModeChange(0);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
|
|
||||||
// Restart the add-on for good measure.
|
// Restart the add-on for good measure.
|
||||||
await restartAddon();
|
await restartAddon();
|
||||||
await ensureNoTRRModeChange(0);
|
await ensureNoTRRModeChange(0);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
|
|
||||||
// Simulate another network change.
|
// Simulate another network change.
|
||||||
simulateNetworkChange();
|
simulateNetworkChange();
|
||||||
await ensureNoTRRModeChange(0);
|
await ensureNoTRRModeChange(0);
|
||||||
|
ensureNoHeuristicsTelemetry();
|
||||||
// Clean up.
|
|
||||||
await resetPrefsAndRestartAddon();
|
|
||||||
});
|
});
|
||||||
|
@ -59,12 +59,48 @@ async function setup() {
|
|||||||
Services.telemetry.canRecordExtended = true;
|
Services.telemetry.canRecordExtended = true;
|
||||||
Services.telemetry.clearEvents();
|
Services.telemetry.clearEvents();
|
||||||
|
|
||||||
registerCleanupFunction(() => {
|
registerCleanupFunction(async () => {
|
||||||
Services.telemetry.canRecordExtended = oldCanRecord;
|
Services.telemetry.canRecordExtended = oldCanRecord;
|
||||||
Services.telemetry.clearEvents();
|
Services.telemetry.clearEvents();
|
||||||
|
await resetPrefsAndRestartAddon();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkHeuristicsTelemetry(decision, evaluateReason) {
|
||||||
|
let events = Services.telemetry.snapshotEvents(
|
||||||
|
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS
|
||||||
|
).dynamic;
|
||||||
|
events = events.filter(
|
||||||
|
e => e[1] == "doh" && e[2] == "evaluate" && e[3] == "heuristics"
|
||||||
|
);
|
||||||
|
is(events.length, 1, "Found the expected heuristics event.");
|
||||||
|
is(events[0][4], decision, "The event records the expected decision");
|
||||||
|
if (evaluateReason) {
|
||||||
|
is(events[0][5].evaluateReason, evaluateReason, "Got the expected reason.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// After checking the event, clear all telemetry. Since we check for a single
|
||||||
|
// event above, this ensures all heuristics events are intentional and tested.
|
||||||
|
// TODO: Test events other than heuristics. Those tests would also work the
|
||||||
|
// same way, so as to test one event at a time, and this clearEvents() call
|
||||||
|
// will continue to exist as-is.
|
||||||
|
Services.telemetry.clearEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureNoHeuristicsTelemetry() {
|
||||||
|
let events = Services.telemetry.snapshotEvents(
|
||||||
|
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS
|
||||||
|
).dynamic;
|
||||||
|
if (!events) {
|
||||||
|
ok(true, "Found no heuristics events.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
events = events.filter(
|
||||||
|
e => e[1] == "doh" && e[2] == "evaluate" && e[3] == "heuristics"
|
||||||
|
);
|
||||||
|
is(events.length, 0, "Found no heuristics events.");
|
||||||
|
}
|
||||||
|
|
||||||
function setPassingHeuristics() {
|
function setPassingHeuristics() {
|
||||||
Preferences.set(prefs.MOCK_HEURISTICS_PREF, fakePassingHeuristics);
|
Preferences.set(prefs.MOCK_HEURISTICS_PREF, fakePassingHeuristics);
|
||||||
}
|
}
|
||||||
@ -101,7 +137,12 @@ async function waitForDoorhanger() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function simulateNetworkChange() {
|
function simulateNetworkChange() {
|
||||||
Services.obs.notifyObservers(null, "network:link-status-changed", "down");
|
// The networkStatus API does not actually propagate the link status we supply
|
||||||
|
// here, but rather sends the link status from the NetworkLinkService.
|
||||||
|
// This means there's no point sending a down and then an up - the extension
|
||||||
|
// will just receive "up" twice.
|
||||||
|
// TODO: Implement a mock NetworkLinkService and use it to also simulate
|
||||||
|
// network down events.
|
||||||
Services.obs.notifyObservers(null, "network:link-status-changed", "up");
|
Services.obs.notifyObservers(null, "network:link-status-changed", "up");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user