Bug 1576318 Part 5 - Add tests that we can preview assorted objects while replaying, and pause the debugger without making debugger requests, r=loganfsmyth.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 30 Aug 2019 17:13:34 +0000
changeset 551413 ed6ce702ad2a72b94cf35e96d2567400cb31156a
parent 551412 aa75f0814cbe0364bfd545cea8be68e226cb09e0
child 551414 653ffc92a954626c649bac11255c41223316fd79
push id11865
push userbtara@mozilla.com
push dateMon, 02 Sep 2019 08:54:37 +0000
treeherdermozilla-beta@37f59c4671b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersloganfsmyth
bugs1576318
milestone70.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 1576318 Part 5 - Add tests that we can preview assorted objects while replaying, and pause the debugger without making debugger requests, r=loganfsmyth. Depends on D43321 Differential Revision: https://phabricator.services.mozilla.com/D43322
devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-03.js
devtools/client/webreplay/mochitest/examples/doc_rr_objects.html
devtools/client/webreplay/mochitest/head.js
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-03.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-03.js
@@ -14,36 +14,123 @@ const BrowserTest = {
   BrowserTestUtils,
 };
 
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/head.js",
   BrowserTest
 );
 
+async function checkMessageObjectContents(msg, expected, expandList = []) {
+  const oi = msg.querySelector(".tree");
+  const node = oi.querySelector(".tree-node");
+  BrowserTest.expandObjectInspectorNode(node);
+
+  for (const label of expandList) {
+    const labelNode = await waitFor(() =>
+      BrowserTest.findObjectInspectorNode(oi, label)
+    );
+    BrowserTest.expandObjectInspectorNode(labelNode);
+  }
+
+  const properties = await waitFor(() => {
+    const nodes = BrowserTest.getObjectInspectorNodes(oi);
+    if (nodes && nodes.length > 1) {
+      return [...nodes].map(n => n.textContent);
+    }
+    return null;
+  });
+
+  expected.forEach(s => {
+    ok(properties.find(v => v.includes(s)), `Object contents include "${s}"`);
+  });
+}
+
 // Test evaluating various expressions in the console after time warping.
 add_task(async function() {
-  const dbg = await attachRecordingDebugger("doc_rr_error.html", {
+  const dbg = await attachRecordingDebugger("doc_rr_objects.html", {
     waitForRecording: true,
   });
 
-  const { toolbox } = dbg;
+  const { threadFront, toolbox } = dbg;
   const console = await toolbox.selectTool("webconsole");
   const hud = console.hud;
 
-  await warpToMessage(hud, dbg, "Number 5");
+  await warpToMessage(hud, dbg, "Done");
+
+  const requests = await threadFront.debuggerRequests();
+
+  requests.forEach(({ request, stack }) => {
+    if (request.type != "pauseData") {
+      dump(`Unexpected debugger request stack:\n${stack}\n`);
+      ok(false, `Unexpected debugger request while paused: ${request.type}`);
+    }
+  });
 
   BrowserTest.execute(hud, "Error('helo')");
-  await waitFor(() => findMessage(hud, "helo"));
+  await waitForMessage(hud, "helo");
 
   BrowserTest.execute(
     hud,
     `
 function f() {
   throw Error("there");
 }
 f();
 `
   );
   await BrowserTest.checkMessageStack(hud, "Error: there", [3, 5]);
 
+  let msg;
+
+  BrowserTest.execute(hud, "Array(1, 2, 3)");
+  msg = await waitForMessage(hud, "Array(3) [ 1, 2, 3 ]");
+  await checkMessageObjectContents(msg, ["0: 1", "1: 2", "2: 3", "length: 3"]);
+
+  BrowserTest.execute(hud, "new Uint8Array([1, 2, 3, 4])");
+  msg = await waitForMessage(hud, "Uint8Array(4) [ 1, 2, 3, 4 ]");
+  await checkMessageObjectContents(msg, [
+    "0: 1",
+    "1: 2",
+    "2: 3",
+    "3: 4",
+    "byteLength: 4",
+    "byteOffset: 0",
+  ]);
+
+  BrowserTest.execute(hud, `RegExp("abc", "g")`);
+  msg = await waitForMessage(hud, "/abc/g");
+  await checkMessageObjectContents(msg, ["global: true", `source: "abc"`]);
+
+  BrowserTest.execute(hud, "new Set([1, 2, 3])");
+  msg = await waitForMessage(hud, "Set(3) [ 1, 2, 3 ]");
+  await checkMessageObjectContents(
+    msg,
+    ["0: 1", "1: 2", "2: 3", "size: 3"],
+    ["<entries>"]
+  );
+
+  BrowserTest.execute(hud, "new Map([[1, {a:1}], [2, {b:2}]])");
+  msg = await waitForMessage(hud, "Map { 1 → {…}, 2 → {…} }");
+  await checkMessageObjectContents(
+    msg,
+    ["0: 1 → Object { … }", "1: 2 → Object { … }", "size: 2"],
+    ["<entries>"]
+  );
+
+  BrowserTest.execute(hud, "new WeakSet([{a:1}, {b:2}])");
+  msg = await waitForMessage(hud, "WeakSet [ {…}, {…} ]");
+  await checkMessageObjectContents(
+    msg,
+    ["0: Object { … }", "1: Object { … }"],
+    ["<entries>"]
+  );
+
+  BrowserTest.execute(hud, "new WeakMap([[{a:1},{b:1}], [{a:2},{b:2}]])");
+  msg = await waitForMessage(hud, "WeakMap { {…} → {…}, {…} → {…} }");
+  await checkMessageObjectContents(
+    msg,
+    ["0: Object { … } → Object { … }", "1: Object { … } → Object { … }"],
+    ["<entries>"]
+  );
+
   await shutdownDebugger(dbg);
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/examples/doc_rr_objects.html
@@ -0,0 +1,38 @@
+<div id="foo">BAR</div>
+<script>
+const cpmm = SpecialPowers.Services.cpmm;
+function recordingFinished() {
+  cpmm.sendAsyncMessage("RecordingFinished");
+}
+
+function foo() {
+// Create various objects which the debugger must be able to show in the scopes
+// pane using only the pause data, i.e. without additional debugger requests.
+// Not performing debugger requests allows the debugger to finish updating the
+// UI using cached pause data, and without any replaying process actually being
+// at the point where we are pausing.
+var a = Array();
+var b = new Uint8Array(20);
+var c = new Set([{a:0},{b:1}]);
+var d = new Map([[{a:0},{b:1}]]);
+var e = new WeakSet();
+var f = new WeakMap();
+var g = { a:0 };
+for (let i = 0; i < 20; i++) {
+  a.push(i);
+  b[i] = i;
+  c.add(i);
+  d.set(i, i + 1);
+  e.add({ i });
+  f.set({ i }, { j: i + 1 });
+  g[`a${i}`] = i;
+}
+var h = /abc/gi;
+var i = new Date();
+var j = RangeError();
+var k = document.getElementById("foo");
+console.log("Done");
+window.setTimeout(recordingFinished);
+}
+foo();
+</script>
--- a/devtools/client/webreplay/mochitest/head.js
+++ b/devtools/client/webreplay/mochitest/head.js
@@ -106,32 +106,37 @@ function newRecordingFile() {
   const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
   return OS.Path.join(
     OS.Constants.Path.tmpDir,
     "MochitestRecording" + Math.round(Math.random() * 1000000000)
   );
 }
 
 function findMessage(hud, text, selector = ".message") {
-  return findMessages(hud, text, selector)[0];
+  const messages = findMessages(hud, text, selector);
+  return messages ? messages[0] : null;
 }
 
 function findMessages(hud, text, selector = ".message") {
   const messages = hud.ui.outputNode.querySelectorAll(selector);
   const elements = Array.prototype.filter.call(messages, el =>
     el.textContent.includes(text)
   );
 
   if (elements.length == 0) {
     return null;
   }
 
   return elements;
 }
 
+function waitForMessage(hud, text, selector = ".message") {
+  return waitUntilPredicate(() => findMessage(hud, text, selector));
+}
+
 function waitForMessages(hud, text, selector = ".message") {
   return waitUntilPredicate(() => findMessages(hud, text, selector));
 }
 
 async function waitForMessageCount(hud, text, length, selector = ".message") {
   let messages;
   await waitUntil(() => {
     messages = findMessages(hud, text, selector);