Bug 1465806 - Wait for addon manager and session restore full initialization before starting DAMP tests. r=jdescottes draft
authorAlexandre Poirot <poirot.alex@gmail.com>
Tue, 29 May 2018 11:07:08 -0700
changeset 804812 100a5da1ed74228dc739df07c4055ced1f7ed465
parent 803479 fa5724780fe76d6ccbbd08d978342a1db6a43d49
child 805304 d88a61980bf68d865684b508137a373c5c86f3dc
child 805406 6146057e49cfda9d9d39a908ead9de321d6bf88e
push id112469
push userbmo:poirot.alex@gmail.com
push dateWed, 06 Jun 2018 16:23:30 +0000
reviewersjdescottes
bugs1465806
milestone62.0a1
Bug 1465806 - Wait for addon manager and session restore full initialization before starting DAMP tests. r=jdescottes MozReview-Commit-ID: 1xszgL781BU
testing/talos/talos/tests/devtools/addon/content/damp.js
testing/talos/talos/tests/devtools/addon/content/tests/head.js
--- a/testing/talos/talos/tests/devtools/addon/content/damp.js
+++ b/testing/talos/talos/tests/devtools/addon/content/damp.js
@@ -1,10 +1,11 @@
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
 const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
+const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
 const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
 
 XPCOMUtils.defineLazyGetter(this, "require", function() {
   let { require } =
     ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
   return require;
 });
 XPCOMUtils.defineLazyGetter(this, "gDevTools", function() {
@@ -158,16 +159,31 @@ Damp.prototype = {
 
   async addTab(url) {
     let tab = this._win.gBrowser.selectedTab = this._win.gBrowser.addTab(url);
     let browser = tab.linkedBrowser;
     await awaitBrowserLoaded(browser);
     return tab;
   },
 
+  async waitForPendingPaints(window) {
+    let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIDOMWindowUtils);
+    window.performance.mark("pending paints.start");
+    while (utils.isMozAfterPaintPending) {
+      await new Promise(done => {
+        window.addEventListener("MozAfterPaint", function listener() {
+          window.performance.mark("pending paint");
+          done();
+        }, { once: true });
+      });
+    }
+    window.performance.measure("pending paints", "pending paints.start");
+  },
+
   closeCurrentTab() {
     this._win.BrowserCloseTabOrWindow();
     return this._win.gBrowser.selectedTab;
   },
 
   reloadPage(onReload) {
     return new Promise(resolve => {
       let browser = gBrowser.selectedBrowser;
@@ -195,19 +211,16 @@ Damp.prototype = {
     await this.garbageCollect();
 
     let duration = Math.round(performance.now() - this._startTime);
     dump(`${this._currentTest} took ${duration}ms.\n`);
 
     this._runNextTest();
   },
 
-  // Everything below here are common pieces needed for the test runner to function,
-  // just copy and pasted from Tart with /s/TART/DAMP
-
   _win: undefined,
   _dampTab: undefined,
   _results: [],
   _config: {subtests: [], repeat: 1, rest: 100},
   _nextTestIndex: 0,
   _tests: [],
   _onSequenceComplete: 0,
 
@@ -345,16 +358,50 @@ Damp.prototype = {
     this._doneInternal();
   },
 
   exception(e) {
     this.error(e);
     dump(e.stack + "\n");
   },
 
+  // Waits for any pending operations that may execute on Firefox startup and that
+  // can still be pending when we start running DAMP tests.
+  async waitBeforeRunningTests() {
+    // Addons may still be being loaded, so wait for them to be fully set up.
+    if (!AddonManager.isReady) {
+      let onAddonManagerReady = new Promise(resolve => {
+        let listener = {
+          onStartup() {
+            AddonManager.removeManagerListener(listener);
+            resolve();
+          },
+          onShutdown() {},
+        };
+        AddonManager.addManagerListener(listener);
+      });
+      await onAddonManagerReady;
+    }
+
+    // SessionRestore triggers some saving sequence on idle,
+    // so wait for that to be processed before starting tests.
+    // https://searchfox.org/mozilla-central/rev/83a923ef7a3b95a516f240a6810c20664b1e0ac9/browser/components/sessionstore/content/content-sessionStore.js#828-830
+    // https://searchfox.org/mozilla-central/rev/83a923ef7a3b95a516f240a6810c20664b1e0ac9/browser/components/sessionstore/content/content-sessionStore.js#858
+    await new Promise(resolve => {
+      setTimeout(resolve, 1500);
+    });
+    await new Promise(resolve => {
+      requestIdleCallback(resolve, { timeout: 15000 });
+    });
+
+    // Free memory before running the first test, otherwise we may have a GC
+    // related to Firefox startup or DAMP setup during the first test.
+    await this.garbageCollect();
+  },
+
   startTest(doneCallback, config) {
     try {
       dump("Initialize the head file with a reference to this DAMP instance\n");
       let head = require("chrome://damp/content/tests/head.js");
       head.initialize(this);
 
       this._onTestComplete = function(results) {
         TalosParentProfiler.pause("DAMP - end");
@@ -389,19 +436,17 @@ Damp.prototype = {
       // Construct the sequence array while filtering tests
       let sequenceArray = [];
       for (let test of tests) {
         for (let r = 0; r < config.repeat; r++) {
           sequenceArray.push(test.path);
         }
       }
 
-      // Free memory before running the first test, otherwise we may have a GC
-      // related to Firefox startup or DAMP setup during the first test.
-      this.garbageCollect().then(() => {
+     this.waitBeforeRunningTests().then(() => {
         this._doSequence(sequenceArray, this._doneInternal);
       }).catch(e => {
         this.exception(e);
       });
     } catch (e) {
       this.exception(e);
     }
   }
--- a/testing/talos/talos/tests/devtools/addon/content/tests/head.js
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/head.js
@@ -3,18 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 /**
  * This helper contains the public API that can be used by DAMP tests.
  */
 
-// eslint-disable-next-line mozilla/no-define-cc-etc
-const { Ci } = require("chrome");
 const Services = require("Services");
 const { gDevTools } = require("devtools/client/framework/devtools");
 const { TargetFactory } = require("devtools/client/framework/target");
 
 const webserver = Services.prefs.getCharPref("addon.test.damp.webserver");
 
 const PAGES_BASE_URL = webserver + "/tests/devtools/addon/content/pages/";
 
@@ -72,29 +70,17 @@ exports.getToolbox = function() {
  * Wait for any pending paint.
  * The tool may have touched the DOM elements at the very end of the current test.
  * We should ensure waiting for the reflow related to these changes.
  */
 async function waitForPendingPaints(toolbox) {
   let panel = toolbox.getCurrentPanel();
   // All panels have its own way of exposing their window object...
   let window = panel.panelWin || panel._frameWindow || panel.panelWindow;
-
-  let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindowUtils);
-  window.performance.mark("pending paints.start");
-  while (utils.isMozAfterPaintPending) {
-    await new Promise(done => {
-      window.addEventListener("MozAfterPaint", function listener() {
-        window.performance.mark("pending paint");
-        done();
-      }, { once: true });
-    });
-  }
-  window.performance.measure("pending paints", "pending paints.start");
+  return damp.waitForPendingPaints(window);
 }
 
 const openToolbox = async function(tool = "webconsole", onLoad) {
   let tab = getActiveTab();
   let target = TargetFactory.forTab(tab);
   let onToolboxCreated = gDevTools.once("toolbox-created");
   let showPromise = gDevTools.showToolbox(target, tool);
   let toolbox = await onToolboxCreated;