tor-browser

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

test_addons_engine.js (7987B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const { AddonManager } = ChromeUtils.importESModule(
      7  "resource://gre/modules/AddonManager.sys.mjs"
      8 );
      9 const { CHANGE_INSTALLED } = ChromeUtils.importESModule(
     10  "resource://services-sync/addonsreconciler.sys.mjs"
     11 );
     12 const { AddonsEngine } = ChromeUtils.importESModule(
     13  "resource://services-sync/engines/addons.sys.mjs"
     14 );
     15 const { Service } = ChromeUtils.importESModule(
     16  "resource://services-sync/service.sys.mjs"
     17 );
     18 
     19 Services.prefs.setStringPref(
     20  "extensions.getAddons.get.url",
     21  "http://localhost:8888/search/guid:%IDS%"
     22 );
     23 Services.prefs.setBoolPref("extensions.install.requireSecureOrigin", false);
     24 
     25 let engine;
     26 let syncID;
     27 let reconciler;
     28 let tracker;
     29 
     30 AddonTestUtils.init(this);
     31 
     32 const ADDON_ID = "addon1@tests.mozilla.org";
     33 const XPI = AddonTestUtils.createTempWebExtensionFile({
     34  manifest: {
     35    name: "Test 1",
     36    description: "Test Description",
     37    browser_specific_settings: { gecko: { id: ADDON_ID } },
     38  },
     39 });
     40 
     41 async function resetReconciler() {
     42  reconciler._addons = {};
     43  reconciler._changes = [];
     44 
     45  await reconciler.saveState();
     46 
     47  await tracker.clearChangedIDs();
     48 }
     49 
     50 add_task(async function setup() {
     51  AddonTestUtils.createAppInfo(
     52    "xpcshell@tests.mozilla.org",
     53    "XPCShell",
     54    "1",
     55    "1.9.2"
     56  );
     57  AddonTestUtils.overrideCertDB();
     58  await AddonTestUtils.promiseStartupManager();
     59 
     60  await Service.engineManager.register(AddonsEngine);
     61  engine = Service.engineManager.get("addons");
     62  syncID = await engine.resetLocalSyncID();
     63  reconciler = engine._reconciler;
     64  tracker = engine._tracker;
     65 
     66  reconciler.startListening();
     67 
     68  // Don't flush to disk in the middle of an event listener!
     69  // This causes test hangs on WinXP.
     70  reconciler._shouldPersist = false;
     71 
     72  await resetReconciler();
     73 });
     74 
     75 // This is a basic sanity test for the unit test itself. If this breaks, the
     76 // add-ons API likely changed upstream.
     77 add_task(async function test_addon_install() {
     78  _("Ensure basic add-on APIs work as expected.");
     79 
     80  let install = await AddonManager.getInstallForFile(XPI);
     81  Assert.notEqual(install, null);
     82  Assert.equal(install.type, "extension");
     83  Assert.equal(install.name, "Test 1");
     84 
     85  await resetReconciler();
     86 });
     87 
     88 add_task(async function test_find_dupe() {
     89  _("Ensure the _findDupe() implementation is sane.");
     90 
     91  // This gets invoked at the top of sync, which is bypassed by this
     92  // test, so we do it manually.
     93  await engine._refreshReconcilerState();
     94 
     95  let addon = await installAddon(XPI, reconciler);
     96 
     97  let record = {
     98    id: Utils.makeGUID(),
     99    addonID: ADDON_ID,
    100    enabled: true,
    101    applicationID: Services.appinfo.ID,
    102    source: "amo",
    103  };
    104 
    105  let dupe = await engine._findDupe(record);
    106  Assert.equal(addon.syncGUID, dupe);
    107 
    108  record.id = addon.syncGUID;
    109  dupe = await engine._findDupe(record);
    110  Assert.equal(null, dupe);
    111 
    112  await uninstallAddon(addon, reconciler);
    113  await resetReconciler();
    114 });
    115 
    116 add_task(async function test_get_changed_ids() {
    117  let timerPrecision = Services.prefs.getBoolPref(
    118    "privacy.reduceTimerPrecision"
    119  );
    120  Services.prefs.setBoolPref("privacy.reduceTimerPrecision", false);
    121 
    122  registerCleanupFunction(function () {
    123    Services.prefs.setBoolPref("privacy.reduceTimerPrecision", timerPrecision);
    124  });
    125 
    126  _("Ensure getChangedIDs() has the appropriate behavior.");
    127 
    128  _("Ensure getChangedIDs() returns an empty object by default.");
    129  let changes = await engine.getChangedIDs();
    130  Assert.equal("object", typeof changes);
    131  Assert.equal(0, Object.keys(changes).length);
    132 
    133  _("Ensure tracker changes are populated.");
    134  let now = new Date();
    135  let changeTime = now.getTime() / 1000;
    136  let guid1 = Utils.makeGUID();
    137  await tracker.addChangedID(guid1, changeTime);
    138 
    139  changes = await engine.getChangedIDs();
    140  Assert.equal("object", typeof changes);
    141  Assert.equal(1, Object.keys(changes).length);
    142  Assert.ok(guid1 in changes);
    143  Assert.equal(changeTime, changes[guid1]);
    144 
    145  await tracker.clearChangedIDs();
    146 
    147  _("Ensure reconciler changes are populated.");
    148  let addon = await installAddon(XPI, reconciler);
    149  await tracker.clearChangedIDs(); // Just in case.
    150  changes = await engine.getChangedIDs();
    151  Assert.equal("object", typeof changes);
    152  Assert.equal(1, Object.keys(changes).length);
    153  Assert.ok(addon.syncGUID in changes);
    154  _(
    155    "Change time: " + changeTime + ", addon change: " + changes[addon.syncGUID]
    156  );
    157  Assert.greaterOrEqual(changes[addon.syncGUID], changeTime);
    158 
    159  let oldTime = changes[addon.syncGUID];
    160  let guid2 = addon.syncGUID;
    161  await uninstallAddon(addon, reconciler);
    162  changes = await engine.getChangedIDs();
    163  Assert.equal(1, Object.keys(changes).length);
    164  Assert.ok(guid2 in changes);
    165  Assert.greater(changes[guid2], oldTime);
    166 
    167  _("Ensure non-syncable add-ons aren't picked up by reconciler changes.");
    168  reconciler._addons = {};
    169  reconciler._changes = [];
    170  let record = {
    171    id: "DUMMY",
    172    guid: Utils.makeGUID(),
    173    enabled: true,
    174    installed: true,
    175    modified: new Date(),
    176    type: "UNSUPPORTED",
    177    scope: 0,
    178    foreignInstall: false,
    179  };
    180  reconciler.addons.DUMMY = record;
    181  await reconciler._addChange(record.modified, CHANGE_INSTALLED, record);
    182 
    183  changes = await engine.getChangedIDs();
    184  _(JSON.stringify(changes));
    185  Assert.equal(0, Object.keys(changes).length);
    186 
    187  await resetReconciler();
    188 });
    189 
    190 add_task(async function test_disabled_install_semantics() {
    191  _("Ensure that syncing a disabled add-on preserves proper state.");
    192 
    193  // This is essentially a test for bug 712542, which snuck into the original
    194  // add-on sync drop. It ensures that when an add-on is installed that the
    195  // disabled state and incoming syncGUID is preserved, even on the next sync.
    196  const USER = "foo";
    197  const PASSWORD = "password";
    198 
    199  let server = new SyncServer();
    200  server.start();
    201  await SyncTestingInfrastructure(server, USER, PASSWORD);
    202 
    203  await generateNewKeys(Service.collectionKeys);
    204 
    205  let contents = {
    206    meta: {
    207      global: { engines: { addons: { version: engine.version, syncID } } },
    208    },
    209    crypto: {},
    210    addons: {},
    211  };
    212 
    213  server.registerUser(USER, "password");
    214  server.createContents(USER, contents);
    215 
    216  let amoServer = new HttpServer();
    217  amoServer.registerFile(
    218    "/search/guid:addon1%40tests.mozilla.org",
    219    do_get_file("addon1-search.json")
    220  );
    221 
    222  amoServer.registerFile("/addon1.xpi", XPI);
    223  amoServer.start(8888);
    224 
    225  // Insert an existing record into the server.
    226  let id = Utils.makeGUID();
    227  let now = Date.now() / 1000;
    228 
    229  let record = encryptPayload({
    230    id,
    231    applicationID: Services.appinfo.ID,
    232    addonID: ADDON_ID,
    233    enabled: false,
    234    deleted: false,
    235    source: "amo",
    236  });
    237  let wbo = new ServerWBO(id, record, now - 2);
    238  server.insertWBO(USER, "addons", wbo);
    239 
    240  _("Performing sync of add-ons engine.");
    241  await engine._sync();
    242 
    243  // At this point the non-restartless extension should be staged for install.
    244 
    245  // Don't need this server any more.
    246  await promiseStopServer(amoServer);
    247 
    248  // We ensure the reconciler has recorded the proper ID and enabled state.
    249  let addon = reconciler.getAddonStateFromSyncGUID(id);
    250  Assert.notEqual(null, addon);
    251  Assert.equal(false, addon.enabled);
    252 
    253  // We fake an app restart and perform another sync, just to make sure things
    254  // are sane.
    255  await AddonTestUtils.promiseRestartManager();
    256 
    257  let collection = server.getCollection(USER, "addons");
    258  engine.lastModified = collection.timestamp;
    259  await engine._sync();
    260 
    261  // The client should not upload a new record. The old record should be
    262  // retained and unmodified.
    263  Assert.equal(1, collection.count());
    264 
    265  let payload = collection.payloads()[0];
    266  Assert.notEqual(null, collection.wbo(id));
    267  Assert.equal(ADDON_ID, payload.addonID);
    268  Assert.ok(!payload.enabled);
    269 
    270  await promiseStopServer(server);
    271 });
    272 
    273 add_test(function cleanup() {
    274  // There's an xpcom-shutdown hook for this, but let's give this a shot.
    275  reconciler.stopListening();
    276  run_next_test();
    277 });