tor-browser

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

test_PreferencesBackupResource_Nimbus.js (8882B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 https://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const { _ExperimentFeature: ExperimentFeature } = ChromeUtils.importESModule(
      7  "resource://nimbus/ExperimentAPI.sys.mjs"
      8 );
      9 const { NimbusTestUtils } = ChromeUtils.importESModule(
     10  "resource://testing-common/NimbusTestUtils.sys.mjs"
     11 );
     12 const { PreferencesBackupResource } = ChromeUtils.importESModule(
     13  "resource:///modules/backup/PreferencesBackupResource.sys.mjs"
     14 );
     15 
     16 NimbusTestUtils.init(this);
     17 
     18 const USER_TYPED_FEATURE = new ExperimentFeature("test-typed-prefs", {
     19  description: "Test feature that sets each type of pref",
     20  owner: "test@test.test",
     21  hasExposure: false,
     22  variables: {
     23    string: {
     24      type: "string",
     25      description: "test string variable",
     26      setPref: {
     27        branch: "user",
     28        pref: "nimbus-test.types.string",
     29      },
     30    },
     31    int: {
     32      type: "int",
     33      description: "test int variable",
     34      setPref: {
     35        branch: "user",
     36        pref: "nimbus-test.types.int",
     37      },
     38    },
     39    boolean: {
     40      type: "boolean",
     41      description: "test boolean variable",
     42      setPref: {
     43        branch: "user",
     44        pref: "nimbus-test.types.boolean",
     45      },
     46    },
     47    json: {
     48      type: "json",
     49      description: "test json variable",
     50      setPref: {
     51        branch: "user",
     52        pref: "nimbus-test.types.json",
     53      },
     54    },
     55  },
     56 });
     57 
     58 function getPrefsFromMap(prefs) {
     59  return prefs.map(([pref, value]) => {
     60    if (!Services.prefs.prefHasUserValue(pref)) {
     61      return [pref, undefined];
     62    }
     63    switch (typeof value) {
     64      case "boolean":
     65        return [pref, Services.prefs.getBoolPref(pref)];
     66      case "number":
     67        return [pref, Services.prefs.getIntPref(pref)];
     68      case "string":
     69        return [pref, Services.prefs.getStringPref(pref)];
     70      default:
     71        throw new Error("Unsupported pref type!");
     72    }
     73  });
     74 }
     75 
     76 function setPrefsFromMap(prefs) {
     77  for (let [pref, value] of prefs) {
     78    if (value === undefined) {
     79      Services.prefs.clearUserPref(pref);
     80      continue;
     81    }
     82 
     83    switch (typeof value) {
     84      case "boolean":
     85        Services.prefs.setBoolPref(pref, value);
     86        break;
     87      case "number":
     88        Services.prefs.setIntPref(pref, value);
     89        break;
     90      case "string":
     91        Services.prefs.setStringPref(pref, value);
     92        break;
     93      default:
     94        throw new Error("Unsupported pref type!");
     95    }
     96  }
     97 }
     98 
     99 const fakeNimbusTestPrefs = [
    100  ["nimbus-test.types.boolean", false],
    101  ["nimbus-test.types.int", 100],
    102  ["nimbus-test.types.string", "bar"],
    103  ["nimbus.all_prefs_with_nimbus._prefix_should_be_removed", "not serialized"],
    104 ];
    105 
    106 // NB: Nimbus experiment setup is adapted from
    107 // test_setPref_getOriginalPrefValuesForAllExperiments.
    108 function initFakeNimbusExperimentPrefs() {
    109  const originalPrefs = getPrefsFromMap(fakeNimbusTestPrefs);
    110  setPrefsFromMap(fakeNimbusTestPrefs);
    111  return originalPrefs;
    112 }
    113 
    114 function uninitFakeNimbusExperimentPrefs(originalPrefs) {
    115  setPrefsFromMap(originalPrefs);
    116 }
    117 
    118 const jsonTestValue = {
    119  foo: "foo",
    120  bar: 12345,
    121  baz: true,
    122  qux: null,
    123  quux: ["corge"],
    124 };
    125 
    126 async function enrollFakeNimbusExperiment() {
    127  const featureCleanup = NimbusTestUtils.addTestFeatures(USER_TYPED_FEATURE);
    128 
    129  const { manager, cleanup } = await NimbusTestUtils.setupTest();
    130 
    131  const experimentCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
    132    {
    133      featureId: USER_TYPED_FEATURE.featureId,
    134      value: {
    135        string: "hello, world",
    136        int: 12345,
    137        boolean: true,
    138        json: jsonTestValue,
    139      },
    140    },
    141    { manager }
    142  );
    143 
    144  return { featureCleanup, experimentCleanup, cleanup };
    145 }
    146 
    147 async function unenrollFakeNimbusExperiment({
    148  featureCleanup,
    149  experimentCleanup,
    150  cleanup,
    151 }) {
    152  await experimentCleanup();
    153  featureCleanup();
    154  await cleanup();
    155 }
    156 
    157 function checkNimbusHasSetPrefs() {
    158  Assert.equal(
    159    Services.prefs.getPrefType("nimbus-test.types.string"),
    160    Services.prefs.PREF_STRING
    161  );
    162  Assert.equal(
    163    Services.prefs.getStringPref("nimbus-test.types.string"),
    164    "hello, world"
    165  );
    166 
    167  Assert.equal(
    168    Services.prefs.getPrefType("nimbus-test.types.int"),
    169    Services.prefs.PREF_INT
    170  );
    171  Assert.equal(Services.prefs.getIntPref("nimbus-test.types.int"), 12345);
    172 
    173  Assert.equal(
    174    Services.prefs.getPrefType("nimbus-test.types.boolean"),
    175    Services.prefs.PREF_BOOL
    176  );
    177  Assert.equal(Services.prefs.getBoolPref("nimbus-test.types.boolean"), true);
    178 
    179  Assert.equal(
    180    Services.prefs.getPrefType("nimbus-test.types.json"),
    181    Services.prefs.PREF_STRING
    182  );
    183 
    184  const parsedJson = JSON.parse(
    185    Services.prefs.getStringPref("nimbus-test.types.json")
    186  );
    187 
    188  Assert.deepEqual(jsonTestValue, parsedJson);
    189 }
    190 
    191 async function checkIgnoredBackupPrefs(backupPrefsFilePath) {
    192  let contents = (await IOUtils.readUTF8(backupPrefsFilePath))
    193    .split("\n")
    194    .map(line => line.trim());
    195 
    196  let checkPrefNotSerialized = pref => {
    197    Assert.ok(
    198      !contents.reduce((acc, line) => acc || line.includes(pref), false)
    199    );
    200  };
    201 
    202  checkPrefNotSerialized("browser.backup.");
    203 }
    204 
    205 async function checkBackupIgnoredNimbusPrefs(backupPrefsFilePath) {
    206  let contents = (await IOUtils.readUTF8(backupPrefsFilePath))
    207    .split("\n")
    208    .map(line => line.trim());
    209 
    210  Assert.ok(
    211    contents.includes('user_pref("nimbus-test.types.boolean", false);')
    212  );
    213  Assert.ok(contents.includes('user_pref("nimbus-test.types.int", 100);'));
    214  Assert.ok(contents.includes('user_pref("nimbus-test.types.string", "bar");'));
    215 
    216  let checkPrefNotSerialized = pref => {
    217    Assert.ok(
    218      !contents.reduce((acc, line) => acc || line.includes(pref), false)
    219    );
    220  };
    221 
    222  // nimbus-test.types.json had no prior value so it should not appear.
    223  checkPrefNotSerialized("nimbus-test.types.json");
    224 
    225  // nimbus metadata should not appear.
    226  checkPrefNotSerialized("app.normandy.user_id");
    227  checkPrefNotSerialized("toolkit.telemetry.cachedProfileGroupID");
    228 
    229  // Prefs that start with "nimbus." are assumed to be internal and should
    230  // not be included.
    231  checkPrefNotSerialized(
    232    "nimbus.all_prefs_with_nimbus._prefix_should_be_removed"
    233  );
    234 }
    235 
    236 async function performBackup() {
    237  let sandbox = sinon.createSandbox();
    238 
    239  let preferencesBackupResource = new PreferencesBackupResource();
    240  let sourcePath = await IOUtils.createUniqueDirectory(
    241    PathUtils.tempDir,
    242    "PreferencesBackupResource-source-test"
    243  );
    244  let stagingPath = await IOUtils.createUniqueDirectory(
    245    PathUtils.tempDir,
    246    "PreferencesBackupResource-staging-test"
    247  );
    248 
    249  const simpleCopyFiles = [
    250    { path: "xulstore.json" },
    251    { path: "containers.json" },
    252    { path: "handlers.json" },
    253    { path: "search.json.mozlz4" },
    254    { path: "user.js" },
    255    { path: ["chrome", "userChrome.css"] },
    256    { path: ["chrome", "userContent.css"] },
    257    { path: ["chrome", "childFolder", "someOtherStylesheet.css"] },
    258  ];
    259  await createTestFiles(sourcePath, simpleCopyFiles);
    260 
    261  // Create our fake database files. We don't expect these to be copied to the
    262  // staging directory in this test due to our stubbing of the backup method, so
    263  // we don't include it in `simpleCopyFiles`.
    264  await createTestFiles(sourcePath, [
    265    { path: "permissions.sqlite" },
    266    { path: "content-prefs.sqlite" },
    267  ]);
    268 
    269  // We have no need to test that Sqlite.sys.mjs's backup method is working -
    270  // this is something that is tested in Sqlite's own tests. We can just make
    271  // sure that it's being called using sinon. Unfortunately, we cannot do the
    272  // same thing with IOUtils.copy, as its methods are not stubbable.
    273  let fakeConnection = {
    274    backup: sandbox.stub().resolves(true),
    275    close: sandbox.stub().resolves(true),
    276  };
    277  sandbox.stub(Sqlite, "openConnection").returns(fakeConnection);
    278 
    279  await preferencesBackupResource.backup(stagingPath, sourcePath);
    280 
    281  return { sandbox, stagingPath, sourcePath };
    282 }
    283 
    284 /**
    285 * Test that the backup method correctly serializes preference values that are
    286 * manipulated by Nimbus experiments.  Nimbus-set prefs should ignore the
    287 * Nimbus-set values.
    288 */
    289 add_task(async function test_prefs_backup_does_not_backup_nimbus_prefs() {
    290  // Pre-set some nimbus-test.* prefs so they have originalValues that
    291  // should be backed up instead of the values Nimbus will set when we enroll
    292  // in the experiment.
    293  const originalPrefs = initFakeNimbusExperimentPrefs();
    294  const cleanup = await enrollFakeNimbusExperiment();
    295  checkNimbusHasSetPrefs();
    296  const { sandbox, stagingPath, sourcePath } = await performBackup();
    297  await checkBackupIgnoredNimbusPrefs(PathUtils.join(stagingPath, "prefs.js"));
    298  await checkIgnoredBackupPrefs(PathUtils.join(stagingPath, "prefs.js"));
    299  await maybeRemovePath(stagingPath);
    300  await maybeRemovePath(sourcePath);
    301  sandbox.restore();
    302  await unenrollFakeNimbusExperiment(cleanup);
    303  uninitFakeNimbusExperimentPrefs(originalPrefs);
    304 });