Bug 1346854 - Adjust network monitor test expectations. r=Honza draft
authorJ. Ryan Stinnett <jryans@gmail.com>
Tue, 04 Apr 2017 18:05:58 -0500
changeset 562805 7f8012b128e952a348e76b2ed2946a729a0357e4
parent 562804 425e3a12e70969df91d44ddea72bdc0e96cc551b
child 624316 7e1c73a9477f1ab934fa714b576396e485db75df
push id54118
push userbmo:jryans@gmail.com
push dateFri, 14 Apr 2017 09:13:53 +0000
reviewersHonza
bugs1346854
milestone55.0a1
Bug 1346854 - Adjust network monitor test expectations. r=Honza After the previous commit, we're now starting network listening at a different point in toolbox startup. We need to rework some test expectations to account for the new timing. MozReview-Commit-ID: 1lNDE51uVPS
devtools/client/netmonitor/src/netmonitor-controller.js
devtools/client/netmonitor/test/browser_net_autoscroll.js
devtools/client/netmonitor/test/browser_net_cached-status.js
devtools/client/netmonitor/test/browser_net_leak_on_tab_close.js
devtools/client/netmonitor/test/browser_net_reload-markers.js
devtools/client/netmonitor/test/browser_net_security-error.js
devtools/client/netmonitor/test/browser_net_service-worker-status.js
devtools/client/netmonitor/test/head.js
--- a/devtools/client/netmonitor/src/netmonitor-controller.js
+++ b/devtools/client/netmonitor/src/netmonitor-controller.js
@@ -374,18 +374,18 @@ NetworkEventsHandler.prototype = {
     }
   },
 
   /**
    * The "DOMContentLoaded" and "Load" events sent by the timeline actor.
    * @param object marker
    */
   _onDocLoadingMarker: function (marker) {
+    this.actions.addTimingMarker(marker);
     window.emit(EVENTS.TIMELINE_EVENT, marker);
-    this.actions.addTimingMarker(marker);
   },
 
   /**
    * The "networkEvent" message type handler.
    *
    * @param string type
    *        Message type.
    * @param object networkInfo
--- a/devtools/client/netmonitor/test/browser_net_autoscroll.js
+++ b/devtools/client/netmonitor/test/browser_net_autoscroll.js
@@ -2,19 +2,19 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
  * Bug 863102 - Automatically scroll down upon new network requests.
  */
 add_task(function* () {
-  requestLongerTimeout(2);
+  requestLongerTimeout(4);
 
-  let { monitor } = yield initNetMonitor(INFINITE_GET_URL);
+  let { monitor } = yield initNetMonitor(INFINITE_GET_URL, true);
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   // Wait until the first request makes the empty notice disappear
   yield waitForRequestListToAppear();
 
   let requestsContainer = document.querySelector(".requests-list-contents");
   ok(requestsContainer, "Container element exists as expected.");
--- a/devtools/client/netmonitor/test/browser_net_cached-status.js
+++ b/devtools/client/netmonitor/test/browser_net_cached-status.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if cached requests have the correct status code
  */
 
 add_task(function* () {
-  let { tab, monitor } = yield initNetMonitor(STATUS_CODES_URL, null, true);
+  let { tab, monitor } = yield initNetMonitor(STATUS_CODES_URL, true);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
--- a/devtools/client/netmonitor/test/browser_net_leak_on_tab_close.js
+++ b/devtools/client/netmonitor/test/browser_net_leak_on_tab_close.js
@@ -6,12 +6,12 @@
 /**
  * Tests that netmonitor doesn't leak windows on parent-side pages (bug 1285638)
  */
 
 add_task(function* () {
   // Tell initNetMonitor to enable cache. Otherwise it will assert that there were more
   // than zero network requests during the page load. But when loading about:config,
   // there are none.
-  let { monitor } = yield initNetMonitor("about:config", null, true);
+  let { monitor } = yield initNetMonitor("about:config", true);
   ok(monitor, "The network monitor was opened");
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_reload-markers.js
+++ b/devtools/client/netmonitor/test/browser_net_reload-markers.js
@@ -7,27 +7,24 @@
  * Tests if the empty-requests reload button works.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document } = monitor.panelWin;
+
+  let markersDone = waitForTimelineMarkers(monitor);
+
   let button = document.querySelector(".requests-list-reload-notice-button");
   button.click();
 
-  let markers = [];
-
-  monitor.panelWin.on(EVENTS.TIMELINE_EVENT, (_, marker) => {
-    markers.push(marker);
-  });
-
   yield waitForNetworkEvents(monitor, 1);
-  yield waitUntil(() => markers.length == 2);
+  let markers = yield markersDone;
 
   ok(true, "Reloading finished");
 
   is(markers[0].name, "document::DOMContentLoaded",
     "The first received marker is correct.");
   is(markers[1].name, "document::Load",
     "The second received marker is correct.");
 
--- a/devtools/client/netmonitor/test/browser_net_security-error.js
+++ b/devtools/client/netmonitor/test/browser_net_security-error.js
@@ -12,28 +12,28 @@ add_task(function* () {
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
 
   gStore.dispatch(Actions.batchEnable(false));
 
   info("Requesting a resource that has a certificate problem.");
 
-  let wait = waitForSecurityBrokenNetworkEvent();
+  let requestsDone = waitForSecurityBrokenNetworkEvent();
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests(1, "https://nocert.example.com");
   });
-  yield wait;
+  yield requestsDone;
 
-  wait = waitForDOM(document, "#security-panel");
+  let securityInfoLoaded = waitForDOM(document, ".security-info-value");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#security-tab"));
-  yield wait;
+  yield securityInfoLoaded;
 
   let errormsg = document.querySelector(".security-info-value");
   isnot(errormsg.textContent, "", "Error message is not empty.");
 
   return teardown(monitor);
 
   /**
    * Returns a promise that's resolved once a request with security issues is
--- a/devtools/client/netmonitor/test/browser_net_service-worker-status.js
+++ b/devtools/client/netmonitor/test/browser_net_service-worker-status.js
@@ -8,17 +8,17 @@
  */
 
 // Service workers only work on https
 const URL = EXAMPLE_URL.replace("http:", "https:");
 
 const TEST_URL = URL + "service-workers/status-codes.html";
 
 add_task(function* () {
-  let { tab, monitor } = yield initNetMonitor(TEST_URL, null, true);
+  let { tab, monitor } = yield initNetMonitor(TEST_URL, true);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -115,43 +115,133 @@ function toggleCache(target, disabled) {
   let navigationFinished = waitForNavigation(target);
 
   // Disable the cache for any toolbox that it is opened from this point on.
   Services.prefs.setBoolPref("devtools.cache.disabled", disabled);
 
   return reconfigureTab(target, options).then(() => navigationFinished);
 }
 
-function initNetMonitor(url, window, enableCache) {
+/**
+ * Wait for 2 markers during document load.
+ */
+function waitForTimelineMarkers(monitor) {
+  return new Promise(resolve => {
+    let markers = [];
+
+    function handleTimelineEvent(_, marker) {
+      info(`Got marker: ${marker.name}`);
+      markers.push(marker);
+      if (markers.length == 2) {
+        monitor.panelWin.off(EVENTS.TIMELINE_EVENT, handleTimelineEvent);
+        info("Got two timeline markers, done waiting");
+        resolve(markers);
+      }
+    }
+
+    monitor.panelWin.on(EVENTS.TIMELINE_EVENT, handleTimelineEvent);
+  });
+}
+
+/**
+ * Start monitoring all incoming update events about network requests and wait until
+ * a complete info about all requests is received. (We wait for the timings info
+ * explicitly, because that's always the last piece of information that is received.)
+ *
+ * This method is designed to wait for network requests that are issued during a page
+ * load, when retrieving page resources (scripts, styles, images). It has certain
+ * assumptions that can make it unsuitable for other types of network communication:
+ * - it waits for at least one network request to start and finish before returning
+ * - it waits only for request that were issued after it was called. Requests that are
+ *   already in mid-flight will be ignored.
+ * - the request start and end times are overlapping. If a new request starts a moment
+ *   after the previous one was finished, the wait will be ended in the "interim"
+ *   period.
+ * @returns a promise that resolves when the wait is done.
+ */
+function waitForAllRequestsFinished(monitor) {
+  let window = monitor.panelWin;
+  let { windowRequire } = window;
+  let { NetMonitorController } =
+    windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
+
+  return new Promise(resolve => {
+    // Key is the request id, value is a boolean - is request finished or not?
+    let requests = new Map();
+
+    function onRequest(_, id) {
+      let networkInfo = NetMonitorController.webConsoleClient.getNetworkRequest(id);
+      let { url } = networkInfo.request;
+      info(`Request ${id} for ${url} not yet done, keep waiting...`);
+      requests.set(id, false);
+    }
+
+    function onTimings(_, id) {
+      let networkInfo = NetMonitorController.webConsoleClient.getNetworkRequest(id);
+      let { url } = networkInfo.request;
+      info(`Request ${id} for ${url} done`);
+      requests.set(id, true);
+      maybeResolve();
+    }
+
+    function maybeResolve() {
+      // Have all the requests in the map finished yet?
+      if (![...requests.values()].every(finished => finished)) {
+        return;
+      }
+
+      // All requests are done - unsubscribe from events and resolve!
+      window.off(EVENTS.NETWORK_EVENT, onRequest);
+      window.off(EVENTS.RECEIVED_EVENT_TIMINGS, onTimings);
+      info("All requests finished");
+      resolve();
+    }
+
+    window.on(EVENTS.NETWORK_EVENT, onRequest);
+    window.on(EVENTS.RECEIVED_EVENT_TIMINGS, onTimings);
+  });
+}
+
+function initNetMonitor(url, enableCache) {
   info("Initializing a network monitor pane.");
 
   return Task.spawn(function* () {
     let tab = yield addTab(url);
     info("Net tab added successfully: " + url);
 
     let target = TargetFactory.forTab(tab);
 
     yield target.makeRemote();
     info("Target remoted.");
 
+    let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
+    info("Network monitor pane shown successfully.");
+
+    let monitor = toolbox.getCurrentPanel();
+
     if (!enableCache) {
+      let panel = monitor.panelWin;
+      let { gStore, windowRequire } = panel;
       info("Disabling cache and reloading page.");
+      let requestsDone = waitForAllRequestsFinished(monitor);
+      let markersDone = waitForTimelineMarkers(monitor);
       yield toggleCache(target, true);
+      yield Promise.all([requestsDone, markersDone]);
       info("Cache disabled when the current and all future toolboxes are open.");
       // Remove any requests generated by the reload while toggling the cache to
       // avoid interfering with the test.
       isnot([...target.activeConsole.getNetworkEvents()].length, 0,
          "Request to reconfigure the tab was recorded.");
+      info("Clearing requests in the console client.");
       target.activeConsole.clearNetworkRequests();
+      info("Clearing requests in the UI.");
+      let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+      gStore.dispatch(Actions.clearRequests());
     }
 
-    let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
-    info("Network monitor pane shown successfully.");
-
-    let monitor = toolbox.getCurrentPanel();
     return {tab, monitor};
   });
 }
 
 function restartNetMonitor(monitor, newUrl) {
   info("Restarting the specified network monitor.");
 
   return Task.spawn(function* () {
@@ -216,43 +306,54 @@ function waitForNetworkEvents(monitor, g
     }
 
     function updateProgressForURL(url, event) {
       initProgressForURL(url);
       progress[url][Object.keys(EVENTS).find(e => EVENTS[e] == event)] = 1;
     }
 
     function onGenericEvent(event, actor) {
+      let networkInfo = NetMonitorController.webConsoleClient.getNetworkRequest(actor);
+      if (!networkInfo) {
+        // Must have been related to reloading document to disable cache.
+        // Ignore the event.
+        return;
+      }
       genericEvents++;
-      maybeResolve(event, actor);
+      maybeResolve(event, actor, networkInfo);
     }
 
     function onPostEvent(event, actor) {
+      let networkInfo = NetMonitorController.webConsoleClient.getNetworkRequest(actor);
+      if (!networkInfo) {
+        // Must have been related to reloading document to disable cache.
+        // Ignore the event.
+        return;
+      }
       postEvents++;
-      maybeResolve(event, actor);
+      maybeResolve(event, actor, networkInfo);
     }
 
-    function maybeResolve(event, actor) {
+    function maybeResolve(event, actor, networkInfo) {
       info("> Network events progress: " +
         genericEvents + "/" + ((getRequests + postRequests) * 13) + ", " +
         postEvents + "/" + (postRequests * 2) + ", " +
         "got " + event + " for " + actor);
 
-      let networkInfo = NetMonitorController.webConsoleClient.getNetworkRequest(actor);
       let url = networkInfo.request.url;
       updateProgressForURL(url, event);
 
       // Uncomment this to get a detailed progress logging (when debugging a test)
       // info("> Current state: " + JSON.stringify(progress, null, 2));
 
       // There are 15 updates which need to be fired for a request to be
       // considered finished. The "requestPostData" packet isn't fired for
       // non-POST requests.
       if (genericEvents >= (getRequests + postRequests) * 13 &&
-          postEvents >= postRequests * 2) {
+        postEvents >= postRequests * 2) {
         awaitedEventsToListeners.forEach(([e, l]) => panel.off(EVENTS[e], l));
         executeSoon(resolve);
       }
     }
 
     awaitedEventsToListeners.forEach(([e, l]) => panel.on(EVENTS[e], l));
   });
 }