Bug 557710 - add tests for behavior when the same restartless add-on is present in multiple install locations r?aswan draft
authorRobert Helmer <rhelmer@mozilla.com>
Mon, 15 Aug 2016 16:24:12 -0700
changeset 408162 1de796382514d5dfc14cdf76733ee253b38b54c6
parent 408161 d981eb1bef3c5815a269a1402b13083a07fcee28
child 408163 ebe21b3bff7f1be8e5951c17606d9f4c7ba08373
child 408214 f1b55cea4912f6aad4ca03467b8e2426e05226eb
push id28152
push userrhelmer@mozilla.com
push dateWed, 31 Aug 2016 19:02:17 +0000
reviewersaswan
bugs557710
milestone51.0a1
Bug 557710 - add tests for behavior when the same restartless add-on is present in multiple install locations r?aswan MozReview-Commit-ID: Fx9psv9sufD
toolkit/mozapps/extensions/internal/XPIProviderUtils.js
toolkit/mozapps/extensions/test/xpcshell/data/test_overload.json
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_overloading.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
--- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -2036,17 +2036,17 @@ this.XPIDatabaseReconcile = {
     // Validate the updated system add-ons
     let systemAddonLocation = XPIProvider.installLocationsByName[KEY_APP_SYSTEM_ADDONS];
     let addons = currentAddons.get(KEY_APP_SYSTEM_ADDONS) || new Map();
 
     let hideLocation;
 
     if (!systemAddonLocation.isValid(addons)) {
       // Hide the system add-on updates if any are invalid.
-      logger.info("One or more updated system add-ons invalid, falling back to defaults.");
+      logger.warn("One or more updated system add-ons invalid, falling back to defaults.");
       hideLocation = KEY_APP_SYSTEM_ADDONS;
     }
 
     let previousVisible = this.getVisibleAddons(previousAddons);
     let currentVisible = this.flattenByID(currentAddons, hideLocation);
     let sawActiveTheme = false;
     XPIProvider.bootstrappedAddons = {};
 
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_overload.json
@@ -0,0 +1,11 @@
+{
+  "addons": {
+    "test_overload@tests.mozilla.org": {
+      "updates": [
+        { "version": "3.0",
+          "update_link": "http://localhost:%PORT%/addons/test_overload_v3.xpi"
+        }
+      ]
+    }
+  }
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -654,26 +654,28 @@ function writeInstallRDFToDir(aData, aDi
  * application compatibility.
  *
  * @param   aData
  *          The object holding data about the add-on
  * @param   aDir
  *          The install directory to add the extension to
  * @param   aId
  *          An optional string to override the default installation aId
- * @param   aExtraFile
- *          An optional dummy file to create in the extension
+ * @param   {Object} aExtraFiles
+ *          An optional object containing file names and their contents.
  * @return  A file pointing to where the extension was installed
  */
-function writeInstallRDFToXPI(aData, aDir, aId = aData.id, aExtraFile = null) {
+function writeInstallRDFToXPI(aData, aDir, aId = aData.id, aExtraFiles = null) {
   let files = {
     "install.rdf": AddonTestUtils.createInstallRDF(aData),
   };
-  if (aExtraFile)
-    files[aExtraFile] = "";
+
+  if (aExtraFiles) {
+    Object.assign(files, aExtraFiles);
+  }
 
   if (!aDir.exists())
     aDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 
   var file = aDir.clone();
   file.append(`${aId}.xpi`);
 
   AddonTestUtils.writeFilesToZip(file.path, files);
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_overloading.js
@@ -0,0 +1,260 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that "overloading" an add-on in a higher-precedence
+// install location works.
+const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
+const PREF_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_UPDATE_SECURITY, false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const tempdir = gTmpD.clone();
+
+const OVERLOADED_ID = "test_overload@tests.mozilla.org";
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+// Create and configure the HTTP server.
+let testserver = createHttpServer();
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_overload.json", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+// this is the "built-in" system add-on location
+const distroDir = FileUtils.getDir("ProfD", ["sysfeatures"], true);
+// this is the "updated" system add-on location
+const updatesDir = FileUtils.getDir("ProfD", ["features", "set1"], true);
+const tempDir = gTmpD.clone();
+
+// precedence is: distroDir < updatesDir < profile
+registerDirectory("XREAppFeat", distroDir);
+
+BootstrapMonitor.init();
+
+// bootstrap.js to inject into XPI files.
+const mockBootstrapFile = {
+  "bootstrap.js": `Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);`
+}
+
+
+// Override a system add-on with a:
+// 1. system add-on update
+// 2. regular (profile) add-on
+//
+// Then remove each in reverse order and ensure the correct add-on is revealed.
+add_task(function*() {
+
+  writeInstallRDFToDir({
+    id: OVERLOADED_ID,
+    version: "1.0",
+    bootstrap: true,
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Overload, system add-on default",
+  }, distroDir, OVERLOADED_ID);
+
+  startupManager();
+
+  const systemAddon = yield promiseAddonByID(OVERLOADED_ID);
+  do_check_neq(systemAddon, null);
+  do_check_eq(systemAddon.version, "1.0");
+  do_check_eq(systemAddon.name, "Overload, system add-on default");
+  do_check_true(systemAddon.isCompatible);
+  do_check_false(systemAddon.appDisabled);
+  do_check_true(systemAddon.isActive);
+  do_check_eq(systemAddon.type, "extension");
+
+  // write a system add-on upgrade to the upgrade dir
+  writeInstallRDFToXPI({
+    id: OVERLOADED_ID,
+    version: "2.0",
+    bootstrap: true,
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Overload, system add-on upgrade",
+  }, updatesDir, OVERLOADED_ID);
+
+  // Set the system addon pref to the expected value.
+  let addonSet = {
+    schema: 1,
+    directory: updatesDir.leafName,
+    addons: {
+      "test_overload@tests.mozilla.org": {
+        version: "2.0"
+      }
+    }
+  };
+  Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(addonSet));
+
+  // system add-on upgrades currently require restart (bug 1204156).
+  yield promiseRestartManager();
+
+  const systemAddonUpgrade = yield promiseAddonByID(OVERLOADED_ID);
+  do_check_neq(systemAddonUpgrade, null);
+  do_check_eq(systemAddonUpgrade.version, "2.0");
+  do_check_eq(systemAddonUpgrade.name, "Overload, system add-on upgrade");
+  do_check_true(systemAddonUpgrade.isCompatible);
+  do_check_false(systemAddonUpgrade.appDisabled);
+  do_check_true(systemAddonUpgrade.isActive);
+  do_check_eq(systemAddonUpgrade.type, "extension");
+
+  // install new add-on with same ID "on top" of lower-precedence location.
+  let normalXpiFile = writeInstallRDFToXPI({
+    id: OVERLOADED_ID,
+    version: "3.0",
+    bootstrap: true,
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Overload, normal",
+  }, tempDir, OVERLOADED_ID, mockBootstrapFile);
+
+  yield promiseInstallAllFiles([normalXpiFile]);
+
+  const overloadedAddon = yield promiseAddonByID(OVERLOADED_ID);
+  do_check_neq(overloadedAddon, null);
+  do_check_eq(overloadedAddon.version, "3.0");
+  do_check_eq(overloadedAddon.name, "Overload, normal");
+  do_check_true(overloadedAddon.isCompatible);
+  do_check_false(overloadedAddon.appDisabled);
+  do_check_true(overloadedAddon.isActive);
+  do_check_eq(overloadedAddon.type, "extension");
+
+  BootstrapMonitor.checkAddonStarted(OVERLOADED_ID, "3.0");
+
+  // disabling does not reveal underlying add-on, "top" stays disabled
+  overloadedAddon.userDisabled = true;
+  do_check_false(overloadedAddon.isActive);
+  do_check_eq(overloadedAddon.version, "3.0");
+
+  overloadedAddon.userDisabled = false;
+  do_check_true(overloadedAddon.isActive);
+  do_check_eq(overloadedAddon.version, "3.0");
+
+  yield overloadedAddon.uninstall();
+  yield normalXpiFile.remove(false);
+
+  // the "system add-on upgrade" is now revealed
+  const originalSystemAddonUpgrade = yield promiseAddonByID(OVERLOADED_ID);
+  do_check_neq(originalSystemAddonUpgrade, null);
+  do_check_eq(originalSystemAddonUpgrade.version, "2.0");
+  do_check_eq(originalSystemAddonUpgrade.name, "Overload, system add-on upgrade");
+  do_check_true(originalSystemAddonUpgrade.isCompatible);
+  do_check_false(originalSystemAddonUpgrade.appDisabled);
+  do_check_true(originalSystemAddonUpgrade.isActive);
+  do_check_eq(originalSystemAddonUpgrade.type, "extension");
+
+  // system add-on upgrades currently require restart (bug 1204156).
+  yield updatesDir.remove(true);
+  yield promiseRestartManager();
+
+  do_check_false(updatesDir.exists());
+
+  // the "system add-on default" is now revealed
+  const originalSystemAddon = yield promiseAddonByID(OVERLOADED_ID);
+  do_check_neq(originalSystemAddon, null);
+  do_check_eq(originalSystemAddon.version, "1.0");
+  do_check_eq(originalSystemAddon.name, "Overload, system add-on default");
+  do_check_true(originalSystemAddon.isCompatible);
+  do_check_false(originalSystemAddon.appDisabled);
+  do_check_true(originalSystemAddon.isActive);
+  do_check_eq(originalSystemAddon.type, "extension");
+
+  yield shutdownManager();
+});
+
+//  test that updates when overriding locked locations works.
+add_task(function*() {
+  writeInstallRDFToDir({
+    id: OVERLOADED_ID,
+    version: "1.0",
+    bootstrap: true,
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Overload, system add-on default",
+  }, distroDir, OVERLOADED_ID);
+
+  startupManager();
+
+  const systemAddon = yield promiseAddonByID(OVERLOADED_ID);
+  do_check_neq(systemAddon, null);
+  do_check_eq(systemAddon.version, "1.0");
+  do_check_eq(systemAddon.name, "Overload, system add-on default");
+  do_check_true(systemAddon.isCompatible);
+  do_check_false(systemAddon.appDisabled);
+  do_check_true(systemAddon.isActive);
+  do_check_eq(systemAddon.type, "extension");
+
+  // install new add-on with same ID "on top" of lower-precedence location.
+  let normalXpiFile = writeInstallRDFToXPI({
+    id: OVERLOADED_ID,
+    version: "2.0",
+    bootstrap: true,
+    updateURL: `http://localhost:${gPort}/data/test_overload.json`,
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Overload, normal",
+  }, tempDir, OVERLOADED_ID, mockBootstrapFile);
+
+  yield promiseInstallAllFiles([normalXpiFile]);
+
+  const overloadedAddon = yield promiseAddonByID(OVERLOADED_ID);
+  do_check_neq(overloadedAddon, null);
+  do_check_eq(overloadedAddon.version, "2.0");
+  do_check_eq(overloadedAddon.name, "Overload, normal");
+  do_check_true(overloadedAddon.isCompatible);
+  do_check_false(overloadedAddon.appDisabled);
+  do_check_true(overloadedAddon.isActive);
+  do_check_eq(overloadedAddon.type, "extension");
+
+  BootstrapMonitor.checkAddonStarted(OVERLOADED_ID, "2.0");
+
+  // the "normal" add-on should allow updates, even though the underlying
+  // add-on is in a locked location which does not.
+  let update = yield promiseFindAddonUpdates(overloadedAddon);
+  let install = update.updateAvailable;
+  yield promiseCompleteAllInstalls([install]);
+
+  const upgradedAddon = yield promiseAddonByID(OVERLOADED_ID);
+  do_check_neq(upgradedAddon, null);
+  do_check_eq(upgradedAddon.version, "3.0");
+  do_check_eq(upgradedAddon.name, "Overload, normal, upgraded");
+  do_check_true(upgradedAddon.isCompatible);
+  do_check_false(upgradedAddon.appDisabled);
+  do_check_true(upgradedAddon.isActive);
+  do_check_eq(upgradedAddon.type, "extension");
+
+  yield upgradedAddon.uninstall();
+  yield normalXpiFile.remove(false);
+
+  // the system add-on default is now revealed.
+  const originalSystemAddon = yield promiseAddonByID(OVERLOADED_ID);
+  do_check_neq(originalSystemAddon, null);
+  do_check_eq(originalSystemAddon.version, "1.0");
+  do_check_eq(originalSystemAddon.name, "Overload, system add-on default");
+  do_check_true(originalSystemAddon.isCompatible);
+  do_check_false(originalSystemAddon.appDisabled);
+  do_check_true(originalSystemAddon.isActive);
+  do_check_eq(originalSystemAddon.type, "extension");
+
+  yield shutdownManager();
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -38,10 +38,11 @@ tags = webextensions
 [test_proxy.js]
 [test_pass_symbol.js]
 [test_delay_update.js]
 [test_nodisable_hidden.js]
 [test_delay_update_webextension.js]
 skip-if = appname == "thunderbird"
 tags = webextensions
 [test_dependencies.js]
+[test_overloading.js]
 
 [include:xpcshell-shared.ini]