Bug 1392408 Part 6 - Add test for capturing worker stacks in net monitor, r=ochameau.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 16 May 2019 08:09:31 -1000
changeset 533133 5168e91ed5b4da5b8c9c9a05cfc7f250a6de47af
parent 533132 8a7327b91ed243944681e5a82c1da2db7023d297
child 533134 2e2f3fc37e2eca6e5451c04d395ccf435c3a067c
push id11276
push userrgurzau@mozilla.com
push dateMon, 20 May 2019 13:11:24 +0000
treeherdermozilla-beta@847755a7c325 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau
bugs1392408
milestone68.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 1392408 Part 6 - Add test for capturing worker stacks in net monitor, r=ochameau.
devtools/client/netmonitor/test/browser.ini
devtools/client/netmonitor/test/browser_net_cause.js
devtools/client/netmonitor/test/browser_net_worker_stacks.js
devtools/client/netmonitor/test/head.js
devtools/client/netmonitor/test/html_worker-test-page.html
devtools/client/netmonitor/test/js_worker-test.js
devtools/client/netmonitor/test/js_worker-test2.js
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -39,16 +39,19 @@ support-files =
   html_sorting-test-page.html
   html_statistics-test-page.html
   html_status-codes-test-page.html
   html_tracking-protection.html
   html_api-calls-test-page.html
   html_copy-as-curl.html
   html_curl-utils.html
   html_open-request-in-tab.html
+  html_worker-test-page.html
+  js_worker-test.js
+  js_worker-test2.js
   sjs_content-type-test-server.sjs
   sjs_cors-test-server.sjs
   sjs_https-redirect-test-server.sjs
   sjs_hsts-test-server.sjs
   sjs_json-test-server.sjs
   sjs_method-test-server.sjs
   sjs_set-cookie-same-site.sjs
   sjs_simple-test-server.sjs
@@ -216,8 +219,9 @@ skip-if = true # Bug 1373558
 [browser_net_timeline_ticks.js]
 skip-if = true # TODO: fix the test
 [browser_net_timing-division.js]
 [browser_net_tracking-resources.js]
 [browser_net_truncate-post-data.js]
 [browser_net_truncate.js]
 [browser_net_view-source-debugger.js]
 [browser_net_waterfall-click.js]
+[browser_net_worker_stacks.js]
--- a/devtools/client/netmonitor/test/browser_net_cause.js
+++ b/devtools/client/netmonitor/test/browser_net_cause.js
@@ -88,71 +88,33 @@ add_task(async function() {
   // all the requests the page is making, not only the XHRs.
   // We can't use about:blank here, because initNetMonitor checks that the
   // page has actually made at least one request.
   const { tab, monitor } = await initNetMonitor(SIMPLE_URL);
 
   const { document, store, windowRequire, connector } = monitor.panelWin;
   const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   const {
-    getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   const wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
   BrowserTestUtils.loadURI(tab.linkedBrowser, CAUSE_URL);
   await wait;
 
   const requests = getSortedRequests(store.getState());
   await Promise.all(requests.map(requestItem =>
     connector.requestData(requestItem.id, "stackTrace")));
 
   is(store.getState().requests.requests.size, EXPECTED_REQUESTS.length,
     "All the page events should be recorded.");
 
-  EXPECTED_REQUESTS.forEach((spec, i) => {
-    const { method, url, causeType, causeUri, stack } = spec;
-
-    const requestItem = getSortedRequests(store.getState()).get(i);
-    verifyRequestItemTarget(
-      document,
-      getDisplayedRequests(store.getState()),
-      requestItem,
-      method,
-      url,
-      { cause: { type: causeType, loadingDocumentUri: causeUri } }
-    );
-
-    const stacktrace = requestItem.stacktrace;
-    const stackLen = stacktrace ? stacktrace.length : 0;
-
-    if (stack) {
-      ok(stacktrace, `Request #${i} has a stacktrace`);
-      ok(stackLen > 0,
-        `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
-
-      // if "stack" is array, check the details about the top stack frames
-      if (Array.isArray(stack)) {
-        stack.forEach((frame, j) => {
-          is(stacktrace[j].functionName, frame.fn,
-            `Request #${i} has the correct function on JS stack frame #${j}`);
-          is(stacktrace[j].filename.split("/").pop(), frame.file,
-            `Request #${i} has the correct file on JS stack frame #${j}`);
-          is(stacktrace[j].lineNumber, frame.line,
-            `Request #${i} has the correct line number on JS stack frame #${j}`);
-          is(stacktrace[j].asyncCause, frame.asyncCause,
-            `Request #${i} has the correct async cause on JS stack frame #${j}`);
-        });
-      }
-    } else {
-      is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`);
-    }
-  });
+  validateRequests(EXPECTED_REQUESTS, monitor);
 
   // Sort the requests by cause and check the order
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-cause-button"));
   const expectedOrder = EXPECTED_REQUESTS.map(r => r.causeType).sort();
   expectedOrder.forEach((expectedCause, i) => {
     const cause = getSortedRequests(store.getState()).get(i).cause.type;
     is(cause, expectedCause, `The request #${i} has the expected cause after sorting`);
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_worker_stacks.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that we get stack traces for the network requests made when starting or
+// running worker threads.
+
+const TOP_FILE_NAME = "html_worker-test-page.html";
+const TOP_URL = EXAMPLE_URL + TOP_FILE_NAME;
+const WORKER_FILE_NAME = "js_worker-test.js";
+const WORKER_URL = EXAMPLE_URL + WORKER_FILE_NAME;
+
+const EXPECTED_REQUESTS = [
+  {
+    method: "GET",
+    url: TOP_URL,
+    causeType: "document",
+    causeUri: null,
+    stack: true,
+  },
+  {
+    method: "GET",
+    url: WORKER_URL,
+    causeType: "script",
+    causeUri: TOP_URL,
+    stack: [
+      { fn: "startWorkerInner", file: TOP_FILE_NAME, line: 11 },
+      { fn: "startWorker", file: TOP_FILE_NAME, line: 8 },
+      { file: TOP_FILE_NAME, line: 4 },
+    ],
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "missing1.js",
+    causeType: "script",
+    causeUri: TOP_URL,
+    stack: [
+      { fn: "importScriptsFromWorker", file: WORKER_FILE_NAME, line: 14 },
+      { file: WORKER_FILE_NAME, line: 10 },
+    ],
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "missing2.js",
+    causeType: "script",
+    causeUri: TOP_URL,
+    stack: [
+      { fn: "importScriptsFromWorker", file: WORKER_FILE_NAME, line: 14 },
+      { file: WORKER_FILE_NAME, line: 10 },
+    ],
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "js_worker-test2.js",
+    causeType: "script",
+    causeUri: TOP_URL,
+    stack: [
+      { fn: "startWorkerFromWorker", file: WORKER_FILE_NAME, line: 7 },
+      { file: WORKER_FILE_NAME, line: 3 },
+    ],
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "missing.json",
+    causeType: "xhr",
+    causeUri: TOP_URL,
+    stack: [
+      { fn: "createJSONRequest", file: WORKER_FILE_NAME, line: 22 },
+      { file: WORKER_FILE_NAME, line: 18 },
+    ],
+  },
+];
+
+add_task(async function() {
+  // Load a different URL first to instantiate the network monitor before we
+  // load the page we're really interested in.
+  const { tab, monitor } = await initNetMonitor(SIMPLE_URL);
+
+  const { store, windowRequire, connector } = monitor.panelWin;
+  const {
+    getSortedRequests,
+  } = windowRequire("devtools/client/netmonitor/src/selectors/index");
+
+  BrowserTestUtils.loadURI(tab.linkedBrowser, TOP_URL);
+
+  await waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
+
+  is(store.getState().requests.requests.size, EXPECTED_REQUESTS.length,
+    "All the page events should be recorded.");
+
+  // Wait for stack traces from all requests.
+  const requests = getSortedRequests(store.getState());
+  await Promise.all(requests.map(requestItem =>
+    connector.requestData(requestItem.id, "stackTrace")));
+
+  validateRequests(EXPECTED_REQUESTS, monitor);
+
+  await teardown(monitor);
+});
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -864,16 +864,63 @@ function queryTelemetryEvents(query) {
     event[2] === query.method &&
     event[3] === object
   );
 
   // Return the `extra` field (which is event[5]e).
   return filtersChangedEvents.map(event => event[5]);
 }
 
+function validateRequests(requests, monitor) {
+  const { document, store, windowRequire } = monitor.panelWin;
+
+  const {
+    getDisplayedRequests,
+  } = windowRequire("devtools/client/netmonitor/src/selectors/index");
+
+  requests.forEach((spec, i) => {
+    const { method, url, causeType, causeUri, stack } = spec;
+
+    const requestItem = getSortedRequests(store.getState()).get(i);
+    verifyRequestItemTarget(
+      document,
+      getDisplayedRequests(store.getState()),
+      requestItem,
+      method,
+      url,
+      { cause: { type: causeType, loadingDocumentUri: causeUri } }
+    );
+
+    const stacktrace = requestItem.stacktrace;
+    const stackLen = stacktrace ? stacktrace.length : 0;
+
+    if (stack) {
+      ok(stacktrace, `Request #${i} has a stacktrace`);
+      ok(stackLen > 0,
+        `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
+
+      // if "stack" is array, check the details about the top stack frames
+      if (Array.isArray(stack)) {
+        stack.forEach((frame, j) => {
+          is(stacktrace[j].functionName, frame.fn,
+            `Request #${i} has the correct function on JS stack frame #${j}`);
+          is(stacktrace[j].filename.split("/").pop(), frame.file,
+            `Request #${i} has the correct file on JS stack frame #${j}`);
+          is(stacktrace[j].lineNumber, frame.line,
+            `Request #${i} has the correct line number on JS stack frame #${j}`);
+          is(stacktrace[j].asyncCause, frame.asyncCause,
+            `Request #${i} has the correct async cause on JS stack frame #${j}`);
+        });
+      }
+    } else {
+      is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`);
+    }
+  });
+}
+
 /**
  * Retrieve the context menu element corresponding to the provided id, for the provided
  * netmonitor instance.
  */
 function getContextMenuItem(monitor, id) {
   const Menu = require("devtools/client/framework/menu");
   return Menu.getMenuElementById(id, monitor.panelWin.document);
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/html_worker-test-page.html
@@ -0,0 +1,13 @@
+<script>
+/* eslint-disable no-unused-vars */
+"use strict";
+startWorker();
+
+var w;
+function startWorker() {
+  startWorkerInner();
+}
+function startWorkerInner() {
+  w = new Worker("js_worker-test.js");
+}
+</script>
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/js_worker-test.js
@@ -0,0 +1,24 @@
+/* eslint-disable no-unused-vars, no-undef */
+"use strict";
+startWorkerFromWorker();
+
+var w;
+function startWorkerFromWorker() {
+  w = new Worker("js_worker-test2.js");
+}
+
+importScriptsFromWorker();
+
+function importScriptsFromWorker() {
+  try {
+    importScripts("missing1.js", "missing2.js");
+  } catch (e) {}
+}
+
+createJSONRequest();
+
+function createJSONRequest() {
+  const request = new XMLHttpRequest();
+  request.open("GET", "missing.json", true);
+  request.send(null);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/js_worker-test2.js
@@ -0,0 +1,3 @@
+"use strict";
+
+console.log("I AM A WORKER");