Bug 1324192 - move common system add-on test functions to head_addons.js r=aswan
authorRobert Helmer <rhelmer@mozilla.com>
Thu, 02 Mar 2017 11:30:09 -0800
changeset 346324 513015fce0eea1cc51ed7f3f3e0cb82f7386dad4
parent 346323 1475fe1bf157a9df76eba9e3aff61d89cd07f436
child 346325 62163a007dd51b25d66592df89d23ba10a811f9f
push id31464
push userkwierso@gmail.com
push dateWed, 08 Mar 2017 00:27:44 +0000
treeherdermozilla-central@dd92d0734a26 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1324192
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1324192 - move common system add-on test functions to head_addons.js r=aswan MozReview-Commit-ID: 7vV9nLmLs7D
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js
toolkit/mozapps/extensions/test/xpcshell/test_system_reset.js
toolkit/mozapps/extensions/test/xpcshell/test_system_update.js
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -15,16 +15,19 @@ const CERTDB_CID = Components.ID("{fb0bb
 
 const PREF_EM_CHECK_UPDATE_SECURITY   = "extensions.checkUpdateSecurity";
 const PREF_EM_STRICT_COMPATIBILITY    = "extensions.strictCompatibility";
 const PREF_EM_MIN_COMPAT_APP_VERSION      = "extensions.minCompatibleAppVersion";
 const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
 const PREF_GETADDONS_BYIDS               = "extensions.getAddons.get.url";
 const PREF_GETADDONS_BYIDS_PERFORMANCE   = "extensions.getAddons.getWithPerformance.url";
 const PREF_XPI_SIGNATURES_REQUIRED    = "xpinstall.signatures.required";
+const PREF_SYSTEM_ADDON_SET           = "extensions.systemAddonSet";
+const PREF_SYSTEM_ADDON_UPDATE_URL    = "extensions.systemAddon.update.url";
+const PREF_APP_UPDATE_ENABLED         = "app.update.enabled";
 
 // Forcibly end the test if it runs longer than 15 minutes
 const TIMEOUT_MS = 900000;
 
 // Maximum error in file modification times. Some file systems don't store
 // modification times exactly. As long as we are closer than this then it
 // still passes.
 const MAX_TIME_DIFFERENCE = 3000;
@@ -1359,8 +1362,275 @@ function* buildSystemAddonUpdates(addons
       xml += `/>\n`;
     }
     xml += `  </addons>\n`;
   }
   xml += `</updates>\n`;
 
   return xml;
 }
+
+function initSystemAddonDirs() {
+  let hiddenSystemAddonDir = FileUtils.getDir("ProfD", ["sysfeatures", "hidden"], true);
+  do_get_file("data/system_addons/system1_1.xpi").copyTo(hiddenSystemAddonDir, "system1@tests.mozilla.org.xpi");
+  do_get_file("data/system_addons/system2_1.xpi").copyTo(hiddenSystemAddonDir, "system2@tests.mozilla.org.xpi");
+
+  let prefilledSystemAddonDir = FileUtils.getDir("ProfD", ["sysfeatures", "prefilled"], true);
+  do_get_file("data/system_addons/system2_2.xpi").copyTo(prefilledSystemAddonDir, "system2@tests.mozilla.org.xpi");
+  do_get_file("data/system_addons/system3_2.xpi").copyTo(prefilledSystemAddonDir, "system3@tests.mozilla.org.xpi");
+}
+
+/**
+ * Returns current system add-on update directory (stored in pref).
+ */
+function getCurrentSystemAddonUpdatesDir() {
+  const updatesDir = FileUtils.getDir("ProfD", ["features"], false);
+  let dir = updatesDir.clone();
+  let set = JSON.parse(Services.prefs.getCharPref(PREF_SYSTEM_ADDON_SET));
+  dir.append(set.directory);
+  return dir;
+}
+
+/**
+ * Removes all files from system add-on update directory.
+ */
+function clearSystemAddonUpdatesDir() {
+  const updatesDir = FileUtils.getDir("ProfD", ["features"], false);
+  // Delete any existing directories
+  if (updatesDir.exists())
+    updatesDir.remove(true);
+
+  Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET);
+}
+
+/**
+ * Installs a known set of add-ons into the system add-on update directory.
+ */
+function buildPrefilledUpdatesDir() {
+  clearSystemAddonUpdatesDir();
+
+  // Build the test set
+  let dir = FileUtils.getDir("ProfD", ["features", "prefilled"], true);
+
+  do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
+  do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
+
+  // Mark these in the past so the startup file scan notices when files have changed properly
+  FileUtils.getFile("ProfD", ["features", "prefilled", "system2@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000;
+  FileUtils.getFile("ProfD", ["features", "prefilled", "system3@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000;
+
+  Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify({
+    schema: 1,
+    directory: dir.leafName,
+    addons: {
+      "system2@tests.mozilla.org": {
+        version: "2.0"
+      },
+      "system3@tests.mozilla.org": {
+        version: "2.0"
+      },
+    }
+  }));
+}
+
+/**
+ * Check currently installed ssystem add-ons against a set of conditions.
+ *
+ * @param {Array<Object>} conditions - an array of objects of the form { isUpgrade: false, version: null}
+ * @param {nsIFile} distroDir - the system add-on distribution directory (the "features" dir in the app directory)
+ */
+function* checkInstalledSystemAddons(conditions, distroDir) {
+  for (let i = 0; i < conditions.length; i++) {
+    let condition = conditions[i];
+    let id = "system" + (i + 1) + "@tests.mozilla.org";
+    let addon = yield promiseAddonByID(id);
+
+    if (!("isUpgrade" in condition) || !("version" in condition)) {
+      throw Error("condition must contain isUpgrade and version");
+    }
+    let isUpgrade = conditions[i].isUpgrade;
+    let version = conditions[i].version;
+
+    let expectedDir = isUpgrade ? getCurrentSystemAddonUpdatesDir() : distroDir;
+
+    if (version) {
+      do_print(`Checking state of add-on ${id}, expecting version ${version}`);
+
+      // Add-on should be installed
+      do_check_neq(addon, null);
+      do_check_eq(addon.version, version);
+      do_check_true(addon.isActive);
+      do_check_false(addon.foreignInstall);
+      do_check_true(addon.hidden);
+      do_check_true(addon.isSystem);
+
+      // Verify the add-ons file is in the right place
+      let file = expectedDir.clone();
+      file.append(id + ".xpi");
+      do_check_true(file.exists());
+      do_check_true(file.isFile());
+
+      let uri = addon.getResourceURI(null);
+      do_check_true(uri instanceof AM_Ci.nsIFileURL);
+      do_check_eq(uri.file.path, file.path);
+
+      if (isUpgrade) {
+        do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SYSTEM);
+      }
+
+      // Verify the add-on actually started
+      BootstrapMonitor.checkAddonStarted(id, version);
+    } else {
+      do_print(`Checking state of add-on ${id}, expecting it to be missing`);
+
+      if (isUpgrade) {
+        // Add-on should not be installed
+        do_check_eq(addon, null);
+      }
+
+      BootstrapMonitor.checkAddonNotStarted(id);
+
+      if (addon)
+        BootstrapMonitor.checkAddonInstalled(id);
+      else
+        BootstrapMonitor.checkAddonNotInstalled(id);
+    }
+  }
+}
+
+/**
+ * Returns all system add-on updates directories.
+ */
+function* getSystemAddonDirectories() {
+  const updatesDir = FileUtils.getDir("ProfD", ["features"], false);
+  let subdirs = [];
+
+  if (yield OS.File.exists(updatesDir.path)) {
+    let iterator = new OS.File.DirectoryIterator(updatesDir.path);
+    yield iterator.forEach(entry => {
+      if (entry.isDir) {
+        subdirs.push(entry);
+      }
+    });
+    iterator.close();
+  }
+
+  return subdirs;
+}
+
+/**
+ * Sets up initial system add-on update conditions.
+ *
+ * @param {Object<function, Array<Object>} setup - an object containing a setup function and an array of objects
+ *  of the form {isUpgrade: false, version: null}
+ *
+ * @param {nsIFile} distroDir - the system add-on distribution directory (the "features" dir in the app directory)
+ */
+function* setupSystemAddonConditions(setup, distroDir) {
+  do_print("Clearing existing database.");
+  Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET);
+  distroDir.leafName = "empty";
+  startupManager(false);
+  yield promiseShutdownManager();
+
+  do_print("Setting up conditions.");
+  yield setup.setup();
+
+  startupManager(false);
+
+  // Make sure the initial state is correct
+  do_print("Checking initial state.");
+  yield checkInstalledSystemAddons(setup.initialState, distroDir);
+}
+
+/**
+ * Verify state of system add-ons after installation.
+ *
+ * @param {Array<Object>} initialState - an array of objects of the form {isUpgrade: false, version: null}
+ * @param {Array<Object>} finalState - an array of objects of the form {isUpgrade: false, version: null}
+ * @param {Boolean} alreadyUpgraded - whether a restartless upgrade has already been performed.
+ * @param {nsIFile} distroDir - the system add-on distribution directory (the "features" dir in the app directory)
+ */
+function* verifySystemAddonState(initialState, finalState = undefined, alreadyUpgraded = false, distroDir) {
+  let expectedDirs = 0;
+
+  // If the initial state was using the profile set then that directory will
+  // still exist.
+
+  if (initialState.some(a => a.isUpgrade)) {
+    expectedDirs++;
+  }
+
+  if (finalState == undefined) {
+    finalState = initialState;
+  } else if (finalState.some(a => a.isUpgrade)) {
+    // If the new state is using the profile then that directory will exist.
+    expectedDirs++;
+  }
+
+  // Since upgrades are restartless now, the previous update dir hasn't been removed.
+  if (alreadyUpgraded) {
+    expectedDirs++;
+  }
+
+  do_print("Checking final state.");
+
+  let dirs = yield getSystemAddonDirectories();
+  do_check_eq(dirs.length, expectedDirs);
+
+  yield checkInstalledSystemAddons(...finalState, distroDir);
+
+  // Check that the new state is active after a restart
+  yield promiseRestartManager();
+  yield checkInstalledSystemAddons(finalState, distroDir);
+}
+
+/**
+ * Run system add-on tests and compare the results against a set of expected conditions.
+ *
+ * @param {String} setupName - name of the current setup conditions.
+ * @param {Object<function, Array<Object>} setup -  Defines the set of initial conditions to run each test against. Each should
+ *                                                  define the following properties:
+ *    setup:        A task to setup the profile into the initial state.
+ *    initialState: The initial expected system add-on state after setup has run.
+ * @param {Array<Object>} test -  The test to run. Each test must define an updateList or test. The following
+ *                                properties are used:
+ *    updateList: The set of add-ons the server should respond with.
+ *    test:       A function to run to perform the update check (replaces
+ *                updateList)
+ *    fails:      An optional property, if true the update check is expected to
+ *                fail.
+ *    finalState: An optional property, the expected final state of system add-ons,
+ *                if missing the test condition's initialState is used.
+ * @param {nsIFile} distroDir - the system add-on distribution directory (the "features" dir in the app directory)
+ * @param {String} root - HTTP URL to the test server
+ * @param {HttpServer} testserver - existing HTTP test server to use
+ */
+
+function* execSystemAddonTest(setupName, setup, test, distroDir, root, testserver) {
+  yield setupSystemAddonConditions(setup, distroDir);
+
+  try {
+    if ("test" in test) {
+      yield test.test();
+    } else {
+      yield installSystemAddons(yield buildSystemAddonUpdates(test.updateList, root), testserver);
+    }
+
+    if (test.fails) {
+      do_throw("Expected this test to fail");
+    }
+  } catch (e) {
+    if (!test.fails) {
+      do_throw(e);
+    }
+  }
+
+  // some tests have a different expected combination of default
+  // and updated add-ons.
+  if (test.finalState && setupName in test.finalState) {
+    yield verifySystemAddonState(setup.initialState, test.finalState[setupName], false, distroDir);
+  } else {
+    yield verifySystemAddonState(setup.initialState, undefined, false, distroDir);
+  }
+
+  yield promiseShutdownManager();
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js
@@ -3,19 +3,16 @@
  */
 
 // This verifies that delaying a system add-on update works.
 
 Components.utils.import("resource://testing-common/httpd.js");
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
-const PREF_SYSTEM_ADDON_SET           = "extensions.systemAddonSet";
-const PREF_SYSTEM_ADDON_UPDATE_URL    = "extensions.systemAddon.update.url";
-
 const IGNORE_ID = "system_delay_ignore@tests.mozilla.org";
 const COMPLETE_ID = "system_delay_complete@tests.mozilla.org";
 const DEFER_ID = "system_delay_defer@tests.mozilla.org";
 const DEFER_ALSO_ID = "system_delay_defer_also@tests.mozilla.org";
 const NORMAL_ID = "system1@tests.mozilla.org";
 
 
 const TEST_IGNORE_PREF = "delaytest.ignore";
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_reset.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_reset.js
@@ -1,11 +1,10 @@
 // Tests that we reset to the default system add-ons correctly when switching
 // application versions
-const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
 
 BootstrapMonitor.init();
 
 const updatesDir = FileUtils.getDir("ProfD", ["features"]);
 
 // Build the test sets
 var dir = FileUtils.getDir("ProfD", ["sysfeatures", "app1"], true);
 do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_update.js
@@ -1,169 +1,54 @@
-// Tests that we reset to the default system add-ons correctly when switching
-// application versions
-const PREF_SYSTEM_ADDON_SET           = "extensions.systemAddonSet";
-const PREF_SYSTEM_ADDON_UPDATE_URL    = "extensions.systemAddon.update.url";
-const PREF_APP_UPDATE_ENABLED         = "app.update.enabled";
+// Tests that system add-on upgrades work.
 
 Components.utils.import("resource://testing-common/httpd.js");
 
 BootstrapMonitor.init();
 
-const updatesDir = FileUtils.getDir("ProfD", ["features"], false);
-
-function getCurrentUpdatesDir() {
-  let dir = updatesDir.clone();
-  let set = JSON.parse(Services.prefs.getCharPref(PREF_SYSTEM_ADDON_SET));
-  dir.append(set.directory);
-  return dir;
-}
-
-function clearUpdatesDir() {
-  // Delete any existing directories
-  if (updatesDir.exists())
-    updatesDir.remove(true);
-
-  Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET);
-}
-
-function buildPrefilledUpdatesDir() {
-  clearUpdatesDir();
-
-  // Build the test set
-  let dir = FileUtils.getDir("ProfD", ["features", "prefilled"], true);
-
-  do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
-  do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
-
-  // Mark these in the past so the startup file scan notices when files have changed properly
-  FileUtils.getFile("ProfD", ["features", "prefilled", "system2@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000;
-  FileUtils.getFile("ProfD", ["features", "prefilled", "system3@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000;
-
-  Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify({
-    schema: 1,
-    directory: dir.leafName,
-    addons: {
-      "system2@tests.mozilla.org": {
-        version: "2.0"
-      },
-      "system3@tests.mozilla.org": {
-        version: "2.0"
-      },
-    }
-  }));
-}
-
-let dir = FileUtils.getDir("ProfD", ["sysfeatures", "hidden"], true);
-do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
-do_get_file("data/system_addons/system2_1.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
-
-dir = FileUtils.getDir("ProfD", ["sysfeatures", "prefilled"], true);
-do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
-do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
-
-const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "empty"], true);
-registerDirectory("XREAppFeat", distroDir);
-
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2");
 
 var testserver = new HttpServer();
 testserver.registerDirectory("/data/", do_get_file("data/system_addons"));
 testserver.start();
 var root = testserver.identity.primaryScheme + "://" +
            testserver.identity.primaryHost + ":" +
            testserver.identity.primaryPort + "/data/"
 Services.prefs.setCharPref(PREF_SYSTEM_ADDON_UPDATE_URL, root + "update.xml");
 
-function* check_installed(conditions) {
-  for (let i = 0; i < conditions.length; i++) {
-    let condition = conditions[i];
-    let id = "system" + (i + 1) + "@tests.mozilla.org";
-    let addon = yield promiseAddonByID(id);
-
-    if (!("isUpgrade" in condition) || !("version" in condition)) {
-      throw Error("condition must contain isUpgrade and version");
-    }
-    let isUpgrade = conditions[i].isUpgrade;
-    let version = conditions[i].version;
-
-    let expectedDir = isUpgrade ? getCurrentUpdatesDir() : distroDir;
-
-    if (version) {
-      do_print(`Checking state of add-on ${id}, expecting version ${version}`);
-
-      // Add-on should be installed
-      do_check_neq(addon, null);
-      do_check_eq(addon.version, version);
-      do_check_true(addon.isActive);
-      do_check_false(addon.foreignInstall);
-      do_check_true(addon.hidden);
-      do_check_true(addon.isSystem);
-
-      // Verify the add-ons file is in the right place
-      let file = expectedDir.clone();
-      file.append(id + ".xpi");
-      do_check_true(file.exists());
-      do_check_true(file.isFile());
-
-      let uri = addon.getResourceURI(null);
-      do_check_true(uri instanceof AM_Ci.nsIFileURL);
-      do_check_eq(uri.file.path, file.path);
-
-      if (isUpgrade) {
-        do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SYSTEM);
-      }
-
-      // Verify the add-on actually started
-      BootstrapMonitor.checkAddonStarted(id, version);
-    } else {
-      do_print(`Checking state of add-on ${id}, expecting it to be missing`);
-
-      if (isUpgrade) {
-        // Add-on should not be installed
-        do_check_eq(addon, null);
-      }
-
-      BootstrapMonitor.checkAddonNotStarted(id);
-
-      if (addon)
-        BootstrapMonitor.checkAddonInstalled(id);
-      else
-        BootstrapMonitor.checkAddonNotInstalled(id);
-    }
-  }
-}
-
+let distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "empty"], true);
+registerDirectory("XREAppFeat", distroDir);
+initSystemAddonDirs();
 
 /**
  * Defines the set of initial conditions to run each test against. Each should
  * define the following properties:
  *
  * setup:        A task to setup the profile into the initial state.
  * initialState: The initial expected system add-on state after setup has run.
  */
 const TEST_CONDITIONS = {
   // Runs tests with no updated or default system add-ons initially installed
   blank: {
     *setup() {
-      clearUpdatesDir();
+      clearSystemAddonUpdatesDir();
       distroDir.leafName = "empty";
     },
     initialState: [
       { isUpgrade: false, version: null},
       { isUpgrade: false, version: null},
       { isUpgrade: false, version: null},
       { isUpgrade: false, version: null},
       { isUpgrade: false, version: null}
     ],
   },
   // Runs tests with default system add-ons installed
   withAppSet: {
     *setup() {
-      clearUpdatesDir();
+      clearSystemAddonUpdatesDir();
       distroDir.leafName = "prefilled";
     },
     initialState: [
       { isUpgrade: false, version: null},
       { isUpgrade: false, version: "2.0"},
       { isUpgrade: false, version: "2.0"},
       { isUpgrade: false, version: null},
       { isUpgrade: false, version: null}
@@ -245,17 +130,17 @@ const TESTS = {
         { isUpgrade: false, version: null},
         { isUpgrade: false, version: null}
       ],
       withBothSets: [
         { isUpgrade: false, version: "1.0"},
         { isUpgrade: false, version: "1.0"},
         { isUpgrade: false, version: null},
         { isUpgrade: false, version: null},
-        // Set this to `true` to so `verify_state()` expects a blank profile dir
+        // Set this to `true` to so `verifySystemAddonState()` expects a blank profile dir
         { isUpgrade: true, version: null}
       ]
     },
   },
   // Tests that a new set of system add-ons gets installed
   newset: {
     updateList: [
       { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" },
@@ -366,43 +251,16 @@ const TESTS = {
         { isUpgrade: true, version: "2.0"},
         { isUpgrade: true, version: "3.0"},
         { isUpgrade: true, version: "1.0"},
         { isUpgrade: false, version: null}
       ]
     }
   },
 
-  // Specifying an incorrect version should stop us updating anything
-  badVersion: {
-    fails: true,
-    updateList: [
-      { id: "system2@tests.mozilla.org", version: "4.0", path: "system2_3.xpi" },
-      { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
-    ],
-  },
-
-  // Specifying an invalid size should stop us updating anything
-  badSize: {
-    fails: true,
-    updateList: [
-      { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi", size: 2 },
-      { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
-    ],
-  },
-
-  // Specifying an incorrect hash should stop us updating anything
-  badHash: {
-    fails: true,
-    updateList: [
-      { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi" },
-      { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi", hashFunction: "sha1", hashValue: "205a4c49bd513ebd30594e380c19e86bba1f83e2" }
-    ],
-  },
-
   // Correct sizes and hashes should work
   checkSizeHash: {
     updateList: [
       { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi", size: 4697 },
       { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi", hashFunction: "sha1", hashValue: "a4c7198d56deb315511c02937fd96c696de6cb84" },
       { id: "system5@tests.mozilla.org", version: "1.0", path: "system5_1.xpi", size: 4691, hashFunction: "sha1", hashValue: "6887b916a1a9a5338b0df4181f6187f5396861eb" }
     ],
     finalState: {
@@ -430,346 +288,213 @@ const TESTS = {
       withBothSets: [
         { isUpgrade: false, version: "1.0"},
         { isUpgrade: true, version: "3.0"},
         { isUpgrade: true, version: "3.0"},
         { isUpgrade: false, version: null},
         { isUpgrade: true, version: "1.0"}
       ]
     }
-  },
-
-  // A bad certificate should stop updates
-  badCert: {
-    fails: true,
-    updateList: [
-      { id: "system1@tests.mozilla.org", version: "1.0", path: "system1_1_badcert.xpi" },
-      { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
-    ],
-  },
-
-  // An unpacked add-on should stop updates.
-  notPacked: {
-    fails: true,
-    updateList: [
-      { id: "system6@tests.mozilla.org", version: "1.0", path: "system6_1_unpack.xpi" },
-      { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
-    ],
-  },
-
-  // A non-bootstrap add-on should stop updates.
-  notBootstrap: {
-    fails: true,
-    updateList: [
-      { id: "system6@tests.mozilla.org", version: "1.0", path: "system6_2_notBootstrap.xpi" },
-      { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
-    ],
-  },
-
-  // A non-multiprocess add-on should stop updates.
-  notMultiprocess: {
-    fails: true,
-    updateList: [
-      { id: "system6@tests.mozilla.org", version: "1.0", path: "system6_3_notMultiprocess.xpi" },
-      { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
-    ],
   }
 }
 
 add_task(function* setup() {
   // Initialise the profile
   startupManager();
   yield promiseShutdownManager();
-})
-
-function* get_directories() {
-  let subdirs = [];
-
-  if (yield OS.File.exists(updatesDir.path)) {
-    let iterator = new OS.File.DirectoryIterator(updatesDir.path);
-    yield iterator.forEach(entry => {
-      if (entry.isDir) {
-        subdirs.push(entry);
-      }
-    });
-    iterator.close();
-  }
-
-  return subdirs;
-}
-
-function* setup_conditions(setup) {
-  do_print("Clearing existing database.");
-  Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET);
-  distroDir.leafName = "empty";
-  startupManager(false);
-  yield promiseShutdownManager();
-
-  do_print("Setting up conditions.");
-  yield setup.setup();
-
-  startupManager(false);
-
-  // Make sure the initial state is correct
-  do_print("Checking initial state.");
-  yield check_installed(setup.initialState);
-}
-
-function* verify_state(initialState, finalState = undefined, alreadyUpgraded = false) {
-  let expectedDirs = 0;
-
-  // If the initial state was using the profile set then that directory will
-  // still exist.
-
-  if (initialState.some(a => a.isUpgrade)) {
-    expectedDirs++;
-  }
-
-  if (finalState == undefined) {
-    finalState = initialState;
-  } else if (finalState.some(a => a.isUpgrade)) {
-    // If the new state is using the profile then that directory will exist.
-    expectedDirs++;
-  }
-
-  // Since upgrades are restartless now, the previous update dir hasn't been removed.
-  if (alreadyUpgraded) {
-    expectedDirs++;
-  }
-
-  do_print("Checking final state.");
-
-  let dirs = yield get_directories();
-  do_check_eq(dirs.length, expectedDirs);
-
-  yield check_installed(...finalState);
-
-  // Check that the new state is active after a restart
-  yield promiseRestartManager();
-  yield check_installed(finalState);
-}
-
-function* exec_test(setupName, testName) {
-  let setup = TEST_CONDITIONS[setupName];
-  let test = TESTS[testName];
-
-  yield setup_conditions(setup);
-
-  try {
-    if ("test" in test) {
-      yield test.test();
-    } else {
-      yield installSystemAddons(yield buildSystemAddonUpdates(test.updateList, root), testserver);
-    }
-
-    if (test.fails) {
-      do_throw("Expected this test to fail");
-    }
-  } catch (e) {
-    if (!test.fails) {
-      do_throw(e);
-    }
-  }
-
-  // some tests have a different expected combination of default
-  // and updated add-ons.
-  if (test.finalState && setupName in test.finalState) {
-    yield verify_state(setup.initialState, test.finalState[setupName]);
-  } else {
-    yield verify_state(setup.initialState, test.finalState);
-  }
-
-  yield promiseShutdownManager();
-}
+});
 
 add_task(function*() {
-  for (let setup of Object.keys(TEST_CONDITIONS)) {
-    for (let test of Object.keys(TESTS)) {
-        do_print("Running test " + setup + " " + test);
+  for (let setupName of Object.keys(TEST_CONDITIONS)) {
+    for (let testName of Object.keys(TESTS)) {
+        do_print("Running test " + setupName + " " + testName);
 
-        yield exec_test(setup, test);
+        let setup = TEST_CONDITIONS[setupName];
+        let test = TESTS[testName];
+
+        yield execSystemAddonTest(setupName, setup, test, distroDir, root, testserver);
     }
   }
 });
 
 // Some custom tests
 // Test that the update check is performed as part of the regular add-on update
 // check
 add_task(function* test_addon_update() {
-  yield setup_conditions(TEST_CONDITIONS.blank);
+  yield setupSystemAddonConditions(TEST_CONDITIONS.blank, distroDir);
 
   yield updateAllSystemAddons(yield buildSystemAddonUpdates([
     { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
     { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
   ], root), testserver);
 
-  yield verify_state(TEST_CONDITIONS.blank.initialState, [
+  yield verifySystemAddonState(TEST_CONDITIONS.blank.initialState, [
     {isUpgrade: false, version: null},
     {isUpgrade: true, version: "2.0"},
     {isUpgrade: true, version: "2.0"},
     {isUpgrade: false, version: null},
     {isUpgrade: false, version: null}
-  ]);
+  ], false, distroDir);
 
   yield promiseShutdownManager();
 });
 
 // Disabling app updates should block system add-on updates
 add_task(function* test_app_update_disabled() {
-  yield setup_conditions(TEST_CONDITIONS.blank);
+  yield setupSystemAddonConditions(TEST_CONDITIONS.blank, distroDir);
 
   Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, false);
   yield updateAllSystemAddons(yield buildSystemAddonUpdates([
     { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
     { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
   ], root), testserver);
   Services.prefs.clearUserPref(PREF_APP_UPDATE_ENABLED);
 
-  yield verify_state(TEST_CONDITIONS.blank.initialState);
+  yield verifySystemAddonState(TEST_CONDITIONS.blank.initialState, undefined, false, distroDir);
 
   yield promiseShutdownManager();
 });
 
 // Safe mode should block system add-on updates
 add_task(function* test_safe_mode() {
   gAppInfo.inSafeMode = true;
 
-  yield setup_conditions(TEST_CONDITIONS.blank);
+  yield setupSystemAddonConditions(TEST_CONDITIONS.blank, distroDir);
 
   Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, false);
   yield updateAllSystemAddons(yield buildSystemAddonUpdates([
     { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
     { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
   ], root), testserver);
   Services.prefs.clearUserPref(PREF_APP_UPDATE_ENABLED);
 
-  yield verify_state(TEST_CONDITIONS.blank.initialState);
+  yield verifySystemAddonState(TEST_CONDITIONS.blank.initialState, undefined, false, distroDir);
 
   yield promiseShutdownManager();
 
   gAppInfo.inSafeMode = false;
 });
 
 // Tests that a set that matches the default set does nothing
 add_task(function* test_match_default() {
-  yield setup_conditions(TEST_CONDITIONS.withAppSet);
+  yield setupSystemAddonConditions(TEST_CONDITIONS.withAppSet, distroDir);
 
   yield installSystemAddons(yield buildSystemAddonUpdates([
     { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
     { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
   ], root), testserver);
 
   // Shouldn't have installed an updated set
-  yield verify_state(TEST_CONDITIONS.withAppSet.initialState);
+  yield verifySystemAddonState(TEST_CONDITIONS.withAppSet.initialState, undefined, false, distroDir);
 
   yield promiseShutdownManager();
 });
 
 // Tests that a set that matches the hidden default set works
 add_task(function* test_match_default_revert() {
-  yield setup_conditions(TEST_CONDITIONS.withBothSets);
+  yield setupSystemAddonConditions(TEST_CONDITIONS.withBothSets, distroDir);
 
   yield installSystemAddons(yield buildSystemAddonUpdates([
     { id: "system1@tests.mozilla.org", version: "1.0", path: "system1_1.xpi" },
     { id: "system2@tests.mozilla.org", version: "1.0", path: "system2_1.xpi" }
   ], root), testserver);
 
   // This should revert to the default set instead of installing new versions
   // into an updated set.
-  yield verify_state(TEST_CONDITIONS.withBothSets.initialState, [
+  yield verifySystemAddonState(TEST_CONDITIONS.withBothSets.initialState, [
     {isUpgrade: false, version: "1.0"},
     {isUpgrade: false, version: "1.0"},
     {isUpgrade: false, version: null},
     {isUpgrade: false, version: null},
     {isUpgrade: false, version: null}
-  ]);
+  ], false, distroDir);
 
   yield promiseShutdownManager();
 });
 
 // Tests that a set that matches the current set works
 add_task(function* test_match_current() {
-  yield setup_conditions(TEST_CONDITIONS.withBothSets);
+  yield setupSystemAddonConditions(TEST_CONDITIONS.withBothSets, distroDir);
 
   yield installSystemAddons(yield buildSystemAddonUpdates([
     { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
     { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
   ], root), testserver);
 
   // This should remain with the current set instead of creating a new copy
   let set = JSON.parse(Services.prefs.getCharPref(PREF_SYSTEM_ADDON_SET));
   do_check_eq(set.directory, "prefilled");
 
-  yield verify_state(TEST_CONDITIONS.withBothSets.initialState);
+  yield verifySystemAddonState(TEST_CONDITIONS.withBothSets.initialState, undefined, false, distroDir);
 
   yield promiseShutdownManager();
 });
 
 // Tests that a set with a minor change doesn't re-download existing files
 add_task(function* test_no_download() {
-  yield setup_conditions(TEST_CONDITIONS.withBothSets);
+  yield setupSystemAddonConditions(TEST_CONDITIONS.withBothSets, distroDir);
 
   // The missing file here is unneeded since there is a local version already
   yield installSystemAddons(yield buildSystemAddonUpdates([
     { id: "system2@tests.mozilla.org", version: "2.0", path: "missing.xpi" },
     { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" }
   ], root), testserver);
 
-  yield verify_state(TEST_CONDITIONS.withBothSets.initialState, [
+  yield verifySystemAddonState(TEST_CONDITIONS.withBothSets.initialState, [
     {isUpgrade: false, version: "1.0"},
     {isUpgrade: true, version: "2.0"},
     {isUpgrade: false, version: null},
     {isUpgrade: true, version: "1.0"},
     {isUpgrade: false, version: null}
-  ]);
+  ], false, distroDir);
 
   yield promiseShutdownManager();
 });
 
 // Tests that a second update before a restart works
 add_task(function* test_double_update() {
-  yield setup_conditions(TEST_CONDITIONS.withAppSet);
+  yield setupSystemAddonConditions(TEST_CONDITIONS.withAppSet, distroDir);
 
   yield installSystemAddons(yield buildSystemAddonUpdates([
     { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
     { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
   ], root), testserver);
 
   yield installSystemAddons(yield buildSystemAddonUpdates([
     { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" },
     { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" }
   ], root), testserver);
 
-  yield verify_state(TEST_CONDITIONS.withAppSet.initialState, [
+  yield verifySystemAddonState(TEST_CONDITIONS.withAppSet.initialState, [
     {isUpgrade: false, version: null},
     {isUpgrade: false, version: "2.0"},
     {isUpgrade: true, version: "2.0"},
     {isUpgrade: true, version: "1.0"},
     {isUpgrade: false, version: null}
-  ], true);
+  ], true, distroDir);
 
   yield promiseShutdownManager();
 });
 
 // A second update after a restart will delete the original unused set
 add_task(function* test_update_purges() {
-  yield setup_conditions(TEST_CONDITIONS.withBothSets);
+  yield setupSystemAddonConditions(TEST_CONDITIONS.withBothSets, distroDir);
 
   yield installSystemAddons(yield buildSystemAddonUpdates([
     { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
     { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
   ], root), testserver);
 
-  yield verify_state(TEST_CONDITIONS.withBothSets.initialState, [
+  yield verifySystemAddonState(TEST_CONDITIONS.withBothSets.initialState, [
     {isUpgrade: false, version: "1.0"},
     {isUpgrade: true, version: "2.0"},
     {isUpgrade: true, version: "1.0"},
     {isUpgrade: false, version: null},
     {isUpgrade: false, version: null}
-  ]);
+  ], false, distroDir);
 
   yield installSystemAddons(yield buildSystemAddonUpdates(null), testserver);
 
-  let dirs = yield get_directories();
+  let dirs = yield getSystemAddonDirectories();
   do_check_eq(dirs.length, 1);
 
   yield promiseShutdownManager();
 });