1
0
Fork 0
firefox/toolkit/components/nimbus/test/browser/browser_experimentapi_child.js
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

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();
});