197 lines
5.7 KiB
JavaScript
197 lines
5.7 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
add_setup(async function setup() {
|
|
const cleanup = await setupTest();
|
|
|
|
SpecialPowers.addTaskImport(
|
|
"ExperimentAPI",
|
|
"resource://nimbus/ExperimentAPI.sys.mjs"
|
|
);
|
|
SpecialPowers.addTaskImport(
|
|
"NimbusFeatures",
|
|
"resource://nimbus/ExperimentAPI.sys.mjs"
|
|
);
|
|
SpecialPowers.addTaskImport(
|
|
"TestUtils",
|
|
"resource://testing-common/TestUtils.sys.mjs"
|
|
);
|
|
|
|
registerCleanupFunction(cleanup);
|
|
});
|
|
|
|
add_task(async function testGetFromChildNewEnrollment() {
|
|
const browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
|
|
|
// Open a tab so we have a content process.
|
|
const tab = await BrowserTestUtils.openNewForegroundTab({
|
|
gBrowser: browserWindow.gBrowser,
|
|
url: "https://example.com",
|
|
forceNewProcess: true,
|
|
});
|
|
const browser = tab.linkedBrowser;
|
|
|
|
// Assert that the tab is in fact a content process and that we don't have any
|
|
// experiments available yet.
|
|
await SpecialPowers.spawn(browser, [], async () => {
|
|
Assert.equal(
|
|
Services.appinfo.processType,
|
|
Services.appinfo.PROCESS_TYPE_CONTENT,
|
|
"This is running in a content process"
|
|
);
|
|
|
|
await ExperimentAPI.ready();
|
|
|
|
Assert.equal(
|
|
NimbusFeatures.testFeature.getEnrollmentMetadata(),
|
|
null,
|
|
"Experiment should not exist in child yet"
|
|
);
|
|
});
|
|
|
|
let childUpdated = await childSharedDataChanged(browser);
|
|
|
|
// Enroll in an experiment in the parent process.
|
|
await ExperimentAPI.manager.enroll(
|
|
NimbusTestUtils.factories.recipe.withFeatureConfig("foo", {
|
|
featureId: "testFeature",
|
|
value: {
|
|
enabled: true,
|
|
testInt: 123,
|
|
},
|
|
}),
|
|
"test"
|
|
);
|
|
|
|
// Immediately serialize sharedData and broadcast changes to the child processes.
|
|
//
|
|
// In normal operation, this will happen during idle dispatch [1], but we want
|
|
// to test that our IPC mechanisms work correctly, so we force it to happen so
|
|
// that we can act immediately in the child.
|
|
//
|
|
// [1]: https://searchfox.org/mozilla-central/rev/5bea6ede57be43d450ecc24af7a535288c9a9f7d/dom/ipc/SharedMap.cpp#416-422
|
|
Services.ppmm.sharedData.flush();
|
|
await childUpdated.promise;
|
|
|
|
// Check that the new state is reflected in the content process.
|
|
await SpecialPowers.spawn(browser, [], async () => {
|
|
await TestUtils.waitForCondition(
|
|
() => NimbusFeatures.testFeature.getEnrollmentMetadata(),
|
|
"Wait for enrollment child to sync"
|
|
);
|
|
|
|
const meta = NimbusFeatures.testFeature.getEnrollmentMetadata();
|
|
|
|
Assert.equal(meta.slug, "foo", "Experiment slug is correct");
|
|
Assert.equal(meta.branch, "control", "Experiment branch slug is correct");
|
|
|
|
Assert.deepEqual(
|
|
NimbusFeatures.testFeature.getAllVariables(),
|
|
{ enabled: true, testInt: 123 },
|
|
"Experiment values are correct"
|
|
);
|
|
|
|
Assert.equal(
|
|
NimbusFeatures.testFeature.getVariable("enabled"),
|
|
true,
|
|
"Experiment values are correct"
|
|
);
|
|
|
|
Assert.equal(
|
|
NimbusFeatures.testFeature.getVariable("testInt"),
|
|
123,
|
|
"Experiment values are correct"
|
|
);
|
|
});
|
|
|
|
childUpdated = await childSharedDataChanged(browser);
|
|
// Unenroll from the experiment in the parent process.
|
|
await ExperimentAPI.manager.unenroll("foo");
|
|
// Propagate the change to child processes.
|
|
Services.ppmm.sharedData.flush();
|
|
await childUpdated.promise;
|
|
|
|
// Check that the new state is reflected in the content process.
|
|
await SpecialPowers.spawn(browser, [], async () => {
|
|
await TestUtils.waitForCondition(
|
|
() => NimbusFeatures.testFeature.getEnrollmentMetadata() === null,
|
|
"Wait for unenrollment to sync"
|
|
);
|
|
});
|
|
|
|
ExperimentAPI.manager.store._deleteForTests("foo");
|
|
|
|
BrowserTestUtils.removeTab(tab);
|
|
|
|
Services.ppmm.sharedData.flush();
|
|
});
|
|
|
|
add_task(async function testGetFromChildExistingEnrollment() {
|
|
const browserWindow =
|
|
Services.wm.getMostRecentBrowserWindow("navigator:browser");
|
|
|
|
// We only want to test the new process case, so make sure to shut down any
|
|
// existing processes.
|
|
Services.ppmm.releaseCachedProcesses();
|
|
|
|
await ExperimentAPI.manager.enroll(
|
|
NimbusTestUtils.factories.recipe.withFeatureConfig("qux", {
|
|
branchSlug: "treatment",
|
|
featureId: "testFeature",
|
|
value: {
|
|
enabled: false,
|
|
testInt: 456,
|
|
},
|
|
}),
|
|
"test"
|
|
);
|
|
|
|
// We don't have to wait for this to update in the client, but we *do* have to
|
|
// flush to make it re-serialize the contents to make it available to new
|
|
// processes.
|
|
Services.ppmm.sharedData.flush();
|
|
|
|
// Open a tab so that we have a content process.
|
|
const tab = await BrowserTestUtils.openNewForegroundTab({
|
|
gBrowser: browserWindow.gBrowser,
|
|
url: "https://example.com",
|
|
forceNewProcess: true,
|
|
});
|
|
const browser = tab.linkedBrowser;
|
|
|
|
// Check that the experiment is available in the child process.
|
|
await SpecialPowers.spawn(browser, [], async () => {
|
|
await ExperimentAPI.ready();
|
|
|
|
const meta = NimbusFeatures.testFeature.getEnrollmentMetadata();
|
|
|
|
Assert.equal(meta.slug, "qux", "Experiment slug is correct");
|
|
Assert.equal(meta.branch, "treatment", "Experiment branch slug is correct");
|
|
|
|
Assert.deepEqual(
|
|
NimbusFeatures.testFeature.getAllVariables(),
|
|
{ enabled: false, testInt: 456 },
|
|
"Experiment values are correct"
|
|
);
|
|
|
|
Assert.equal(
|
|
NimbusFeatures.testFeature.getVariable("enabled"),
|
|
false,
|
|
"Experiment values are correct"
|
|
);
|
|
|
|
Assert.equal(
|
|
NimbusFeatures.testFeature.getVariable("testInt"),
|
|
456,
|
|
"Experiment values are correct"
|
|
);
|
|
});
|
|
|
|
await ExperimentAPI.manager.unenroll("qux");
|
|
ExperimentAPI.manager.store._deleteForTests("qux");
|
|
BrowserTestUtils.removeTab(tab);
|
|
|
|
Services.ppmm.sharedData.flush();
|
|
});
|