Bug 709388 - Restrict set of enabled engines when running TPS tests; r=rnewmana a=testonly
authorGregory Szorc <gps@mozilla.com>
Wed, 14 Dec 2011 20:03:46 -0800
changeset 84644 fa294e936c014e11304034d8d85df87f0c53239f
parent 84643 e8ff5ef91e3167b00217e8d718ead45a87aea53c
child 84645 883459c2e7a774f5e204d9aa48fe4ae727be5c5a
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewmana, testonly
bugs709388
milestone11.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 709388 - Restrict set of enabled engines when running TPS tests; r=rnewmana a=testonly
services/sync/tests/tps/test_bookmarks_in_same_named_folder.js
services/sync/tests/tps/test_bug501528.js
services/sync/tests/tps/test_bug530717.js
services/sync/tests/tps/test_bug531489.js
services/sync/tests/tps/test_bug535326.js
services/sync/tests/tps/test_bug538298.js
services/sync/tests/tps/test_bug546807.js
services/sync/tests/tps/test_bug556509.js
services/sync/tests/tps/test_bug562515.js
services/sync/tests/tps/test_bug563989.js
services/sync/tests/tps/test_bug575423.js
services/sync/tests/tps/test_formdata.js
services/sync/tests/tps/test_history.js
services/sync/tests/tps/test_history_collision.js
services/sync/tests/tps/test_passwords.js
services/sync/tests/tps/test_prefs.js
services/sync/tests/tps/test_privbrw_formdata.js
services/sync/tests/tps/test_privbrw_passwords.js
services/sync/tests/tps/test_privbrw_tabs.js
services/sync/tests/tps/test_special_tabs.js
services/sync/tests/tps/test_tabs.js
services/sync/tps/extensions/tps/components/tps-cmdline.js
services/sync/tps/extensions/tps/modules/tps.jsm
testing/tps/tps/cli.py
testing/tps/tps/phase.py
testing/tps/tps/testrunner.py
--- a/services/sync/tests/tps/test_bookmarks_in_same_named_folder.js
+++ b/services/sync/tests/tps/test_bookmarks_in_same_named_folder.js
@@ -3,16 +3,17 @@
 
 // bug 558077
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["bookmarks"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1"};
 
 var bookmarks_initial_1 = {
   "menu": [
     { folder: "aaa",
--- a/services/sync/tests/tps/test_bug501528.js
+++ b/services/sync/tests/tps/test_bug501528.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["passwords"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1",
                "phase4": "profile2" };
 
 /*
  * Password lists
--- a/services/sync/tests/tps/test_bug530717.js
+++ b/services/sync/tests/tps/test_bug530717.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["prefs"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1"};
 
 /*
  * Preference lists
  */
--- a/services/sync/tests/tps/test_bug531489.js
+++ b/services/sync/tests/tps/test_bug531489.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["bookmarks"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1"};
 
 /*
  * Bookmark asset lists: these define bookmarks that are used during the test
  */
--- a/services/sync/tests/tps/test_bug535326.js
+++ b/services/sync/tests/tps/test_bug535326.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["tabs"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2"};
 
 var tabs1 = [
   { uri: "data:text/html,<html><head><title>Howdy</title></head><body>Howdy</body></html>",
     title: "Howdy",
     profile: "profile1"
--- a/services/sync/tests/tps/test_bug538298.js
+++ b/services/sync/tests/tps/test_bug538298.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["bookmarks"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1",
                "phase4": "profile2" };
 
 /*
  * Bookmark asset lists: these define bookmarks that are used during the test
--- a/services/sync/tests/tps/test_bug546807.js
+++ b/services/sync/tests/tps/test_bug546807.js
@@ -2,16 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
 
+EnableEngines(["tabs"]);
+
 var phases = { "phase1": "profile1",
                "phase2": "profile2"};
 
 /*
  * Tabs data
  */
 
 var tabs1 = [
--- a/services/sync/tests/tps/test_bug556509.js
+++ b/services/sync/tests/tps/test_bug556509.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["bookmarks"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2"};
 
 
 // the initial list of bookmarks to add to the browser
 var bookmarks_initial = {
   "menu": [
--- a/services/sync/tests/tps/test_bug562515.js
+++ b/services/sync/tests/tps/test_bug562515.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["bookmarks"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1",
                "phase4": "profile2" };
 
 /*
  * Bookmark lists
--- a/services/sync/tests/tps/test_bug563989.js
+++ b/services/sync/tests/tps/test_bug563989.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["bookmarks"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1",
                "phase4": "profile2" };
 
 /*
  * Bookmark asset lists: these define bookmarks that are used during the test
--- a/services/sync/tests/tps/test_bug575423.js
+++ b/services/sync/tests/tps/test_bug575423.js
@@ -2,16 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
 
+EnableEngines(["history"]);
+
 var phases = { "phase1": "profile1",
                "phase2": "profile2"};
 
 /*
  * History data
  */
 
 // the history data to add to the browser
--- a/services/sync/tests/tps/test_formdata.js
+++ b/services/sync/tests/tps/test_formdata.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["forms"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1",
                "phase4": "profile2" };
 
 /*
  * Form data asset lists: these define form values that are used in the tests.
--- a/services/sync/tests/tps/test_history.js
+++ b/services/sync/tests/tps/test_history.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["history"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2" };
 
 /*
  * History asset lists: these define history entries that are used during
  * the test
  */
--- a/services/sync/tests/tps/test_history_collision.js
+++ b/services/sync/tests/tps/test_history_collision.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["history"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1",
                "phase4": "profile2" };
 
 /*
  * History lists
--- a/services/sync/tests/tps/test_passwords.js
+++ b/services/sync/tests/tps/test_passwords.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["passwords"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1",
                "phase4": "profile2" };
 
 /*
  * Password asset lists: these define password entries that are used during
--- a/services/sync/tests/tps/test_prefs.js
+++ b/services/sync/tests/tps/test_prefs.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["prefs"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1"};
 
 var prefs1 = [
   { name: "browser.startup.homepage",
     value: "http://www.getfirefox.com"
--- a/services/sync/tests/tps/test_privbrw_formdata.js
+++ b/services/sync/tests/tps/test_privbrw_formdata.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["forms"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1",
                "phase4": "profile2" };
 
 /*
  * Form data
--- a/services/sync/tests/tps/test_privbrw_passwords.js
+++ b/services/sync/tests/tps/test_privbrw_passwords.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["passwords"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1",
                "phase4": "profile2" };
 
 /*
  * Password data
--- a/services/sync/tests/tps/test_privbrw_tabs.js
+++ b/services/sync/tests/tps/test_privbrw_tabs.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["tabs"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1",
                "phase4": "profile2" };
 
 /*
  * Tabs data
--- a/services/sync/tests/tps/test_special_tabs.js
+++ b/services/sync/tests/tps/test_special_tabs.js
@@ -3,16 +3,17 @@
 
 // Bug 532173 - Dont sync tabs like about:* , weave firstrun etc
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["tabs"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2" };
 
 var tabs1 = [
   { uri: "data:text/html,<html><head><title>Firefox</title></head><body>Firefox</body></html>",
     title: "Firefox",
     profile: "profile1"
--- a/services/sync/tests/tps/test_tabs.js
+++ b/services/sync/tests/tps/test_tabs.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * The list of phases mapped to their corresponding profiles.  The object
  * here must be in strict JSON format, as it will get parsed by the Python
  * testrunner (no single quotes, extra comma's, etc).
  */
+EnableEngines(["tabs"]);
 
 var phases = { "phase1": "profile1",
                "phase2": "profile2",
                "phase3": "profile1"};
 
 /*
  * Tab lists.
  */
--- a/services/sync/tps/extensions/tps/components/tps-cmdline.js
+++ b/services/sync/tps/extensions/tps/components/tps-cmdline.js
@@ -72,43 +72,50 @@ TPSCmdLineHandler.prototype =
   prefNameForStartup : "general.startup.tps",
   helpText : "Run TPS tests with the given test file.",
   handlesArgs : true,
   defaultArgs : "",
   openWindowWithArgs : true,
 
   /* nsICommandLineHandler */
   handle : function handler_handle(cmdLine) {
-    var uristr = cmdLine.handleFlagWithParam("tps", false);
+    let options = {};
+
+    let uristr = cmdLine.handleFlagWithParam("tps", false);
     if (uristr == null)
         return;
-    var phase = cmdLine.handleFlagWithParam("tpsphase", false);
+    let phase = cmdLine.handleFlagWithParam("tpsphase", false);
     if (phase == null)
         throw("must specify --tpsphase with --tps");
-    var logfile = cmdLine.handleFlagWithParam("tpslogfile", false);
+    let logfile = cmdLine.handleFlagWithParam("tpslogfile", false);
     if (logfile == null)
         logfile = "";
 
+    options.ignoreUnusedEngines = cmdLine.handleFlag("ignore-unused-engines",
+                                                     false);
+
+
     /* Ignore the platform's online/offline status while running tests. */
     var ios = Components.classes["@mozilla.org/network/io-service;1"]
               .getService(Components.interfaces.nsIIOService2);
     ios.manageOfflineStatus = false;
     ios.offline = false;
 
     Components.utils.import("resource://tps/tps.jsm");
     Components.utils.import("resource://tps/quit.js", TPS);
     let uri = cmdLine.resolveURI(uristr).spec;
-    TPS.RunTestPhase(uri, phase, logfile);
+    TPS.RunTestPhase(uri, phase, logfile, options);
 
     //cmdLine.preventDefault = true;
   },
 
   helpInfo : "  -tps <file>               Run TPS tests with the given test file.\n" +
              "  -tpsphase <phase>         Run the specified phase in the TPS test.\n" +
-             "  -tpslogfile <file>        Logfile for TPS output.\n",
+             "  -tpslogfile <file>        Logfile for TPS output.\n" +
+             "  --ignore-unused-engines   Don't load engines not used in tests.\n",
 };
 
 
 var TPSCmdLineFactory =
 {
   createInstance : function(outer, iid)
   {
     if (outer != null) {
--- a/services/sync/tps/extensions/tps/modules/tps.jsm
+++ b/services/sync/tps/extensions/tps/modules/tps.jsm
@@ -41,16 +41,17 @@
   */
 
 let EXPORTED_SYMBOLS = ["TPS"];
 
 const {classes: CC, interfaces: CI, utils: CU} = Components;
 
 CU.import("resource://services-sync/service.js");
 CU.import("resource://services-sync/constants.js");
+CU.import("resource://services-sync/engines.js");
 CU.import("resource://services-sync/async.js");
 CU.import("resource://services-sync/util.js");
 CU.import("resource://gre/modules/XPCOMUtils.jsm");
 CU.import("resource://gre/modules/Services.jsm");
 CU.import("resource://tps/addons.jsm");
 CU.import("resource://tps/bookmarks.jsm");
 CU.import("resource://tps/logger.jsm");
 CU.import("resource://tps/passwords.jsm");
@@ -98,16 +99,17 @@ let TPS =
   _errors: 0,
   _syncErrors: 0,
   _usSinceEpoch: 0,
   _tabsAdded: 0,
   _tabsFinished: 0,
   _phaselist: {},
   _operations_pending: 0,
   _loggedIn: false,
+  _enabledEngines: null,
 
   DumpError: function (msg) {
     this._errors++;
     Logger.logError("[phase" + this._currentPhase + "] " + msg);
     this.quit();
   },
 
   QueryInterface: XPCOMUtils.generateQI([CI.nsIObserver,
@@ -484,18 +486,45 @@ let TPS =
     }
     catch(e) {
       this.DumpError("Exception caught: " + Utils.exceptionStr(e));
       return;
     }
     this.RunNextTestAction();
   },
 
-  RunTestPhase: function (file, phase, logpath) {
+  /**
+   * Runs a single test phase.
+   *
+   * This is the main entry point for each phase of a test. The TPS command
+   * line driver loads this module and calls into the function with the
+   * arguments from the command line.
+   *
+   * When a phase is executed, the file is loaded as JavaScript into the
+   * current object.
+   *
+   * The following keys in the options argument have meaning:
+   *
+   *   - ignoreUnusedEngines  If true, unused engines will be unloaded from
+   *                          Sync. This makes output easier to parse and is
+   *                          useful for debugging test failures.
+   *
+   * @param  file
+   *         String URI of the file to open.
+   * @param  phase
+   *         String name of the phase to run.
+   * @param  logpath
+   *         String path of the log file to write to.
+   * @param  options
+   *         Object defining addition run-time options.
+   */
+  RunTestPhase: function (file, phase, logpath, options) {
     try {
+      let settings = options || {};
+
       Logger.init(logpath);
       Logger.logInfo("Sync version: " + WEAVE_VERSION);
       Logger.logInfo("Firefox builddate: " + Services.appinfo.appBuildID);
       Logger.logInfo("Firefox version: " + Services.appinfo.version);
 
       // do some sync housekeeping
       if (Weave.Service.isLoggedIn) {
         this.DumpError("Sync logged in on startup...profile may be dirty");
@@ -517,16 +546,33 @@ let TPS =
         this.DumpError("invalid phase " + this._currentPhase);
         return;
       }
 
       if (this.phases["phase" + this._currentPhase] == undefined) {
         this.DumpError("no profile defined for phase " + this._currentPhase);
         return;
       }
+
+      // If we have restricted the active engines, unregister engines we don't
+      // care about.
+      if (settings.ignoreUnusedEngines && Array.isArray(this._enabledEngines)) {
+        let names = {};
+        for each (let name in this._enabledEngines) {
+          names[name] = true;
+        }
+
+        for each (let engine in Engines.getEnabled()) {
+          if (!(engine.name in names)) {
+            Logger.logInfo("Unregistering unused engine: " + engine.name);
+            Engines.unregister(engine);
+          }
+        }
+      }
+
       Logger.logInfo("Starting phase " + parseInt(phase, 10) + "/" +
                      Object.keys(this._phaselist).length);
 
       Logger.logInfo("setting client.name to " + this.phases["phase" + this._currentPhase]);
       Weave.Svc.Prefs.set("client.name", this.phases["phase" + this._currentPhase]);
 
       // TODO Phases should be defined in a data type that has strong
       // ordering, not by lexical sorting.
@@ -554,20 +600,52 @@ let TPS =
       this._currentAction = 0;
     }
     catch(e) {
       this.DumpError("Exception caught: " + Utils.exceptionStr(e));
       return;
     }
   },
 
+  /**
+   * Register a single phase with the test harness.
+   *
+   * This is called when loading individual test files.
+   *
+   * @param  phasename
+   *         String name of the phase being loaded.
+   * @param  fnlist
+   *         Array of functions/actions to perform.
+   */
   Phase: function Test__Phase(phasename, fnlist) {
     this._phaselist[phasename] = fnlist;
   },
 
+  /**
+   * Restrict enabled Sync engines to a specified set.
+   *
+   * This can be called by a test to limit what engines are enabled. It is
+   * recommended to call it to reduce the overhead and log clutter for the
+   * test.
+   *
+   * The "clients" engine is special and is always enabled, so there is no
+   * need to specify it.
+   *
+   * @param  names
+   *         Array of Strings for engines to make active during the test.
+   */
+  EnableEngines: function EnableEngines(names) {
+    if (!Array.isArray(names)) {
+      throw new Error("Argument to RestrictEngines() is not an array: "
+                      + typeof(names));
+    }
+
+    this._enabledEngines = names;
+  },
+
   RunMozmillTest: function TPS__RunMozmillTest(testfile) {
     var mozmillfile = CC["@mozilla.org/file/local;1"]
                       .createInstance(CI.nsILocalFile);
     if (hh.oscpu.toLowerCase().indexOf('windows') > -1) {
       let re = /\/(\w)\/(.*)/;
       this.config.testdir = this.config.testdir.replace(re, "$1://$2").replace("/", "\\", "g");
     }
     mozmillfile.initWithPath(this.config.testdir);
--- a/testing/tps/tps/cli.py
+++ b/testing/tps/tps/cli.py
@@ -82,16 +82,22 @@ def main():
                     default = None,
                     help = "path to the config file to use "
                            "[default: %default]")
   parser.add_option("--pulsefile",
                     action = "store", type = "string", dest = "pulsefile",
                     default = None,
                     help = "path to file containing a pulse message in "
                            "json format that you want to inject into the monitor")
+  parser.add_option("--ignore-unused-engines",
+                     default=False,
+                     action="store_true",
+                     dest="ignore_unused_engines",
+                     help="If defined, don't load unused engines in individual tests."
+                           " Has no effect for pulse monitor.")
   (options, args) = parser.parse_args()
 
   configfile = options.configfile
   if configfile is None:
     if os.environ.get('VIRTUAL_ENV'):
       configfile = os.path.join(os.path.dirname(__file__), 'config.json')
     if configfile is None or not os.access(configfile, os.F_OK):
       raise Exception("Unable to find config.json in a VIRTUAL_ENV; you must "
@@ -148,13 +154,14 @@ def main():
   TPS = TPSTestRunner(extensionDir,
                       emailresults=options.emailresults,
                       testfile=options.testfile,
                       logfile=options.logfile,
                       binary=options.binary,
                       config=config,
                       rlock=rlock,
                       mobile=options.mobile,
-                      autolog=options.autolog)
+                      autolog=options.autolog,
+                      ignore_unused_engines=options.ignore_unused_engines)
   TPS.run_tests()
 
 if __name__ == "__main__":
   main()
--- a/testing/tps/tps/phase.py
+++ b/testing/tps/tps/phase.py
@@ -39,48 +39,52 @@ import os
 import re
 
 class TPSTestPhase(object):
 
   lineRe = re.compile(
       r"^(.*?)test phase (?P<matchphase>\d+): (?P<matchstatus>.*)$")
 
   def __init__(self, phase, profile, testname, testpath, logfile, env,
-               firefoxRunner, logfn):
+               firefoxRunner, logfn, ignore_unused_engines=False):
     self.phase = phase
     self.profile = profile
     self.testname = str(testname) # this might be passed in as unicode
     self.testpath = testpath
     self.logfile = logfile
     self.env = env
     self.firefoxRunner = firefoxRunner
     self.log = logfn
+    self.ignore_unused_engines = ignore_unused_engines
     self._status = None
     self.errline = ''
 
   @property
   def phasenum(self):
     match = re.match('.*?(\d+)', self.phase)
     if match:
       return match.group(1)
 
   @property
   def status(self):
     return self._status if self._status else 'unknown'
 
   def run(self):
     # launch Firefox
-    args = [ '-tps', self.testpath, 
-             '-tpsphase', self.phasenum, 
+    args = [ '-tps', self.testpath,
+             '-tpsphase', self.phasenum,
              '-tpslogfile', self.logfile ]
 
-    self.log("\nlaunching firefox for phase %s with args %s\n" % 
+    if self.ignore_unused_engines:
+        args.append('--ignore-unused-engines')
+
+    self.log("\nlaunching Firefox for phase %s with args %s\n" %
              (self.phase, str(args)))
     returncode = self.firefoxRunner.run(env=self.env,
-                                        args=args, 
+                                        args=args,
                                         profile=self.profile)
 
     # parse the logfile and look for results from the current test phase
     found_test = False
     f = open(self.logfile, 'r')
     for line in f:
 
       # skip to the part of the log file that deals with the test we're running
--- a/testing/tps/tps/testrunner.py
+++ b/testing/tps/tps/testrunner.py
@@ -113,22 +113,24 @@ class TPSTestRunner(object):
       r"Sync version: (?P<syncversion>.*)\n")
   ffVerRe = re.compile(
       r"Firefox version: (?P<ffver>.*)\n")
   ffDateRe = re.compile(
       r"Firefox builddate: (?P<ffdate>.*)\n")
 
   def __init__(self, extensionDir, emailresults=False, testfile="sync.test",
                binary=None, config=None, rlock=None, mobile=False,
-               autolog=False, logfile="tps.log"):
+               autolog=False, logfile="tps.log",
+               ignore_unused_engines=False):
     self.extensions = []
     self.emailresults = emailresults
     self.testfile = testfile
     self.logfile = os.path.abspath(logfile)
     self.binary = binary
+    self.ignore_unused_engines = ignore_unused_engines
     self.config = config if config else {}
     self.repo = None
     self.changeset = None
     self.branch = None
     self.numfailed = 0
     self.numpassed = 0
     self.nightly = False
     self.rlock = rlock
@@ -207,24 +209,26 @@ class TPSTestRunner(object):
       profilename = test[phase]
 
       # create the profile if necessary
       if not profilename in profiles:
         profiles[profilename] = Profile(preferences = self.preferences,
                                         addons = self.extensions)
 
       # create the test phase
-      phaselist.append(TPSTestPhase(phase,
-                                    profiles[profilename],
-                                    testname,
-                                    tmpfile.filename,
-                                    self.logfile,
-                                    self.env,
-                                    self.firefoxRunner,
-                                    self.log))
+      phaselist.append(TPSTestPhase(
+          phase,
+          profiles[profilename],
+          testname,
+          tmpfile.filename,
+          self.logfile,
+          self.env,
+          self.firefoxRunner,
+          self.log,
+          ignore_unused_engines=self.ignore_unused_engines))
 
     # sort the phase list by name
     phaselist = sorted(phaselist, key=lambda phase: phase.phase)
 
     # run each phase in sequence, aborting at the first failure
     for phase in phaselist:
       phase.run()