Bug 1451527 - Move sync's TPS tests into a webextension r=markh
☠☠ backed out by 7ad7b862c561 ☠ ☠
authorThom Chiovoloni <chiovolonit@gmail.com>
Tue, 04 Sep 2018 16:40:22 +0000
changeset 483008 b886f2cf9d8966776ebf5344d10df260fb8c4383
parent 483007 cebcf63f46c60f8686e5ca7829c562163583ca9f
child 483009 31d611a28db2019b4d7559085f2a0741d87028eb
push id232
push userfmarier@mozilla.com
push dateWed, 05 Sep 2018 20:45:54 +0000
reviewersmarkh
bugs1451527
milestone63.0a1
Bug 1451527 - Move sync's TPS tests into a webextension r=markh Differential Revision: https://phabricator.services.mozilla.com/D4486
services/sync/tps/extensions/tps/api.js
services/sync/tps/extensions/tps/bootstrap.js
services/sync/tps/extensions/tps/install.rdf
services/sync/tps/extensions/tps/manifest.json
services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm
services/sync/tps/extensions/tps/resource/modules/formautofill.jsm
services/sync/tps/extensions/tps/schema.json
testing/tps/tps/phase.py
testing/tps/tps/testrunner.py
new file mode 100644
--- /dev/null
+++ b/services/sync/tps/extensions/tps/api.js
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Timer.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "resProto",
+                                   "@mozilla.org/network/protocol;1?name=resource",
+                                   "nsISubstitutingProtocolHandler");
+
+async function tpsStartup() {
+  try {
+    ChromeUtils.import("resource://tps/tps.jsm");
+    ChromeUtils.import("resource://tps/quit.js", TPS);
+
+    let testFile = Services.prefs.getStringPref("testing.tps.testFile", "");
+    let testPhase = Services.prefs.getStringPref("testing.tps.testPhase", "");
+    if (!testFile || !testPhase) {
+      // Note: this quits.
+      TPS.DumpError("TPS no longer takes arguments from the command line. " +
+                    "instead you need to pass preferences  `testing.tps.{testFile,testPhase}` " +
+                    "and optionally `testing.tps.{logFile,ignoreUnusedEngines}`.\n");
+    }
+
+    let logFile = Services.prefs.getStringPref("testing.tps.logFile", "");
+    let ignoreUnusedEngines = Services.prefs.getBoolPref("testing.tps.ignoreUnusedEngines", false);
+    let options = { ignoreUnusedEngines };
+    let testFileUri = Services.io.newFileURI(new FileUtils.File(testFile)).spec;
+
+    try {
+      await TPS.RunTestPhase(testFileUri, testPhase, logFile, options)
+    } catch (err) {
+      TPS.DumpError("TestPhase failed", err);
+    }
+  } catch (e) {
+    if (typeof TPSĀ != "undefined") {
+      // Note: This calls quit() under the hood
+      TPS.DumpError("Test initialization failed", err);
+    }
+    // Try and quit right away, no reason to wait around for python
+    // to kill us if initialization failed.
+    Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
+  }
+}
+
+function onStartupFinished() {
+  return new Promise(resolve => {
+    const onStartupFinished = () => {
+      Services.obs.removeObserver(onStartupFinished, "browser-delayed-startup-finished");
+      resolve();
+    };
+    Services.obs.addObserver(onStartupFinished, "browser-delayed-startup-finished");
+  });
+}
+
+this.tps = class extends ExtensionAPI {
+  onStartup() {
+    resProto.setSubstitution("tps",
+      Services.io.newURI("resource/", null, this.extension.rootURI));
+    /* Ignore the platform's online/offline status while running tests. */
+    Services.io.manageOfflineStatus = false;
+    Services.io.offline = false;
+    tpsStartup();
+  }
+
+  onShutdown() {
+    resProto.setSubstitution("tps", null);
+  }
+};
deleted file mode 100644
--- a/services/sync/tps/extensions/tps/bootstrap.js
+++ /dev/null
@@ -1,104 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/osfile.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "resProto",
-                                   "@mozilla.org/network/protocol;1?name=resource",
-                                   "nsISubstitutingProtocolHandler");
-
-const Cm = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-
-const CATMAN_CONTRACTID = "@mozilla.org/categorymanager;1";
-
-const CATEGORY_NAME = "command-line-handler";
-const CATEGORY_ENTRY = "m-tps";
-
-function TPSCmdLine() {}
-
-TPSCmdLine.prototype = {
-  factory: XPCOMUtils._getFactory(TPSCmdLine),
-  classDescription: "TPSCmdLine",
-  classID: Components.ID("{4e5bd3f0-41d3-11df-9879-0800200c9a66}"),
-  contractID: "@mozilla.org/commandlinehandler/general-startup;1?type=tps",
-
-  QueryInterface:   ChromeUtils.generateQI([Ci.nsICommandLineHandler]),
-
-  register() {
-    Cm.registerFactory(this.classID, this.classDescription,
-                       this.contractID, this.factory);
-
-    Services.catMan.addCategoryEntry(CATEGORY_NAME, CATEGORY_ENTRY,
-                                     this.contractID, false, true);
-  },
-
-  unregister() {
-    Services.catMan.deleteCategoryEntry(CATEGORY_NAME, CATEGORY_ENTRY,
-                                        this.contractID, false);
-
-    Cm.unregisterFactory(this.classID, this.factory);
-  },
-
-  /* nsICmdLineHandler */
-  commandLineArgument: "-tps",
-  prefNameForStartup: "general.startup.tps",
-  helpText: "Run TPS tests with the given test file.",
-  handlesArgs: true,
-  defaultArgs: "",
-  openWindowWithArgs: true,
-
-  /* nsICommandLineHandler */
-  handle: function handler_handle(cmdLine) {
-    let options = {};
-
-    let uristr = cmdLine.handleFlagWithParam("tps", false);
-    if (uristr == null) {
-        return;
-    }
-    let phase = cmdLine.handleFlagWithParam("tpsphase", false);
-    if (phase == null) {
-        throw Error("must specify --tpsphase with --tps");
-    }
-    let logfile = cmdLine.handleFlagWithParam("tpslogfile", false);
-    if (logfile == null) {
-        logfile = "";
-    }
-
-    options.ignoreUnusedEngines = cmdLine.handleFlag("ignore-unused-engines",
-                                                     false);
-    let uri = cmdLine.resolveURI(OS.Path.normalize(uristr)).spec;
-
-    const onStartupFinished = () => {
-      Services.obs.removeObserver(onStartupFinished, "browser-delayed-startup-finished");
-      /* Ignore the platform's online/offline status while running tests. */
-      Services.io.manageOfflineStatus = false;
-      Services.io.offline = false;
-      ChromeUtils.import("resource://tps/tps.jsm");
-      ChromeUtils.import("resource://tps/quit.js", TPS);
-      TPS.RunTestPhase(uri, phase, logfile, options).catch(err => TPS.DumpError("TestPhase failed", err));
-    };
-    Services.obs.addObserver(onStartupFinished, "browser-delayed-startup-finished");
-  },
-
-  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" +
-            "  --ignore-unused-engines   Don't load engines not used in tests.\n",
-};
-
-function startup(data, reason) {
-  TPSCmdLine.prototype.register();
-  resProto.setSubstitution("tps", Services.io.newURI("resource/", null, data.resourceURI));
-}
-
-function shutdown(data, reason) {
-  resProto.setSubstitution("tps", null);
-  TPSCmdLine.prototype.unregister();
-}
-
-function install(data, reason) {}
-function uninstall(data, reason) {}
deleted file mode 100644
--- a/services/sync/tps/extensions/tps/install.rdf
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>tps@mozilla.org</em:id>
-    <em:version>0.5</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <em:targetApplication>
-      <!-- Firefox -->
-      <Description>
-        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
-        <em:minVersion>24.0.*</em:minVersion>
-        <em:maxVersion>*</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <!-- front-end metadata -->
-    <em:name>TPS</em:name>
-    <em:description>Sync test extension</em:description>
-    <em:creator>Jonathan Griffin</em:creator>
-    <em:contributor>Henrik Skupin</em:contributor>
-    <em:homepageURL>https://developer.mozilla.org/en-US/docs/TPS</em:homepageURL>
-    <em:multiprocessCompatible>true</em:multiprocessCompatible>
-  </Description>
-</RDF>
new file mode 100644
--- /dev/null
+++ b/services/sync/tps/extensions/tps/manifest.json
@@ -0,0 +1,23 @@
+{
+  "manifest_version": 2,
+  "name": "TPS",
+  "version": "1.0",
+
+  "applications": {
+    "gecko": {
+      "id": "tps@mozilla.org"
+    }
+  },
+
+  "experiment_apis": {
+    "tps": {
+      "schema": "schema.json",
+      "parent": {
+        "scopes": ["addon_parent"],
+        "script": "api.js",
+        "paths": [["tps"]],
+        "events": ["startup"]
+      }
+    }
+  }
+}
--- a/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm
+++ b/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm
@@ -59,17 +59,17 @@ var Authentication = {
       timeoutPromise,
     ]);
     userData = await this.getSignedInUser();
     return userData && userData.verified;
   },
 
   async _openVerificationPage(uri) {
     let mainWindow = Services.wm.getMostRecentWindow("navigator:browser");
-    let newtab = mainWindow.getBrowser().addTab(uri);
+    let newtab = mainWindow.getBrowser().addWebTab(uri);
     let win = mainWindow.getBrowser().getBrowserForTab(newtab);
     await new Promise(resolve => {
       win.addEventListener("loadend", resolve, { once: true });
     });
     let didVerify = await this.shortWaitForVerification(10000);
     mainWindow.getBrowser().removeTab(newtab);
     return didVerify;
   },
--- a/services/sync/tps/extensions/tps/resource/modules/formautofill.jsm
+++ b/services/sync/tps/extensions/tps/resource/modules/formautofill.jsm
@@ -5,18 +5,22 @@
  /* This is a JavaScript module (JSM) to be imported via
   * Components.utils.import() and acts as a singleton. Only the following
   * listed symbols will exposed on import, and only when and where imported.
   */
 
 var EXPORTED_SYMBOLS = ["Address", "CreditCard", "DumpAddresses", "DumpCreditCards"];
 
 ChromeUtils.import("resource://tps/logger.jsm");
-ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm");
-ChromeUtils.import("resource://formautofill/MasterPassword.jsm");
+
+ChromeUtils.defineModuleGetter(this, "formAutofillStorage",
+                               "resource://formautofill/FormAutofillStorage.jsm");
+
+ChromeUtils.defineModuleGetter(this, "MasterPassword",
+                               "resource://formautofill/MasterPassword.jsm");
 
 class FormAutofillBase {
   constructor(props, subStorageName, fields) {
     this._subStorageName = subStorageName;
     this._fields = fields;
 
     this.props = {};
     this.updateProps = null;
new file mode 100644
--- /dev/null
+++ b/services/sync/tps/extensions/tps/schema.json
@@ -0,0 +1,1 @@
+[]
\ No newline at end of file
--- a/testing/tps/tps/phase.py
+++ b/testing/tps/tps/phase.py
@@ -1,13 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import re
+import os.path
 
 class TPSTestPhase(object):
 
     lineRe = re.compile(
         r'^(.*?)test phase (?P<matchphase>[^\s]+): (?P<matchstatus>.*)$')
 
     def __init__(self, phase, profile, testname, testpath, logfile, env,
                  firefoxRunner, logfn, ignore_unused_engines=False):
@@ -24,27 +25,31 @@ class TPSTestPhase(object):
         self.errline = ''
 
     @property
     def status(self):
         return self._status if self._status else 'unknown'
 
     def run(self):
         # launch Firefox
-        args = [ '-tps', self.testpath,
-                 '-tpsphase', self.phase,
-                 '-tpslogfile', self.logfile ]
+
+        prefs = {
+            "testing.tps.testFile": os.path.abspath(self.testpath),
+            "testing.tps.testPhase": self.phase,
+            "testing.tps.logFile": self.logfile,
+            "testing.tps.ignoreUnusedEngines": self.ignore_unused_engines
+        }
 
-        if self.ignore_unused_engines:
-            args.append('--ignore-unused-engines')
+        self.profile.set_preferences(prefs);
 
-        self.log('\nLaunching Firefox for phase %s with args %s\n' %
-                 (self.phase, str(args)))
+        self.log('\nLaunching Firefox for phase %s with prefs %s\n' %
+                 (self.phase, str(prefs)))
+
         self.firefoxRunner.run(env=self.env,
-                               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
@@ -230,17 +230,17 @@ class TPSTestRunner(object):
         # generate the profiles defined in the test, and a list of test phases
         profiles = {}
         phaselist = []
         for phase in test:
             profilename = test[phase]
 
             # create the profile if necessary
             if not profilename in profiles:
-                profiles[profilename] = Profile(preferences = self.preferences,
+                profiles[profilename] = Profile(preferences = self.preferences.copy(),
                                                 addons = self.extensions)
 
             # create the test phase
             phaselist.append(TPSTestPhase(
                 phase,
                 profiles[profilename],
                 testname,
                 testpath,
@@ -257,16 +257,17 @@ class TPSTestRunner(object):
         failed = False
         for phase in phaselist:
             phase.run()
             if phase.status != 'PASS':
                 failed = True
                 break;
 
         for profilename in profiles:
+            print "### Cleanup Profile ", profilename
             cleanup_phase = TPSTestPhase(
                 'cleanup-' + profilename,
                 profiles[profilename], testname,
                 testpath,
                 self.logfile,
                 self.env,
                 self.firefoxRunner,
                 self.log)