Bug 1488899 - Get TPS working. r=tcsc
authorMark Hammond <mhammond@skippinet.com.au>
Mon, 15 Oct 2018 22:42:22 +0000
changeset 441344 5622861eb245cc20b3a121ffb73f1b58c9745934
parent 441343 336f795d5b2621dd2d693649f815ad65182f5c17
child 441345 1119f9458b5d76cef62577b526c508f7908f4710
push id34860
push usernerli@mozilla.com
push dateTue, 16 Oct 2018 04:26:23 +0000
treeherdermozilla-central@8ceb713af9c7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcsc
bugs1488899
milestone64.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 1488899 - Get TPS working. r=tcsc This patch disables a couple of tests, removes old async support from TPS, and puts more effort into preventing Syncs while TPS is running, and removed an unused import. Differential Revision: https://phabricator.services.mozilla.com/D8692
services/sync/modules/browserid_identity.js
services/sync/tests/tps/.eslintrc.js
services/sync/tests/tps/all_tests.json
services/sync/tests/tps/test_addon_reconciling.js
services/sync/tests/tps/test_addon_restartless_xpi.js
services/sync/tests/tps/test_addon_webext_xpi.js
services/sync/tests/tps/test_addon_wipe.js
services/sync/tests/tps/test_addresses.js
services/sync/tests/tps/test_creditcards.js
services/sync/tests/tps/test_tabs.js
services/sync/tps/extensions/tps/api.js
services/sync/tps/extensions/tps/resource/modules/tabs.jsm
services/sync/tps/extensions/tps/resource/modules/windows.jsm
services/sync/tps/extensions/tps/resource/tps.jsm
testing/tps/create_venv.py
testing/tps/tps/testrunner.py
--- a/services/sync/modules/browserid_identity.js
+++ b/services/sync/modules/browserid_identity.js
@@ -251,17 +251,19 @@ this.BrowserIDManager.prototype = {
       let isFirstSync = !Weave.Service.locked && !Svc.Prefs.get("client.syncID", null);
       if (isFirstSync) {
         this._log.info("Doing initial sync actions");
         Svc.Prefs.set("firstSync", "resetClient");
         Services.obs.notifyObservers(null, "weave:service:setup-complete");
       }
       // There's no need to wait for sync to complete and it would deadlock
       // our AsyncObserver.
-      Weave.Service.sync({why: "login"});
+      if (!Svc.Prefs.get("testing.tps", false)) {
+        Weave.Service.sync({why: "login"});
+      }
       break;
     }
 
     case fxAccountsCommon.ONLOGOUT_NOTIFICATION:
       Weave.Service.startOver().then(() => {
         this._log.trace("startOver completed");
       }).catch(err => {
         this._log.warn("Failed to reset sync", err);
--- a/services/sync/tests/tps/.eslintrc.js
+++ b/services/sync/tests/tps/.eslintrc.js
@@ -22,10 +22,20 @@ module.exports = {
     "STATE_DISABLED": false,
     "STATE_ENABLED": false,
     "Sync": false,
     "SYNC_WIPE_CLIENT": false,
     "SYNC_WIPE_REMOTE": false,
     "Tabs": false,
     "Windows": false,
     "WipeServer": false,
-  }
+  },
+  // TPS test files are also hackily parsed by python's JSON parser,
+  // so trailing commas aren't valid.
+  "overrides": [{
+    "files": [
+      "test_*.js",
+    ],
+    "rules": {
+      "comma-dangle": "off",
+    }
+  }]
 };
--- a/services/sync/tests/tps/all_tests.json
+++ b/services/sync/tests/tps/all_tests.json
@@ -1,34 +1,34 @@
-{ "tests": [
-    "test_bookmark_conflict.js",
-    "test_sync.js",
-    "test_prefs.js",
-    "test_tabs.js",
-    "test_passwords.js",
-    "test_history.js",
-    "test_formdata.js",
-    "test_bug530717.js",
-    "test_bug531489.js",
-    "test_bug538298.js",
-    "test_bug556509.js",
-    "test_bug562515.js",
-    "test_bug535326.js",
-    "test_bug501528.js",
-    "test_bug575423.js",
-    "test_bug546807.js",
-    "test_history_collision.js",
-    "test_privbrw_passwords.js",
-    "test_privbrw_tabs.js",
-    "test_bookmarks_in_same_named_folder.js",
-    "test_client_wipe.js",
-    "test_special_tabs.js",
-    "test_addon_restartless_xpi.js",
-    "test_addon_webext_xpi.js",
-    "test_addon_reconciling.js",
-    "test_addon_wipe.js",
-    "test_existing_bookmarks.js",
-    "test_addresses.js",
-    "test_creditcards.js"
-  ]
+{ "tests": {
+    "test_bookmark_conflict.js": {},
+    "test_sync.js": {},
+    "test_prefs.js": {},
+    "test_tabs.js": {},
+    "test_passwords.js": {},
+    "test_history.js": {},
+    "test_formdata.js": {},
+    "test_bug530717.js": {},
+    "test_bug531489.js": {},
+    "test_bug538298.js": {},
+    "test_bug556509.js": {},
+    "test_bug562515.js": {},
+    "test_bug535326.js": {},
+    "test_bug501528.js": {},
+    "test_bug575423.js": {},
+    "test_bug546807.js": {},
+    "test_history_collision.js": {},
+    "test_privbrw_passwords.js": {},
+    "test_privbrw_tabs.js": {},
+    "test_bookmarks_in_same_named_folder.js": {},
+    "test_client_wipe.js": {},
+    "test_special_tabs.js": {},
+    "test_addon_restartless_xpi.js": {"disabled": "Bug 1498974"},
+    "test_addon_webext_xpi.js": {"disabled": "Bug 1498974"},
+    "test_addon_reconciling.js": {"disabled": "Bug 1498974"},
+    "test_addon_wipe.js": {},
+    "test_existing_bookmarks.js": {},
+    "test_addresses.js": {},
+    "test_creditcards.js": {}
+  }
 }
 
 
--- a/services/sync/tests/tps/test_addon_reconciling.js
+++ b/services/sync/tests/tps/test_addon_reconciling.js
@@ -7,17 +7,17 @@
 EnableEngines(["addons"]);
 
 var phases = {
   "phase01": "profile1",
   "phase02": "profile2",
   "phase03": "profile1",
   "phase04": "profile2",
   "phase05": "profile1",
-  "phase06": "profile2",
+  "phase06": "profile2"
 };
 
 const id = "restartless-xpi@tests.mozilla.org";
 
 // Install the add-on in 2 profiles.
 Phase("phase01", [
   [Addons.verifyNot, [id]],
   [Addons.install, [id]],
--- a/services/sync/tests/tps/test_addon_restartless_xpi.js
+++ b/services/sync/tests/tps/test_addon_restartless_xpi.js
@@ -8,17 +8,17 @@ EnableEngines(["addons"]);
 var phases = {
   "phase01": "profile1",
   "phase02": "profile2",
   "phase03": "profile1",
   "phase04": "profile2",
   "phase05": "profile1",
   "phase06": "profile2",
   "phase07": "profile1",
-  "phase08": "profile2",
+  "phase08": "profile2"
 };
 
 const id = "restartless-xpi@tests.mozilla.org";
 
 // Verify install is synced
 Phase("phase01", [
   [Addons.verifyNot, [id]],
   [Addons.install, [id]],
--- a/services/sync/tests/tps/test_addon_webext_xpi.js
+++ b/services/sync/tests/tps/test_addon_webext_xpi.js
@@ -9,17 +9,17 @@ EnableEngines(["addons"]);
 var phases = {
   "phase01": "profile1",
   "phase02": "profile2",
   "phase03": "profile1",
   "phase04": "profile2",
   "phase05": "profile1",
   "phase06": "profile2",
   "phase07": "profile1",
-  "phase08": "profile2",
+  "phase08": "profile2"
 };
 
 const id = "test-webext@quality.mozilla.org";
 
 // Verify install is synced
 Phase("phase01", [
   [Addons.verifyNot, [id]],
   [Addons.install, [id]],
--- a/services/sync/tests/tps/test_addon_wipe.js
+++ b/services/sync/tests/tps/test_addon_wipe.js
@@ -6,17 +6,17 @@
 // specifically around AddonsReconciler. This test is in response to bug
 // 792990.
 
 EnableEngines(["addons"]);
 
 var phases = {
   "phase01": "profile1",
   "phase02": "profile1",
-  "phase03": "profile1",
+  "phase03": "profile1"
 };
 
 const id1 = "restartless-xpi@tests.mozilla.org";
 const id2 = "test-webext@quality.mozilla.org";
 
 Phase("phase01", [
   [Addons.install, [id1]],
   [Addons.install, [id2]],
--- a/services/sync/tests/tps/test_addresses.js
+++ b/services/sync/tests/tps/test_addresses.js
@@ -4,17 +4,17 @@
 /* global Services */
 Services.prefs.setBoolPref("services.sync.engine.addresses", true);
 
 EnableEngines(["addresses"]);
 
 var phases = {
   "phase1": "profile1",
   "phase2": "profile2",
-  "phase3": "profile1",
+  "phase3": "profile1"
 };
 
 const address1 = [{
   "given-name": "Timothy",
   "additional-name": "John",
   "family-name": "Berners-Lee",
   "organization": "World Wide Web Consortium",
   "street-address": "32 Vassar Street\nMIT Room 32-G524",
--- a/services/sync/tests/tps/test_creditcards.js
+++ b/services/sync/tests/tps/test_creditcards.js
@@ -4,17 +4,17 @@
 /* global Services */
 Services.prefs.setBoolPref("services.sync.engine.creditcards", true);
 
 EnableEngines(["creditcards"]);
 
 var phases = {
   "phase1": "profile1",
   "phase2": "profile2",
-  "phase3": "profile1",
+  "phase3": "profile1"
 };
 
 const cc1 = [{
   "cc-name": "John Doe",
   "cc-number": "4716179744040592",
   "cc-exp-month": 4,
   "cc-exp-year": 2050,
   "changes": {
--- a/services/sync/tests/tps/test_tabs.js
+++ b/services/sync/tests/tps/test_tabs.js
@@ -13,28 +13,26 @@ var phases = { "phase1": "profile1",
                "phase3": "profile1"};
 
 /*
  * Tab lists.
  */
 
 var tabs1 = [
   { uri: "https://www.mozilla.org/en-US/firefox/",
-    title: "Firefox",
     profile: "profile1",
   },
   { uri: "data:text/html,<html><head><title>Hello</title></head><body>Hello</body></html>",
     title: "Hello",
     profile: "profile1",
   },
 ];
 
 var tabs2 = [
   { uri: "https://www.mozilla.org/en-US/contribute/",
-    title: "Get Involved",
     profile: "profile2",
   },
   { uri: "data:text/html,<html><head><title>Bye</title></head><body>Bye</body></html>",
     profile: "profile2",
   },
 ];
 
 /*
--- a/services/sync/tps/extensions/tps/api.js
+++ b/services/sync/tps/extensions/tps/api.js
@@ -37,16 +37,17 @@ async function tpsStartup() {
     } 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", e);
     }
+    dump(`TPS test initialization failed: ${e} - ${e.stack}\n`);
     // 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 => {
--- a/services/sync/tps/extensions/tps/resource/modules/tabs.jsm
+++ b/services/sync/tps/extensions/tps/resource/modules/tabs.jsm
@@ -35,17 +35,17 @@ var BrowserTabs = {
    * given uri. Rejects on error.
    *
    * @param uri The uri to load in the new tab
    * @return Promise
    */
   async Add(uri) {
     let mainWindow = Services.wm.getMostRecentWindow("navigator:browser");
     let browser = mainWindow.getBrowser();
-    let newtab = browser.addTab(uri);
+    let newtab = browser.addTrustedTab(uri);
 
     // Wait for the tab to load.
     await new Promise(resolve => {
       let mm = browser.ownerGlobal.messageManager;
       mm.addMessageListener("tps:loadEvent", function onLoad(msg) {
         mm.removeMessageListener("tps:loadEvent", onLoad);
         resolve();
       });
@@ -79,14 +79,15 @@ var BrowserTabs = {
       for (let key in tabClient.tabs) {
         let tab = tabClient.tabs[key];
         let weaveTabUrl = tab.urlHistory[0];
         if (uri == weaveTabUrl && profile == client.name) {
           if (title == undefined || title == tab.title) {
             return true;
           }
         }
-        }
-        Logger.logInfo(`Dumping tabs for ${client.clientName}...\n` + JSON.stringify(client.tabs));
       }
+      Logger.logInfo(`Dumping tabs for ${client.name}...\n` +
+                     JSON.stringify(tabClient.tabs, null, 2));
+    }
     return false;
   },
 };
--- a/services/sync/tps/extensions/tps/resource/modules/windows.jsm
+++ b/services/sync/tps/extensions/tps/resource/modules/windows.jsm
@@ -17,15 +17,17 @@ var BrowserWindows = {
    * Add
    *
    * Opens a new window. Throws on error.
    *
    * @param aPrivate The private option.
    * @return nothing
    */
   Add(aPrivate, fn) {
-    let mainWindow = Services.wm.getMostRecentWindow("navigator:browser");
-    let win = mainWindow.OpenBrowserWindow({private: aPrivate});
-    win.addEventListener("load", function() {
-      fn.call(win);
-    }, {once: true});
+    return new Promise(resolve => {
+      let mainWindow = Services.wm.getMostRecentWindow("navigator:browser");
+      let win = mainWindow.OpenBrowserWindow({private: aPrivate});
+      win.addEventListener("load", function() {
+        resolve(win);
+      }, {once: true});
+    });
   },
 };
--- a/services/sync/tps/extensions/tps/resource/tps.jsm
+++ b/services/sync/tps/extensions/tps/resource/tps.jsm
@@ -11,26 +11,26 @@ var EXPORTED_SYMBOLS = [
   "ACTIONS", "Addons", "Addresses", "Bookmarks", "CreditCards",
   "Formdata", "History", "Passwords", "Prefs",
   "Tabs", "TPS", "Windows",
 ];
 
 var module = this;
 
 // Global modules
-ChromeUtils.import("resource://formautofill/FormAutofillSync.jsm");
 ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
 ChromeUtils.import("resource://gre/modules/osfile.jsm");
+ChromeUtils.import("resource:///modules/sessionstore/SessionStore.jsm");
 ChromeUtils.import("resource://services-common/async.js");
 ChromeUtils.import("resource://services-common/utils.js");
 ChromeUtils.import("resource://services-sync/constants.js");
 ChromeUtils.import("resource://services-sync/main.js");
 ChromeUtils.import("resource://services-sync/util.js");
 ChromeUtils.import("resource://services-sync/telemetry.js");
 ChromeUtils.import("resource://services-sync/bookmark_validator.js");
 ChromeUtils.import("resource://services-sync/engines/passwords.js");
@@ -90,17 +90,16 @@ const ACTIONS = [
   ACTION_SYNC_WIPE_REMOTE,
   ACTION_VERIFY,
   ACTION_VERIFY_NOT,
 ];
 
 const OBSERVER_TOPICS = ["fxaccounts:onlogin",
                          "fxaccounts:onlogout",
                          "profile-before-change",
-                         "sessionstore-windows-restored",
                          "weave:service:tracking-started",
                          "weave:service:tracking-stopped",
                          "weave:service:login:error",
                          "weave:service:setup-complete",
                          "weave:service:sync:finish",
                          "weave:service:sync:delayed",
                          "weave:service:sync:error",
                          "weave:service:sync:start",
@@ -109,17 +108,16 @@ const OBSERVER_TOPICS = ["fxaccounts:onl
                         ];
 
 var TPS = {
   _currentAction: -1,
   _currentPhase: -1,
   _enabledEngines: null,
   _errors: 0,
   _isTracking: false,
-  _operations_pending: 0,
   _phaseFinished: false,
   _phaselist: {},
   _setupComplete: false,
   _syncActive: false,
   _syncCount: 0,
   _syncsReportedViaTelemetry: 0,
   _syncErrors: 0,
   _syncWipeAction: null,
@@ -128,28 +126,37 @@ var TPS = {
   _test: null,
   _triggeredSync: false,
   _msSinceEpoch: 0,
   _requestedQuit: false,
   shouldValidateAddons: false,
   shouldValidateBookmarks: false,
   shouldValidatePasswords: false,
   shouldValidateForms: false,
-  _windowsUpDeferred: PromiseUtils.defer(),
   _placesInitDeferred: PromiseUtils.defer(),
 
   _init: function TPS__init() {
     this.delayAutoSync();
 
     OBSERVER_TOPICS.forEach(function(aTopic) {
       Services.obs.addObserver(this, aTopic, true);
     }, this);
 
     /* global Authentication */
     ChromeUtils.import("resource://tps/auth/fxaccounts.jsm", module);
+
+    // Some engines bump their score during their sync, which then causes
+    // another sync immediately (notably, prefs and addons). We don't want
+    // this to happen, and there's no obvious preference to kill it - so
+    // we do this nasty hack to ensure the global score is always zero.
+    Services.prefs.addObserver("services.sync.globalScore", () => {
+      if (Weave.Service.scheduler.globalScore != 0) {
+        Weave.Service.scheduler.globalScore = 0;
+      }
+    });
   },
 
   DumpError(msg, exc = null) {
     this._errors++;
     let errInfo;
     if (exc) {
       errInfo = Log.exceptionStr(exc); // includes details and stack-trace.
     } else {
@@ -172,20 +179,16 @@ var TPS = {
           OBSERVER_TOPICS.forEach(function(topic) {
             Services.obs.removeObserver(this, topic);
           }, this);
 
           Logger.close();
 
           break;
 
-        case "sessionstore-windows-restored":
-          this._windowsUpDeferred.resolve();
-          break;
-
         case "places-browser-init-complete":
           this._placesInitDeferred.resolve();
           break;
 
         case "weave:service:setup-complete":
           this._setupComplete = true;
 
           if (this._syncWipeAction) {
@@ -218,23 +221,16 @@ var TPS = {
           break;
 
         case "weave:service:resyncs-finished":
           this._syncActive = false;
           this._syncErrors = 0;
           this._triggeredSync = false;
 
           this.delayAutoSync();
-
-          // Wait a second before continuing, otherwise we can get
-          // 'sync not complete' errors.
-          CommonUtils.namedTimer(function() {
-            this.FinishAsyncOperation();
-          }, 1000, this, "postsync");
-
           break;
 
         case "weave:service:sync:start":
           // Ensure that the sync operation has been started by TPS
           if (!this._triggeredSync) {
             this.DumpError("Automatic sync got triggered, which is not allowed.");
           }
 
@@ -251,65 +247,39 @@ var TPS = {
       }
     } catch (e) {
       this.DumpError("Observer failed", e);
 
     }
   },
 
   /**
-   * Given that we cannot complely disable the automatic sync operations, we
+   * Given that we cannot completely disable the automatic sync operations, we
    * massively delay the next sync. Sync operations have to only happen when
    * directly called via TPS.Sync()!
    */
   delayAutoSync: function TPS_delayAutoSync() {
     Weave.Svc.Prefs.set("scheduler.immediateInterval", 7200);
     Weave.Svc.Prefs.set("scheduler.idleInterval", 7200);
     Weave.Svc.Prefs.set("scheduler.activeInterval", 7200);
     Weave.Svc.Prefs.set("syncThreshold", 10000000);
   },
 
-  StartAsyncOperation: function TPS__StartAsyncOperation() {
-    this._operations_pending++;
-  },
-
-  FinishAsyncOperation: function TPS__FinishAsyncOperation() {
-    // We fire a FinishAsyncOperation at the end of a sync finish (somewhat
-    // dubious, but we're consistent about it), but a sync may or may not be
-    // triggered during login, and it's hard for us to know (without e.g.
-    // auth/fxaccounts.jsm calling back into this module). So we just assume
-    // the FinishAsyncOperation without a StartAsyncOperation is fine, and works
-    // as if a StartAsyncOperation had been called.
-    if (this._operations_pending) {
-      this._operations_pending--;
-    }
-    if (!this.operations_pending) {
-      this._currentAction++;
-      CommonUtils.nextTick(function() {
-        this.RunNextTestAction().catch(err => {
-          this.DumpError("RunNextTestActionFailed", err);
-        });
-      }, this);
-    }
-  },
-
   quit: function TPS__quit() {
+    Logger.logInfo("quitting");
     this._requestedQuit = true;
     this.goQuitApplication();
   },
 
-  HandleWindows(aWindow, action) {
+  async HandleWindows(aWindow, action) {
     Logger.logInfo("executing action " + action.toUpperCase() +
                    " on window " + JSON.stringify(aWindow));
     switch (action) {
       case ACTION_ADD:
-        BrowserWindows.Add(aWindow.private, win => {
-          Logger.logInfo("window finished loading");
-          this.FinishAsyncOperation();
-        });
+        await BrowserWindows.Add(aWindow.private);
         break;
     }
     Logger.logPass("executing action " + action.toUpperCase() + " on windows");
   },
 
   async HandleTabs(tabs, action) {
     for (let tab of tabs) {
       Logger.logInfo("executing action " + action.toUpperCase() +
@@ -779,19 +749,21 @@ var TPS = {
     return this.ValidateCollection("forms", FormValidator);
   },
 
   ValidateAddons() {
     return this.ValidateCollection("addons", AddonValidator);
   },
 
   async RunNextTestAction() {
+    Logger.logInfo("Running next test action");
     try {
       if (this._currentAction >= this._phaselist[this._currentPhase].length) {
         // Run necessary validations and then finish up
+        Logger.logInfo("No more actions - running validations...");
         if (this.shouldValidateBookmarks) {
           await this.ValidateBookmarks();
         }
         if (this.shouldValidatePasswords) {
           await this.ValidatePasswords();
         }
         if (this.shouldValidateForms) {
           await this.ValidateForms();
@@ -821,21 +793,16 @@ var TPS = {
         return;
       }
 
       let phase = this._phaselist[this._currentPhase];
       let action = phase[this._currentAction];
       Logger.logInfo("starting action: " + action[0].name);
       await action[0].apply(this, action.slice(1));
 
-      // if we're in an async operation, don't continue on to the next action
-      if (this._operations_pending) {
-        return;
-      }
-
       this._currentAction++;
     } catch (e) {
       if (Async.isShutdownException(e)) {
         if (this._requestedQuit) {
           Logger.logInfo("Sync aborted due to requested shutdown");
         } else {
           this.DumpError("Sync aborted due to shutdown, but we didn't request it");
         }
@@ -1012,17 +979,17 @@ var TPS = {
 
       Logger.logInfo("setting client.name to " + this.phases[this._currentPhase]);
       Weave.Svc.Prefs.set("client.name", this.phases[this._currentPhase]);
 
       this._interceptSyncTelemetry();
 
       // start processing the test actions
       this._currentAction = 0;
-      await this._windowsUpDeferred.promise;
+      await SessionStore.promiseAllWindowsRestored;
       await this.RunNextTestAction();
     } catch (e) {
       this.DumpError("_executeTestPhase failed", e);
     }
   },
 
   /**
    * Override sync telemetry functions so that we can detect errors generating
@@ -1074,18 +1041,19 @@ var TPS = {
    *
    * @param  phasename
    *         String name of the phase being loaded.
    * @param  fnlist
    *         Array of functions/actions to perform.
    */
   Phase: function Test__Phase(phasename, fnlist) {
     if (Object.keys(this._phaselist).length === 0) {
-      // This is the first phase, add that we need to login.
-      fnlist.unshift([this.Login]);
+      // This is the first phase we should wipe the server - this has the
+      // side-effect of forcing a login, which we also need.
+      fnlist.unshift([this.WipeServer]);
     }
     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
@@ -1162,97 +1130,98 @@ var TPS = {
       await this.waitForEvent("weave:service:setup-complete");
     }
   },
 
   /**
    * Waits for Sync to be finished before returning
    */
   async waitForSyncFinished() {
-    if (this._syncActive) {
+    if (Weave.Service.locked) {
       await this.waitForEvent("weave:service:resyncs-finished");
     }
   },
 
   /**
    * Waits for Sync to start tracking before returning.
    */
   async waitForTracking() {
     if (!this._isTracking) {
       await this.waitForEvent("weave:service:tracking-started");
     }
   },
 
   /**
    * Login on the server
    */
-  async Login(force) {
-    if ((await Authentication.isReady()) && !force) {
+  async Login() {
+    if ((await Authentication.isReady())) {
       return;
     }
 
-    // This might come during Authentication.signIn
-    this._triggeredSync = true;
     Logger.logInfo("Setting client credentials and login.");
     await Authentication.signIn(this.config.fx_account);
     await this.waitForSetupComplete();
     Logger.AssertEqual(Weave.Status.service, Weave.STATUS_OK, "Weave status OK");
     await this.waitForTracking();
-    // We might get an initial sync at login time - let that complete.
-    await this.waitForSyncFinished();
   },
 
   /**
    * Triggers a sync operation
    *
    * @param {String} [wipeAction]
    *        Type of wipe to perform (resetClient, wipeClient, wipeRemote)
    *
    */
   async Sync(wipeAction) {
     if (this._syncActive) {
-      Logger.logInfo("WARNING: Sync currently active! Waiting, before triggering another");
-      await this.waitForSyncFinished();
+      this.DumpError("Sync currently active which should be impossible");
+      return;
     }
     Logger.logInfo("Executing Sync" + (wipeAction ? ": " + wipeAction : ""));
 
     // Force a wipe action if requested. In case of an initial sync the pref
     // will be overwritten by Sync itself (see bug 992198), so ensure that we
     // also handle it via the "weave:service:setup-complete" notification.
     if (wipeAction) {
       this._syncWipeAction = wipeAction;
       Weave.Svc.Prefs.set("firstSync", wipeAction);
     } else {
       Weave.Svc.Prefs.reset("firstSync");
     }
     if (!await Weave.Service.login()) {
       // We need to complete verification.
-      await this.Login(false);
+      Logger.logInfo("Logging in before performing sync");
+      await this.Login();
     }
     ++this._syncCount;
 
+    Logger.logInfo("Executing Sync" + (wipeAction ? ": " + wipeAction : ""));
     this._triggeredSync = true;
-    this.StartAsyncOperation();
     await Weave.Service.sync();
     Logger.logInfo("Sync is complete");
+    // wait a second for things to settle...
+    await new Promise(resolve => {
+      CommonUtils.namedTimer(resolve, 1000, this, "postsync");
+    });
   },
 
   async WipeServer() {
     Logger.logInfo("Wiping data from server.");
 
-    await this.Login(false);
+    await this.Login();
     await Weave.Service.login();
     await Weave.Service.wipeServer();
   },
 
   /**
    * Action which ensures changes are being tracked before returning.
    */
   async EnsureTracking() {
-    await this.Login(false);
+    await this.Login();
     await this.waitForTracking();
   },
 };
 
 var Addons = {
   async install(addons) {
     await TPS.HandleAddons(addons, ACTION_ADD);
   },
@@ -1398,16 +1367,15 @@ var Tabs = {
     await TPS.HandleTabs(tabs, ACTION_VERIFY);
   },
   async verifyNot(tabs) {
     await TPS.HandleTabs(tabs, ACTION_VERIFY_NOT);
   },
 };
 
 var Windows = {
-  add: function Window__add(aWindow) {
-    TPS.StartAsyncOperation();
-    TPS.HandleWindows(aWindow, ACTION_ADD);
+  async add(aWindow) {
+    await TPS.HandleWindows(aWindow, ACTION_ADD);
   },
 };
 
 // Initialize TPS
 TPS._init();
--- a/testing/tps/create_venv.py
+++ b/testing/tps/create_venv.py
@@ -100,16 +100,20 @@ def update_configfile(source, target, re
 
     with open(target, 'w') as config:
         for line in lines:
             config.write(line)
 
 
 def main():
     parser = optparse.OptionParser('Usage: %prog [options] path_to_venv')
+    parser.add_option('--keep-config',
+                      dest='keep_config',
+                      action='store_true',
+                      help='Keep the existing config file.')
     parser.add_option('--password',
                       type='string',
                       dest='password',
                       metavar='FX_ACCOUNT_PASSWORD',
                       default=None,
                       help='The Firefox Account password.')
     parser.add_option('-p', '--python',
                       type='string',
@@ -165,30 +169,31 @@ def main():
                                             'sync'))
     if os.path.exists(sync_dir):
         testdir = os.path.join(sync_dir, 'tests', 'tps')
         extdir = os.path.join(sync_dir, 'tps', 'extensions')
     else:
         testdir = os.path.join(here, 'tests')
         extdir = os.path.join(here, 'extensions')
 
-    update_configfile(os.path.join(here, 'config', 'config.json.in'),
-                      os.path.join(target, 'config.json'),
-                      replacements={
-                      '__TESTDIR__': testdir.replace('\\','/'),
-                      '__EXTENSIONDIR__': extdir.replace('\\','/'),
-                      '__FX_ACCOUNT_USERNAME__': options.username,
-                      '__FX_ACCOUNT_PASSWORD__': options.password,
-                      '__SYNC_ACCOUNT_USERNAME__': options.sync_username,
-                      '__SYNC_ACCOUNT_PASSWORD__': options.sync_password,
-                      '__SYNC_ACCOUNT_PASSPHRASE__': options.sync_passphrase})
+    if not options.keep_config:
+        update_configfile(os.path.join(here, 'config', 'config.json.in'),
+                          os.path.join(target, 'config.json'),
+                          replacements={
+                          '__TESTDIR__': testdir.replace('\\','/'),
+                          '__EXTENSIONDIR__': extdir.replace('\\','/'),
+                          '__FX_ACCOUNT_USERNAME__': options.username,
+                          '__FX_ACCOUNT_PASSWORD__': options.password,
+                          '__SYNC_ACCOUNT_USERNAME__': options.sync_username,
+                          '__SYNC_ACCOUNT_PASSWORD__': options.sync_password,
+                          '__SYNC_ACCOUNT_PASSPHRASE__': options.sync_passphrase})
 
-    if not (options.username and options.password):
-        print '\nFirefox Account credentials not specified.'
-    if not (options.sync_username and options.sync_password and options.passphrase):
-        print '\nFirefox Sync account credentials not specified.'
+        if not (options.username and options.password):
+            print '\nFirefox Account credentials not specified.'
+        if not (options.sync_username and options.sync_password and options.passphrase):
+            print '\nFirefox Sync account credentials not specified.'
 
     # Print the user instructions
     print usage_message.format(TARGET=target,
                                BIN_NAME=bin_name)
 
 if __name__ == "__main__":
     main()
--- a/testing/tps/tps/testrunner.py
+++ b/testing/tps/tps/testrunner.py
@@ -69,16 +69,17 @@ class TPSTestRunner(object):
         # Our pretend addons server doesn't support metadata...
         'extensions.getAddons.cache.enabled': False,
         'extensions.install.requireSecureOrigin': False,
         'extensions.update.enabled': False,
         # Don't open a dialog to show available add-on updates
         'extensions.update.notifyUser': False,
         'services.sync.firstSync': 'notReady',
         'services.sync.lastversion': '1.0',
+        'services.sync.autoconnectDelay': 60 * 60 * 10,
         'toolkit.startup.max_resumed_crashes': -1,
         # hrm - not sure what the release/beta channels will do?
         'xpinstall.signatures.required': False,
         'services.sync.testing.tps': True,
         'engine.bookmarks.repair.enabled': False,
         'extensions.legacy.enabled': True,
     }
 
@@ -432,17 +433,23 @@ class TPSTestRunner(object):
         self.extensions.append(os.path.join(self.extensionDir, 'tps'))
 
         # build the test list
         try:
             f = open(self.testfile)
             jsondata = f.read()
             f.close()
             testfiles = json.loads(jsondata)
-            testlist = testfiles['tests']
+            testlist = []
+            for (filename, meta) in testfiles['tests'].items():
+                skip_reason = meta.get("disabled")
+                if skip_reason:
+                    print 'Skipping test %s - %s' % (filename, skip_reason)
+                else:
+                    testlist.append(filename)
         except ValueError:
             testlist = [os.path.basename(self.testfile)]
         testdir = os.path.dirname(self.testfile)
 
         self.mozhttpd = MozHttpd(port=4567, docroot=testdir)
         self.mozhttpd.start()
 
         # run each test, and save the results