Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
"use strict";
const { TelemetryUtils } = ChromeUtils.importESModule(
"resource://gre/modules/TelemetryUtils.sys.mjs"
);
const { Normandy } = ChromeUtils.importESModule(
"resource://normandy/Normandy.sys.mjs"
);
const { AddonRollouts } = ChromeUtils.importESModule(
"resource://normandy/lib/AddonRollouts.sys.mjs"
);
const { PreferenceExperiments } = ChromeUtils.importESModule(
"resource://normandy/lib/PreferenceExperiments.sys.mjs"
);
const { PreferenceRollouts } = ChromeUtils.importESModule(
"resource://normandy/lib/PreferenceRollouts.sys.mjs"
);
const { RecipeRunner } = ChromeUtils.importESModule(
"resource://normandy/lib/RecipeRunner.sys.mjs"
);
const {
NormandyTestUtils: { factories },
} = ChromeUtils.importESModule(
);
const experimentPref1 = "test.initExperimentPrefs1";
const experimentPref2 = "test.initExperimentPrefs2";
const experimentPref3 = "test.initExperimentPrefs3";
const experimentPref4 = "test.initExperimentPrefs4";
function withStubInits() {
return function (testFunction) {
return decorate(
withStub(AddonRollouts, "init"),
withStub(AddonStudies, "init"),
withStub(PreferenceRollouts, "init"),
withStub(PreferenceExperiments, "init"),
withStub(RecipeRunner, "init"),
testFunction
);
};
}
decorate_task(
withPrefEnv({
set: [
[`app.normandy.startupExperimentPrefs.${experimentPref1}`, true],
[`app.normandy.startupExperimentPrefs.${experimentPref2}`, 2],
[`app.normandy.startupExperimentPrefs.${experimentPref3}`, "string"],
],
}),
async function testApplyStartupPrefs() {
const defaultBranch = Services.prefs.getDefaultBranch("");
for (const pref of [experimentPref1, experimentPref2, experimentPref3]) {
is(
defaultBranch.getPrefType(pref),
defaultBranch.PREF_INVALID,
`Pref ${pref} don't exist before being initialized.`
);
}
let oldValues = Normandy.applyStartupPrefs(
"app.normandy.startupExperimentPrefs."
);
Assert.deepEqual(
oldValues,
{
[experimentPref1]: null,
[experimentPref2]: null,
[experimentPref3]: null,
},
"the correct set of old values should be reported"
);
ok(
defaultBranch.getBoolPref(experimentPref1),
`Pref ${experimentPref1} has a default value after being initialized.`
);
is(
defaultBranch.getIntPref(experimentPref2),
2,
`Pref ${experimentPref2} has a default value after being initialized.`
);
is(
defaultBranch.getCharPref(experimentPref3),
"string",
`Pref ${experimentPref3} has a default value after being initialized.`
);
for (const pref of [experimentPref1, experimentPref2, experimentPref3]) {
ok(
!defaultBranch.prefHasUserValue(pref),
`Pref ${pref} doesn't have a user value after being initialized.`
);
Services.prefs.clearUserPref(pref);
defaultBranch.deleteBranch(pref);
}
}
);
decorate_task(
withPrefEnv({
set: [
["app.normandy.startupExperimentPrefs.test.existingPref", "experiment"],
],
}),
async function testApplyStartupPrefsExisting() {
const defaultBranch = Services.prefs.getDefaultBranch("");
defaultBranch.setCharPref("test.existingPref", "default");
Normandy.applyStartupPrefs("app.normandy.startupExperimentPrefs.");
is(
defaultBranch.getCharPref("test.existingPref"),
"experiment",
"applyStartupPrefs overwrites the default values of existing preferences."
);
}
);
decorate_task(
withPrefEnv({
set: [
["app.normandy.startupExperimentPrefs.test.mismatchPref", "experiment"],
],
}),
async function testApplyStartupPrefsMismatch() {
const defaultBranch = Services.prefs.getDefaultBranch("");
defaultBranch.setIntPref("test.mismatchPref", 2);
Normandy.applyStartupPrefs("app.normandy.startupExperimentPrefs.");
is(
defaultBranch.getPrefType("test.mismatchPref"),
Services.prefs.PREF_INT,
"applyStartupPrefs skips prefs that don't match the existing default value's type."
);
}
);
decorate_task(
withStub(Normandy, "finishInit"),
async function testStartupDelayed({ finishInitStub }) {
let originalDeferred = Normandy.uiAvailableNotificationObserved;
let mockUiAvailableDeferred = Promise.withResolvers();
Normandy.uiAvailableNotificationObserved = mockUiAvailableDeferred;
let initPromise = Normandy.init();
await null;
ok(
!finishInitStub.called,
"When initialized, do not call finishInit immediately."
);
Normandy.observe(null, "sessionstore-windows-restored");
await initPromise;
ok(
finishInitStub.called,
"Once the sessionstore-windows-restored event is observed, finishInit should be called."
);
Normandy.uiAvailableNotificationObserved = originalDeferred;
}
);
// During startup, preferences that are changed for experiments should
// be record by calling PreferenceExperiments.recordOriginalValues.
decorate_task(
withStub(PreferenceExperiments, "recordOriginalValues", {
as: "experimentsRecordOriginalValuesStub",
}),
withStub(PreferenceRollouts, "recordOriginalValues", {
as: "rolloutsRecordOriginalValueStub",
}),
async function testApplyStartupPrefs({
experimentsRecordOriginalValuesStub,
rolloutsRecordOriginalValueStub,
}) {
const defaultBranch = Services.prefs.getDefaultBranch("");
defaultBranch.setBoolPref(experimentPref1, false);
defaultBranch.setIntPref(experimentPref2, 1);
defaultBranch.setCharPref(experimentPref3, "original string");
// experimentPref4 is left unset
Normandy.applyStartupPrefs("app.normandy.startupExperimentPrefs.");
Normandy.studyPrefsChanged = { "test.study-pref": 1 };
Normandy.rolloutPrefsChanged = { "test.rollout-pref": 1 };
await Normandy.finishInit();
Assert.deepEqual(
experimentsRecordOriginalValuesStub.args,
[[{ "test.study-pref": 1 }]],
"finishInit should record original values of the study prefs"
);
Assert.deepEqual(
rolloutsRecordOriginalValueStub.args,
[[{ "test.rollout-pref": 1 }]],
"finishInit should record original values of the study prefs"
);
// cleanup
defaultBranch.deleteBranch(experimentPref1);
defaultBranch.deleteBranch(experimentPref2);
defaultBranch.deleteBranch(experimentPref3);
}
);
// Test that startup prefs are handled correctly when there is a value on the user branch but not the default branch.
decorate_task(
withPrefEnv({
set: [
["app.normandy.startupExperimentPrefs.testing.does-not-exist", "foo"],
["testing.does-not-exist", "foo"],
],
}),
withStub(PreferenceExperiments, "recordOriginalValues"),
async function testApplyStartupPrefsNoDefaultValue() {
Normandy.applyStartupPrefs("app.normandy.startupExperimentPrefs");
ok(
true,
"initExperimentPrefs should not throw for prefs that doesn't exist on the default branch"
);
}
);
decorate_task(withStubInits(), async function testStartup() {
const initObserved = TestUtils.topicObserved("shield-init-complete");
await Normandy.finishInit();
ok(AddonStudies.init.called, "startup calls AddonStudies.init");
ok(
PreferenceExperiments.init.called,
"startup calls PreferenceExperiments.init"
);
ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
await initObserved;
});
decorate_task(withStubInits(), async function testStartupPrefInitFail() {
PreferenceExperiments.init.rejects();
await Normandy.finishInit();
ok(AddonStudies.init.called, "startup calls AddonStudies.init");
ok(AddonRollouts.init.called, "startup calls AddonRollouts.init");
ok(
PreferenceExperiments.init.called,
"startup calls PreferenceExperiments.init"
);
ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
ok(PreferenceRollouts.init.called, "startup calls PreferenceRollouts.init");
});
decorate_task(
withStubInits(),
async function testStartupAddonStudiesInitFail() {
AddonStudies.init.rejects();
await Normandy.finishInit();
ok(AddonStudies.init.called, "startup calls AddonStudies.init");
ok(AddonRollouts.init.called, "startup calls AddonRollouts.init");
ok(
PreferenceExperiments.init.called,
"startup calls PreferenceExperiments.init"
);
ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
ok(PreferenceRollouts.init.called, "startup calls PreferenceRollouts.init");
}
);
decorate_task(
withStubInits(),
async function testStartupPreferenceRolloutsInitFail() {
PreferenceRollouts.init.throws();
await Normandy.finishInit();
ok(AddonStudies.init.called, "startup calls AddonStudies.init");
ok(AddonRollouts.init.called, "startup calls AddonRollouts.init");
ok(
PreferenceExperiments.init.called,
"startup calls PreferenceExperiments.init"
);
ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
ok(PreferenceRollouts.init.called, "startup calls PreferenceRollouts.init");
}
);