Bug 1324192 - Move common system add-on test functions to head_addons.js. r=aswan, a=test-only
authorRobert Helmer <rhelmer@mozilla.com>
Thu, 02 Mar 2017 11:30:09 -0800
changeset 378943 0ba3df0bf6c9b69671d2f6c7e40c11b9a6960276
parent 378942 674c744f9b1609d0fe06163d80c254b2c8b9cd02
child 378944 782ebf2dfc40456845358e0a94eb8c9cd72ac8c4
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan, test-only
bugs1324192
milestone53.0
Bug 1324192 - Move common system add-on test functions to head_addons.js. r=aswan, a=test-only 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;
@@ -1335,8 +1338,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();
 });