tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 52a349bba6a928eb6362b0781dd128015e7715ea
parent 819cdd63014d015ee47f1343c339ae36d26a3e20
Author: Beth Rennie <beth@brennie.ca>
Date:   Fri, 14 Nov 2025 22:27:58 +0000

Bug 1996359 - Add NimbusTestUtils.createStoreWith r=nimbus-reviewers,relud

This adds a new test helper that creates and saves an ExperimentStore to
disk. Most Nimbus unit tests have been updated to use this helper.

Differential Revision: https://phabricator.services.mozilla.com/D270235

Diffstat:
Mtoolkit/components/nimbus/test/NimbusTestUtils.sys.mjs | 16++++++++++++++++
Mtoolkit/components/nimbus/test/unit/test_ExperimentManager_lifecycle.js | 68+++++++++++++++++++++++++++-----------------------------------------
Mtoolkit/components/nimbus/test/unit/test_ExperimentStore.js | 52++++++++++++++++------------------------------------
Mtoolkit/components/nimbus/test/unit/test_Migrations.js | 45+++++++++++++++++++++------------------------
Mtoolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader_updateRecipes.js | 26+++++++++-----------------
Mtoolkit/components/nimbus/test/unit/test_policy.js | 24++++++++----------------
Mtoolkit/components/nimbus/test/unit/test_prefFlips.js | 58++++++++++++++++++----------------------------------------
7 files changed, 115 insertions(+), 174 deletions(-)

diff --git a/toolkit/components/nimbus/test/NimbusTestUtils.sys.mjs b/toolkit/components/nimbus/test/NimbusTestUtils.sys.mjs @@ -747,6 +747,22 @@ export const NimbusTestUtils = { } catch (e) {} }, + /** + * Create a Nimbus store and return its path on disk. + * + * @param {function(store: ExperimentStore): void} A function that will be + * called with the store. + * + * @returns {string} The path to the Nimbus store, which can be passed to + * {@link NimbusTestUtils.setupTest}. + */ + async createStoreWith(fn) { + const store = NimbusTestUtils.stubs.store(); + await store.init(); + await fn(store); + return NimbusTestUtils.saveStore(store); + }, + async deleteEnrollmentsFromProfiles(profileIds) { const conn = await lazy.ProfilesDatastoreService.getConnection(); if (!conn) { diff --git a/toolkit/components/nimbus/test/unit/test_ExperimentManager_lifecycle.js b/toolkit/components/nimbus/test/unit/test_ExperimentManager_lifecycle.js @@ -24,36 +24,27 @@ const { ProfilesDatastoreService } = ChromeUtils.importESModule( * - should set call setExperimentActive for each active experiment */ async function test_onStartup_setExperimentActive_called() { - let storePath; - - { - const store = NimbusTestUtils.stubs.store(); - await store.init(); - - NimbusTestUtils.addEnrollmentForRecipe( - NimbusTestUtils.factories.recipe("foo"), - { store, branchSlug: "control" } - ); - NimbusTestUtils.addEnrollmentForRecipe( - NimbusTestUtils.factories.recipe("bar", { isRollout: true }), - { store } - ); - NimbusTestUtils.addEnrollmentForRecipe( - NimbusTestUtils.factories.recipe("baz"), - { store, branchSlug: "control", extra: { active: false } } - ); - NimbusTestUtils.addEnrollmentForRecipe( - NimbusTestUtils.factories.recipe("qux", { isRollout: true }), - { store, extra: { active: false } } - ); - - storePath = await NimbusTestUtils.saveStore(store); - } - const { sandbox, manager, initExperimentAPI, cleanup } = await NimbusTestUtils.setupTest({ - storePath, init: false, + storePath: await NimbusTestUtils.createStoreWith(store => { + NimbusTestUtils.addEnrollmentForRecipe( + NimbusTestUtils.factories.recipe("foo"), + { store, branchSlug: "control" } + ); + NimbusTestUtils.addEnrollmentForRecipe( + NimbusTestUtils.factories.recipe("bar", { isRollout: true }), + { store } + ); + NimbusTestUtils.addEnrollmentForRecipe( + NimbusTestUtils.factories.recipe("baz"), + { store, branchSlug: "control", extra: { active: false } } + ); + NimbusTestUtils.addEnrollmentForRecipe( + NimbusTestUtils.factories.recipe("qux", { isRollout: true }), + { store, extra: { active: false } } + ); + }), migrationState: NimbusTestUtils.migrationState.IMPORTED_ENROLLMENTS_TO_SQL, }); @@ -97,21 +88,16 @@ add_task(async function test_onStartup_setExperimentActive_called_db() { async function test_startup_unenroll() { Services.prefs.setBoolPref("app.shield.optoutstudies.enabled", false); - let storePath; - { - const store = NimbusTestUtils.stubs.store(); - await store.init(); - - NimbusTestUtils.addEnrollmentForRecipe( - NimbusTestUtils.factories.recipe("startup_unenroll"), - { store, branchSlug: "control" } - ); - - storePath = await NimbusTestUtils.saveStore(store); - } - const { sandbox, manager, initExperimentAPI, cleanup } = - await NimbusTestUtils.setupTest({ storePath, init: false }); + await NimbusTestUtils.setupTest({ + init: false, + storePath: await NimbusTestUtils.createStoreWith(store => { + NimbusTestUtils.addEnrollmentForRecipe( + NimbusTestUtils.factories.recipe("startup_unenroll"), + { store, branchSlug: "control" } + ); + }), + }); sandbox.spy(manager, "_unenroll"); diff --git a/toolkit/components/nimbus/test/unit/test_ExperimentStore.js b/toolkit/components/nimbus/test/unit/test_ExperimentStore.js @@ -53,12 +53,7 @@ add_task(async function test_usageBeforeInitialization() { }); async function test_initOnUpdateEventsFire() { - let storePath; - - { - const store = NimbusTestUtils.stubs.store(); - await store.init(); - + const storePath = await NimbusTestUtils.createStoreWith(store => { NimbusTestUtils.addEnrollmentForRecipe( NimbusTestUtils.factories.recipe.withFeatureConfig("testFeature-1", { featureId: "testFeature", @@ -120,13 +115,11 @@ async function test_initOnUpdateEventsFire() { }), { store } ); - - storePath = await NimbusTestUtils.saveStore(store); - } + }); const { sandbox, initExperimentAPI, cleanup } = await setupTest({ - storePath, init: false, + storePath, migrationState: NimbusTestUtils.migrationState.IMPORTED_ENROLLMENTS_TO_SQL, }); @@ -893,25 +886,17 @@ add_task(async function test_cleanupOldRecipes() { }); async function test_restore() { - let storePath; - { - const store = NimbusTestUtils.stubs.store(); - await store.init(); - - NimbusTestUtils.addEnrollmentForRecipe( - NimbusTestUtils.factories.recipe("experiment"), - { store, branchSlug: "control" } - ); - NimbusTestUtils.addEnrollmentForRecipe( - NimbusTestUtils.factories.recipe("rollout", { isRollout: true }), - { store } - ); - - storePath = await NimbusTestUtils.saveStore(store); - } - const { store, cleanup } = await setupTest({ - storePath, + storePath: await NimbusTestUtils.createStoreWith(store => { + NimbusTestUtils.addEnrollmentForRecipe( + NimbusTestUtils.factories.recipe("experiment"), + { store, branchSlug: "control" } + ); + NimbusTestUtils.addEnrollmentForRecipe( + NimbusTestUtils.factories.recipe("rollout", { isRollout: true }), + { store } + ); + }), migrationState: NimbusTestUtils.migrationState.IMPORTED_ENROLLMENTS_TO_SQL, }); @@ -934,12 +919,7 @@ add_task(async function test_restore_db() { async function test_restoreDatabaseConsistency(primary = "jsonfile") { Services.fog.testResetFOG(); - let storePath; - - { - const store = await NimbusTestUtils.stubs.store(); - await store.init(); - + const storePath = await NimbusTestUtils.createStoreWith(store => { const experimentRecipe = NimbusTestUtils.factories.recipe.withFeatureConfig( "experiment", { featureId: "no-feature-firefox-desktop" } @@ -962,9 +942,9 @@ async function test_restoreDatabaseConsistency(primary = "jsonfile") { store, extra: { active: false }, }); + }); - storePath = await NimbusTestUtils.saveStore(store); - + { // We should expect to see one successful databaseWrite event. const events = Glean.nimbusEvents.databaseWrite .testGetValue("events") diff --git a/toolkit/components/nimbus/test/unit/test_Migrations.js b/toolkit/components/nimbus/test/unit/test_Migrations.js @@ -866,22 +866,26 @@ add_task(async function test_migration_firefoxLabsEnrollments_idempotent() { const recipes = mockLabsRecipes("true"); - // Get the store into a partially migrated state (i.e., we have enrolled in at least one - // experiment but the migration pref has not updated). - { - const manager = NimbusTestUtils.stubs.manager(); - await manager.store.init(); - await manager.onStartup(); - - manager.enroll(recipes[0], "rs-loader", { branchSlug: "control" }); - - await NimbusTestUtils.saveStore(manager.store); - - removePrefObservers(manager); - assertNoObservers(manager); - } - const { manager, cleanup } = await setupTest({ + storePath: await NimbusTestUtils.createStoreWith(store => { + // Get the store into a partially migrated state (i.e., we have enrolled in at least one + // experiment but the migration pref has not updated). + NimbusTestUtils.addEnrollmentForRecipe(recipes[0], { + store, + branchSlug: "control", + extra: { + prefs: [ + { + name: prefs[0], + featureId: recipes[0].featureIds[0], + variable: "enabled", + branch: "user", + originalValue: false, + }, + ], + }, + }); + }), experiments: recipes, migrations: { [NimbusMigrations.Phase.AFTER_REMOTE_SETTINGS_UPDATE]: [ @@ -933,7 +937,6 @@ async function testMigrateEnrollmentsToSql(primary = "jsonfile") { }, }, }; - let storePath; const experiments = [ NimbusTestUtils.factories.recipe.withFeatureConfig( @@ -1041,11 +1044,7 @@ async function testMigrateEnrollmentsToSql(primary = "jsonfile") { ), ]; - { - const store = NimbusTestUtils.stubs.store(); - - await store.init(); - + const storePath = await NimbusTestUtils.createStoreWith(store => { store.set( "inactive-1", NimbusTestUtils.factories.experiment.withFeatureConfig( @@ -1194,9 +1193,7 @@ async function testMigrateEnrollmentsToSql(primary = "jsonfile") { } ) ); - - storePath = await NimbusTestUtils.saveStore(store); - } + }); let importMigrationError = null; diff --git a/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader_updateRecipes.js b/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader_updateRecipes.js @@ -472,24 +472,16 @@ add_task(async function test_updateRecipes_simpleFeatureInvalidAfterUpdate() { async function test_updateRecipes_invalidFeatureAfterUpdate() { const featureConfig = { featureId: "bogus", value: {} }; - let storePath; - { - const store = NimbusTestUtils.stubs.store(); - await store.init(); - - await NimbusTestUtils.addEnrollmentForRecipe( - NimbusTestUtils.factories.recipe.withFeatureConfig( - "recipe", - featureConfig - ), - { store } - ); - - storePath = await NimbusTestUtils.saveStore(store); - } - const { manager, cleanup } = await setupTest({ - storePath, + storePath: await NimbusTestUtils.createStoreWith(store => { + NimbusTestUtils.addEnrollmentForRecipe( + NimbusTestUtils.factories.recipe.withFeatureConfig( + "recipe", + featureConfig + ), + { store } + ); + }), experiments: [ NimbusTestUtils.factories.recipe.withFeatureConfig( "recipe", diff --git a/toolkit/components/nimbus/test/unit/test_policy.js b/toolkit/components/nimbus/test/unit/test_policy.js @@ -54,26 +54,18 @@ async function doTest({ "Policy engine is active" ); - let storePath = undefined; - if (existingEnrollments) { - const store = NimbusTestUtils.stubs.store(); - await store.init(); - - for (const slug of existingEnrollments) { - NimbusTestUtils.addEnrollmentForRecipe( - RECIPES.find(e => e.slug === slug), - { store } - ); - } - - storePath = await NimbusTestUtils.saveStore(store); - } - const { initExperimentAPI, cleanup, loader } = await NimbusTestUtils.setupTest({ init: false, + storePath: await NimbusTestUtils.createStoreWith(store => { + for (const slug of existingEnrollments) { + NimbusTestUtils.addEnrollmentForRecipe( + RECIPES.find(e => e.slug === slug), + { store } + ); + } + }), experiments: RECIPES, - storePath, }); sinon.spy(loader, "updateRecipes"); diff --git a/toolkit/components/nimbus/test/unit/test_prefFlips.js b/toolkit/components/nimbus/test/unit/test_prefFlips.js @@ -2381,32 +2381,23 @@ async function test_prefFlips_restore_unenroll() { } ); - // Set up a previous ExperimentStore on disk. - let storePath; - { - const store = NimbusTestUtils.stubs.store(); - await store.init(); - - await NimbusTestUtils.addEnrollmentForRecipe(recipe, { - store, - extra: { - source: "rs-loader", - prefFlips: { - originalValues: { - "test.pref.please.ignore": null, + const { manager, cleanup } = await setupTest({ + storePath: await NimbusTestUtils.createStoreWith(store => { + NimbusTestUtils.addEnrollmentForRecipe(recipe, { + store, + extra: { + source: "rs-loader", + prefFlips: { + originalValues: { + "test.pref.please.ignore": null, + }, }, }, - }, - }); - - storePath = await NimbusTestUtils.saveStore(store); - } - - // Set the pref controlled by the experiment. - Services.prefs.setStringPref("test.pref.please.ignore", "test-value"); + }); - const { manager, cleanup } = await setupTest({ - storePath, + // Set the pref controlled by the experiment. + Services.prefs.setStringPref("test.pref.please.ignore", "test-value"); + }), secureExperiments: [recipe], migrationState: NimbusTestUtils.migrationState.IMPORTED_ENROLLMENTS_TO_SQL, }); @@ -2873,17 +2864,12 @@ add_task(async function test_prefFlips_update_failure() { }); async function test_prefFlips_restore() { - let storePath; - const PREF_1 = "pref.one"; const PREF_2 = "pref.two"; const PREF_3 = "pref.three"; const PREF_4 = "pref.FOUR"; - { - const store = NimbusTestUtils.stubs.store(); - await store.init(); - + const storePath = await NimbusTestUtils.createStoreWith(store => { NimbusTestUtils.addEnrollmentForRecipe( NimbusTestUtils.factories.recipe.withFeatureConfig( "rollout-1", @@ -2971,9 +2957,7 @@ async function test_prefFlips_restore() { }, } ); - - storePath = await NimbusTestUtils.saveStore(store); - } + }); const { manager, cleanup } = await setupTest({ storePath, @@ -3045,13 +3029,9 @@ add_task(async function test_prefFlips_restore_db() { }); async function test_prefFlips_restore_failure_conflict() { - let storePath; - const PREF = "pref.foo.bar"; - { - const store = NimbusTestUtils.stubs.store(); - await store.init(); + const storePath = await NimbusTestUtils.createStoreWith(store => { NimbusTestUtils.addEnrollmentForRecipe( NimbusTestUtils.factories.recipe.withFeatureConfig("rollout-1", { featureId: FEATURE_ID, @@ -3122,9 +3102,7 @@ async function test_prefFlips_restore_failure_conflict() { }, } ); - - storePath = await NimbusTestUtils.saveStore(store); - } + }); const { manager, cleanup } = await setupTest({ storePath,