Bug 1449255: Part 1 - Fold test_bug335238 into test_update. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Tue, 27 Mar 2018 12:26:38 -0700
changeset 773465 44a1e209840968c6e7e663b919f1470d6a100531
parent 773464 a94c1e4db5b8074a63d83672423c3637de00cb6f
child 773466 213e98df838deded8908a2cf420c848ffaf80156
push id104243
push usermaglione.k@gmail.com
push dateWed, 28 Mar 2018 00:15:32 +0000
reviewersaswan
bugs1449255, 335238
milestone61.0a1
Bug 1449255: Part 1 - Fold test_bug335238 into test_update. r?aswan MozReview-Commit-ID: KLODBGSOR37
toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
toolkit/mozapps/extensions/test/xpcshell/test_bug335238.js
toolkit/mozapps/extensions/test/xpcshell/test_update.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
--- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
@@ -5,18 +5,16 @@
 /* eslint "mozilla/no-aArgs": 1 */
 /* eslint "no-unused-vars": [2, {"args": "none", "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$"}] */
 /* eslint "semi": [2, "always"] */
 /* eslint "valid-jsdoc": [2, {requireReturn: false}] */
 
 var EXPORTED_SYMBOLS = ["AddonTestUtils", "MockAsyncShutdown"];
 
 const CERTDB_CONTRACTID = "@mozilla.org/security/x509certdb;1";
-const CERTDB_CID = Components.ID("{fb0bbc5c-452e-4783-b32c-80124693d871}");
-
 
 Cu.importGlobalProperties(["fetch", "TextEncoder"]);
 
 ChromeUtils.import("resource://gre/modules/AsyncShutdown.jsm");
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -30,16 +28,18 @@ XPCOMUtils.defineLazyGetter(this, "Manag
   let {Management} = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
   return Management;
 });
 
 ChromeUtils.defineModuleGetter(this, "FileTestUtils",
                                "resource://testing-common/FileTestUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "HttpServer",
                                "resource://testing-common/httpd.js");
+ChromeUtils.defineModuleGetter(this, "MockRegistrar",
+                               "resource://testing-common/MockRegistrar.jsm");
 
 XPCOMUtils.defineLazyServiceGetters(this, {
   aomStartup: ["@mozilla.org/addons/addon-manager-startup;1", "amIAddonManagerStartup"],
   proxyService: ["@mozilla.org/network/protocol-proxy-service;1", "nsIProtocolProxyService"],
   rdfService: ["@mozilla.org/rdf/rdf-service;1", "nsIRDFService"],
   uuidGen: ["@mozilla.org/uuid-generator;1", "nsIUUIDGenerator"],
 });
 
@@ -97,16 +97,63 @@ var MockAsyncShutdown = {
     }
   },
   // We can use the real Barrier
   Barrier: AsyncShutdown.Barrier,
 };
 
 AMscope.AsyncShutdown = MockAsyncShutdown;
 
+class MockBlocklist {
+  constructor(addons) {
+    if (ChromeUtils.getClassName(addons) === "Object") {
+      addons = new Map(Object.entries(addons));
+    }
+    this.addons = addons;
+  }
+
+  get contractID() {
+    return "@mozilla.org/extensions/blocklist;1";
+  }
+
+  register() {
+    this.originalCID = MockRegistrar.register(this.contractID, this);
+  }
+
+  unregister() {
+    MockRegistrar.unregister(this.originalCID);
+  }
+
+  getAddonBlocklistState(addon, appVersion, toolkitVersion) {
+    return this.addons.get(addon.id, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+  }
+
+  getAddonBlocklistEntry(addon, appVersion, toolkitVersion) {
+    let state = this.getAddonBlocklistState(addon, appVersion, toolkitVersion);
+    if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
+      return {
+        state,
+        url: "http://example.com/",
+      };
+    }
+    return null;
+  }
+
+  getPluginBlocklistState(plugin, version, appVersion, toolkitVersion) {
+    return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+  }
+
+  isAddonBlocklisted(addon, appVersion, toolkitVersion) {
+    return this.getAddonBlocklistState(addon, appVersion, toolkitVersion) ==
+           Ci.nsIBlocklistService.STATE_BLOCKED;
+  }
+}
+
+MockBlocklist.prototype.QueryInterface = XPCOMUtils.generateQI(["nsIBlocklistService"]);
+
 
 /**
  * Escapes any occurances of &, ", < or > with XML entities.
  *
  * @param {string} str
  *        The string to escape.
  * @return {string} The escaped string.
  */
@@ -567,26 +614,16 @@ var AddonTestUtils = {
     } catch (e) {
       // IDs for WebExtensions are extracted from the certificate when
       // not present in the manifest, so just generate a random one.
       return uuidGen.generateUUID().number;
     }
   },
 
   overrideCertDB() {
-    // Unregister the real database. This only works because the add-ons manager
-    // hasn't started up and grabbed the certificate database yet.
-    let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-    let factory = registrar.getClassObject(CERTDB_CID, Ci.nsIFactory);
-    registrar.unregisterFactory(CERTDB_CID, factory);
-
-    // Get the real DB
-    let realCertDB = factory.createInstance(null, Ci.nsIX509CertDB);
-
-
     let verifyCert = async (file, result, cert, callback) => {
       if (result == Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED &&
           !this.useRealCertChecks && callback.wrappedJSObject) {
         // Bypassing XPConnect allows us to create a fake x509 certificate from JS
         callback = callback.wrappedJSObject;
 
         try {
           let manifestURI = this.getManifestURI(file);
@@ -610,53 +647,73 @@ var AddonTestUtils = {
           if (file.isFile())
             Services.obs.notifyObservers(file, "flush-cache-entry", "cert-override");
         }
       }
 
       return [callback, result, cert];
     };
 
-
-    function FakeCertDB() {
-      for (let property of Object.keys(realCertDB)) {
-        if (property in this)
-          continue;
+    let FakeCertDB = {
+      init() {
+        for (let property of Object.keys(this._genuine.QueryInterface(Ci.nsIX509CertDB))) {
+          if (property in this)
+            continue;
 
-        if (typeof realCertDB[property] == "function")
-          this[property] = realCertDB[property].bind(realCertDB);
-      }
-    }
-    FakeCertDB.prototype = {
+          if (typeof this._genuine[property] == "function")
+            this[property] = this._genuine[property].bind(this._genuine);
+        }
+      },
+
       openSignedAppFileAsync(root, file, callback) {
         // First try calling the real cert DB
-        realCertDB.openSignedAppFileAsync(root, file, (result, zipReader, cert) => {
+        this._genuine.openSignedAppFileAsync(root, file, (result, zipReader, cert) => {
           verifyCert(file.clone(), result, cert, callback)
             .then(([callback, result, cert]) => {
               callback.openSignedAppFileFinished(result, zipReader, cert);
             });
         });
       },
 
       verifySignedDirectoryAsync(root, dir, callback) {
         // First try calling the real cert DB
-        realCertDB.verifySignedDirectoryAsync(root, dir, (result, cert) => {
+        this._genuine.verifySignedDirectoryAsync(root, dir, (result, cert) => {
           verifyCert(dir.clone(), result, cert, callback)
             .then(([callback, result, cert]) => {
               callback.verifySignedDirectoryFinished(result, cert);
             });
         });
       },
 
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIX509CertDB]),
     };
 
-    let certDBFactory = XPCOMUtils.generateSingletonFactory(FakeCertDB);
-    registrar.registerFactory(CERTDB_CID, "CertDB",
-                              CERTDB_CONTRACTID, certDBFactory);
+    // Unregister the real database. This only works because the add-ons manager
+    // hasn't started up and grabbed the certificate database yet.
+    MockRegistrar.register(CERTDB_CONTRACTID, FakeCertDB);
+
+    // Initialize the mock service.
+    Cc[CERTDB_CONTRACTID].getService();
+    FakeCertDB.init();
+  },
+
+  /**
+   * Overrides the blocklist service, and returns the given blocklist
+   * states for the given add-ons.
+   *
+   * @param {object|Map} addons
+   *        A mapping of add-on IDs to their blocklist states.
+   * @returns {MockBlocklist}
+   *        A mock blocklist service, which should be unregistered when
+   *        the test is complete.
+   */
+  overrideBlocklist(addons) {
+    let mock = new MockBlocklist(addons);
+    mock.register();
+    return mock;
   },
 
   /**
    * Starts up the add-on manager as if it was started by the application.
    *
    * @param {boolean} [appChanged = true]
    *        An optional boolean parameter to simulate the case where the
    *        application has changed version since the last run. If not passed it
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug335238.js
+++ /dev/null
@@ -1,227 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-// Disables security checking our updates which haven't been signed
-Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
-
-ChromeUtils.import("resource://testing-common/MockRegistrar.jsm");
-
-// This is the data we expect to see sent as part of the update url.
-var EXPECTED = [
-  {
-    id: "bug335238_1@tests.mozilla.org",
-    version: "1.3.4",
-    maxAppVersion: "5",
-    status: "userEnabled",
-    appId: "xpcshell@tests.mozilla.org",
-    appVersion: "1",
-    appOs: "XPCShell",
-    appAbi: "noarch-spidermonkey",
-    locale: "en-US",
-    reqVersion: "2"
-  },
-  {
-    id: "bug335238_2@tests.mozilla.org",
-    version: "28at",
-    maxAppVersion: "7",
-    status: "userDisabled",
-    appId: "xpcshell@tests.mozilla.org",
-    appVersion: "1",
-    appOs: "XPCShell",
-    appAbi: "noarch-spidermonkey",
-    locale: "en-US",
-    reqVersion: "2"
-  },
-  {
-    id: "bug335238_3@tests.mozilla.org",
-    version: "58",
-    maxAppVersion: "*",
-    status: "userDisabled,softblocked",
-    appId: "xpcshell@tests.mozilla.org",
-    appVersion: "1",
-    appOs: "XPCShell",
-    appAbi: "noarch-spidermonkey",
-    locale: "en-US",
-    reqVersion: "2"
-  },
-  {
-    id: "bug335238_4@tests.mozilla.org",
-    version: "4",
-    maxAppVersion: "2+",
-    status: "userEnabled,blocklisted",
-    appId: "xpcshell@tests.mozilla.org",
-    appVersion: "1",
-    appOs: "XPCShell",
-    appAbi: "noarch-spidermonkey",
-    locale: "en-US",
-    reqVersion: "2"
-  }
-];
-
-const MANIFESTS = [
-  {
-    id: "bug335238_1@tests.mozilla.org",
-    version: "1.3.4",
-    name: "Bug 335238",
-    updateURL: "http://example.com/0?id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appId=%APP_ID%&appVersion=%APP_VERSION%&appOs=%APP_OS%&appAbi=%APP_ABI%&locale=%APP_LOCALE%&reqVersion=%REQ_VERSION%",
-    bootstrap: true,
-
-    targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "5"}],
-  },
-  {
-    id: "bug335238_2@tests.mozilla.org",
-    version: "28at",
-    name: "Bug 335238",
-    updateURL: "http://example.com/1?id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appId=%APP_ID%&appVersion=%APP_VERSION%&appOs=%APP_OS%&appAbi=%APP_ABI%&locale=%APP_LOCALE%&reqVersion=%REQ_VERSION%",
-    bootstrap: true,
-
-    targetApplications: [{
-      id: "xpcshell@tests.mozilla.org",
-      minVersion: "1",
-      maxVersion: "7"}],
-  },
-  {
-    id: "bug335238_3@tests.mozilla.org",
-    version: "58",
-    name: "Bug 335238",
-    updateURL: "http://example.com/2?id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appId=%APP_ID%&appVersion=%APP_VERSION%&appOs=%APP_OS%&appAbi=%APP_ABI%&locale=%APP_LOCALE%&reqVersion=%REQ_VERSION%",
-    bootstrap: true,
-
-    targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "*"}],
-  },
-  {
-    id: "bug335238_4@tests.mozilla.org",
-    version: "4",
-    name: "Bug 335238",
-    updateURL: "http://example.com/3?id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appId=%APP_ID%&appVersion=%APP_VERSION%&appOs=%APP_OS%&appAbi=%APP_ABI%&locale=%APP_LOCALE%&reqVersion=%REQ_VERSION%",
-    bootstrap: true,
-
-    targetApplications: [{
-        id: "xpcshell@tests.mozilla.org",
-        minVersion: "1",
-        maxVersion: "2+"}],
-  },
-];
-
-const XPIS = MANIFESTS.map(manifest => createTempXPIFile(manifest));
-
-var ADDONS = [
-  {id: "bug335238_1@tests.mozilla.org",
-   addon: XPIS[0]},
-  {id: "bug335238_2@tests.mozilla.org",
-   addon: XPIS[1]},
-  {id: "bug335238_3@tests.mozilla.org",
-   addon: XPIS[2]},
-  {id: "bug335238_4@tests.mozilla.org",
-   addon: XPIS[3]}
-];
-
-// This is a replacement for the blocklist service
-var BlocklistService = {
-  getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion) {
-    if (aAddon.id == "bug335238_3@tests.mozilla.org")
-      return Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
-    if (aAddon.id == "bug335238_4@tests.mozilla.org")
-      return Ci.nsIBlocklistService.STATE_BLOCKED;
-    return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
-  },
-
-  getAddonBlocklistEntry(aAddon, aAppVersion, aToolkitVersion) {
-    let state = this.getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion);
-    if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
-      return {
-        state,
-        url: "http://example.com/",
-      };
-    }
-    return null;
-  },
-
-  getPluginBlocklistState(aPlugin, aVersion, aAppVersion, aToolkitVersion) {
-    return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
-  },
-
-  isAddonBlocklisted(aAddon, aAppVersion, aToolkitVersion) {
-    return this.getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion) ==
-           Ci.nsIBlocklistService.STATE_BLOCKED;
-  },
-
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsIBlocklistService)
-     || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
-};
-
-MockRegistrar.register("@mozilla.org/extensions/blocklist;1", BlocklistService);
-
-var server;
-
-var updateListener = {
-  pendingCount: 0,
-
-  onUpdateAvailable(aAddon) {
-    do_throw("Should not have seen an update for " + aAddon.id);
-  },
-
-  onUpdateFinished() {
-    if (--this.pendingCount == 0)
-      do_test_finished();
-  }
-};
-
-var requestHandler = {
-  handle(metadata, response) {
-    var expected = EXPECTED[metadata.path.substring(1)];
-    var params = metadata.queryString.split("&");
-    Assert.equal(params.length, 10);
-    for (var k in params) {
-      var pair = params[k].split("=");
-      var name = decodeURIComponent(pair[0]);
-      var value = decodeURIComponent(pair[1]);
-      Assert.equal(expected[name], value);
-    }
-    response.setStatusLine(metadata.httpVersion, 404, "Not Found");
-  }
-};
-
-function run_test() {
-  do_test_pending();
-  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
-
-  server = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
-  server.registerPathHandler("/0", requestHandler);
-  server.registerPathHandler("/1", requestHandler);
-  server.registerPathHandler("/2", requestHandler);
-  server.registerPathHandler("/3", requestHandler);
-
-  Services.locale.setRequestedLocales(["en-US"]);
-
-  startupManager();
-  installAllFiles(ADDONS.map(a => a.addon), function() {
-
-    restartManager();
-    AddonManager.getAddonByID(ADDONS[1].id, callback_soon(function(addon) {
-      Assert.ok(!(!addon));
-      addon.userDisabled = true;
-      restartManager();
-
-      AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), function(installedItems) {
-        installedItems.forEach(function(item) {
-          updateListener.pendingCount++;
-          item.findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
-        });
-      });
-    }));
-  });
-}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update.js
@@ -552,22 +552,80 @@ const PARAM_ADDONS = {
       item_version: "1.0",
       item_maxappversion: "1",
       item_status: "userEnabled",
       app_version: "1",
       update_type: "99",
     },
     updateType: [AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED],
   },
+
+  "blocklist1@tests.mozilla.org": {
+    "install.rdf": {
+      id: "blocklist1@tests.mozilla.org",
+      version: "5.0",
+      bootstrap: true,
+      updateURL: "http://example.com/data/param_test.json" + PARAMS,
+      targetApplications: [{
+        id: appId,
+        minVersion: "1",
+        maxVersion: "2"
+      }],
+      name: "Test Addon 1",
+    },
+    params: {
+      item_version: "5.0",
+      item_maxappversion: "2",
+      item_status: "userDisabled,softblocked",
+      app_version: "1",
+      update_type: "97",
+    },
+    updateType: [AddonManager.UPDATE_WHEN_USER_REQUESTED],
+    blocklistState: "STATE_SOFTBLOCKED",
+  },
+
+  "blocklist2@tests.mozilla.org": {
+    "install.rdf": {
+      id: "blocklist2@tests.mozilla.org",
+      version: "5.0",
+      bootstrap: true,
+      updateURL: "http://example.com/data/param_test.json" + PARAMS,
+      targetApplications: [{
+        id: appId,
+        minVersion: "1",
+        maxVersion: "2"
+      }],
+      name: "Test Addon 1",
+    },
+    params: {
+      item_version: "5.0",
+      item_maxappversion: "2",
+      item_status: "userEnabled,blocklisted",
+      app_version: "1",
+      update_type: "97",
+    },
+    updateType: [AddonManager.UPDATE_WHEN_USER_REQUESTED],
+    blocklistState: "STATE_BLOCKED",
+  },
 };
 
 const PARAM_IDS = Object.keys(PARAM_ADDONS);
 
 // Verify the parameter escaping in update urls.
 add_task(async function test_8() {
+  let blocklistAddons = new Map();
+  for (let [id, options] of Object.entries(PARAM_ADDONS)) {
+    if (options.blocklistState) {
+      blocklistAddons.set(id, Ci.nsIBlocklistService[options.blocklistState]);
+    }
+  }
+  let mockBlocklist = await AddonTestUtils.overrideBlocklist(blocklistAddons);
+
+  await promiseRestartManager();
+
   for (let [id, options] of Object.entries(PARAM_ADDONS)) {
     await promiseInstallXPI(options["install.rdf"], profileDir);
 
     if (options.initialState) {
       let addon = await AddonManager.getAddonByID(id);
       Object.assign(addon, options.initialState);
     }
   }
@@ -618,16 +676,18 @@ add_task(async function test_8() {
     for (let [prop, value] of Object.entries(expected)) {
       equal(params.get(prop), value, `Expected value for ${prop}`);
     }
   }
 
   for (let [, addon] of await getAddons(PARAM_IDS)) {
     addon.uninstall();
   }
+
+  await mockBlocklist.unregister();
 });
 
 // Tests that if an install.rdf claims compatibility then the add-on will be
 // seen as compatible regardless of what the update.rdf says.
 add_task(async function test_9() {
   await promiseInstallXPI({
     id: "addon4@tests.mozilla.org",
     version: "5.0",
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -38,20 +38,16 @@ tags = blocklist
 requesttimeoutfactor = 2
 tags = blocklist
 [test_bootstrap.js]
 skip-if = true # Bug 1358846 Bug 1365021 Bug 676992
 [test_bootstrap_const.js]
 [test_bootstrap_globals.js]
 [test_bug1180901_2.js]
 skip-if = os != "win"
-[test_bug335238.js]
-# Bug 676992: test consistently hangs on Android
-skip-if = os == "android"
-tags = blocklist
 [test_bug371495.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_bug384052.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_bug393285.js]
 # Bug 676992: test consistently hangs on Android