Bug 1517837 - Move web replay tests to their own directory, r=lsmyth.

--HG--
extra : rebase_source : f26c786e9f787a57faed62f54c59178a5067065f
This commit is contained in:
Brian Hackett 2019-01-15 12:26:20 -10:00
parent 491c3a9d73
commit 1290999d83
30 changed files with 442 additions and 411 deletions

View File

@ -106,6 +106,7 @@ devtools/client/shared/webpack/shims/test/test_clipboard.html
devtools/shared/qrcode/tests/mochitest/test_decode.html
devtools/shared/tests/mochitest/*.html
devtools/shared/webconsole/test/test_*.html
devtools/client/webreplay/mochitest/examples/*.html
# Soon to be removed, the new/ directory is explicitly excluded below due to
# also being an imported repository.

View File

@ -656,11 +656,6 @@ support-files =
examples/simple-worker.js
examples/doc-event-handler.html
examples/doc-eval-throw.html
examples/doc_rr_basic.html
examples/doc_rr_continuous.html
examples/doc_rr_logs.html
examples/doc_rr_recovery.html
examples/doc_rr_error.html
[browser_dbg-asm.js]
[browser_dbg-async-stepping.js]
@ -775,36 +770,3 @@ skip-if = true
[browser_dbg-windowless-workers.js]
[browser_dbg-event-handler.js]
[browser_dbg-eval-throw.js]
[browser_dbg_rr_breakpoints-01.js]
skip-if = true # bug 1513057 os != "mac" || debug || !nightly_build
[browser_dbg_rr_breakpoints-02.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_breakpoints-03.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_breakpoints-04.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_breakpoints-05.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_record.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_stepping-01.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_stepping-02.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_stepping-03.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_stepping-04.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_recovery-01.js]
skip-if = true # See bug 1481009
[browser_dbg_rr_replay-01.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_replay-02.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_replay-03.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_console_warp-01.js]
skip-if = true # os != "mac" || debug || !nightly_build
[browser_dbg_rr_console_warp-02.js]
skip-if = true # os != "mac" || debug || !nightly_build

View File

@ -5,7 +5,7 @@ add_task(async function() {
await selectSource(dbg, "switching-01");
// open the console
await getSplitConsole(dbg);
await getDebuggerSplitConsole(dbg);
ok(dbg.toolbox.splitConsole, "Split console is shown.");
// close the console

View File

@ -1,48 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// This functionality was copied from devtools/client/webconsole/test/mochitest/head.js,
// since this test straddles both the web console and the debugger. I couldn't
// figure out how to load that script directly here.
function waitForThreadEvents(threadClient, eventName) {
info(`Waiting for thread event '${eventName}' to fire.`);
return new Promise(function(resolve, reject) {
threadClient.addListener(eventName, function onEvent(eventName, ...args) {
info(`Thread event '${eventName}' fired.`);
threadClient.removeListener(eventName, onEvent);
resolve.apply(resolve, args);
});
});
}
// Test basic console time warping functionality in web replay.
async function test() {
waitForExplicitFinish();
const dbg = await attachRecordingDebugger(
"doc_rr_logs.html",
{ waitForRecording: true }
);
const {tab, toolbox, threadClient} = dbg;
const console = await getSplitConsole(dbg);
const hud = console.hud;
let message = await warpToMessage(hud, threadClient, "number: 1");
ok(!message.classList.contains("paused-before"), "paused before message is not shown");
await stepOverToLine(threadClient, 18);
await reverseStepOverToLine(threadClient, 17);
message = findMessage(hud, "number: 1")
ok(message.classList.contains("paused-before"), "paused before message is shown");
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}

View File

@ -47,16 +47,6 @@ const EXAMPLE_URL =
"http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/";
async function waitUntilPredicate(predicate) {
let result;
await waitUntil(() => {
result = predicate();
return result;
})
return result;
}
// NOTE: still experimental, the screenshots might not be exactly correct
async function takeScreenshot(dbg) {
let canvas = dbg.win.document.createElementNS(
@ -70,193 +60,3 @@ async function takeScreenshot(dbg) {
await waitForTime(1000);
dump(`[SCREENSHOT] ${canvas.toDataURL()}\n`);
}
// Attach a debugger to a tab, returning a promise that resolves with the
// debugger's toolbox.
async function attachDebugger(tab) {
let target = await TargetFactory.forTab(tab);
let toolbox = await gDevTools.showToolbox(target, "jsdebugger");
return toolbox;
}
async function attachRecordingDebugger(url, { waitForRecording } = { waitForRecording: false }) {
let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = tab;
openTrustedLinkIn(EXAMPLE_URL + url, "current");
if (waitForRecording) {
await once(Services.ppmm, "RecordingFinished");
}
const toolbox = await attachDebugger(tab);
const dbg = createDebuggerContext(toolbox)
const threadClient = dbg.toolbox.threadClient;
await threadClient.interrupt();
return {...dbg, tab, threadClient};
}
// Return a promise with a reference to jsterm, opening the split
// console if necessary. This cleans up the split console pref so
// it won't pollute other tests.
async function getSplitConsole(dbg) {
const { toolbox, win } = dbg;
if (!win) {
win = toolbox.win;
}
if (!toolbox.splitConsole) {
pressKey(dbg, "Escape");
}
await toolbox.openSplitConsole();
return toolbox.getPanel("webconsole");
}
// Return a promise that resolves when a breakpoint has been set.
async function setBreakpoint(threadClient, expectedFile, lineno) {
let {sources} = await threadClient.getSources();
ok(sources.length == 1, "Got one source");
ok(RegExp(expectedFile).test(sources[0].url), "Source is " + expectedFile);
let sourceClient = threadClient.source(sources[0]);
await sourceClient.setBreakpoint({ line: lineno });
}
function resumeThenPauseAtLineFunctionFactory(method) {
return async function(threadClient, lineno) {
threadClient[method]();
await threadClient.addOneTimeListener("paused", async function(event, packet) {
let {frames} = await threadClient.getFrames(0, 1);
let frameLine = frames[0] ? frames[0].where.line : undefined;
ok(frameLine == lineno, "Paused at line " + frameLine + " expected " + lineno);
});
};
}
// Define various methods that resume a thread in a specific way and ensure it
// pauses at a specified line.
var rewindToLine = resumeThenPauseAtLineFunctionFactory("rewind");
var resumeToLine = resumeThenPauseAtLineFunctionFactory("resume");
var reverseStepOverToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOver");
var stepOverToLine = resumeThenPauseAtLineFunctionFactory("stepOver");
var reverseStepInToLine = resumeThenPauseAtLineFunctionFactory("reverseStepIn");
var stepInToLine = resumeThenPauseAtLineFunctionFactory("stepIn");
var reverseStepOutToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOut");
var stepOutToLine = resumeThenPauseAtLineFunctionFactory("stepOut");
// Return a promise that resolves with the result of a thread evaluating a
// string in the topmost frame.
async function evaluateInTopFrame(threadClient, text) {
let {frames} = await threadClient.getFrames(0, 1);
ok(frames.length == 1, "Got one frame");
let response = await threadClient.eval(frames[0].actor, text);
ok(response.type == "resumed", "Got resume response from eval");
let rval;
await threadClient.addOneTimeListener("paused", function(event, packet) {
ok(packet.type == "paused" &&
packet.why.type == "clientEvaluated" &&
"return" in packet.why.frameFinished, "Eval returned a value");
rval = packet.why.frameFinished["return"];
});
return (rval.type == "undefined") ? undefined : rval;
}
// Return a promise that resolves when a thread evaluates a string in the
// topmost frame, ensuring the result matches the expected value.
async function checkEvaluateInTopFrame(threadClient, text, expected) {
let rval = await evaluateInTopFrame(threadClient, text);
ok(rval == expected, "Eval returned " + expected);
}
// Return a promise that resolves when a thread evaluates a string in the
// topmost frame, with the result throwing an exception.
async function checkEvaluateInTopFrameThrows(threadClient, text) {
let {frames} = await threadClient.getFrames(0, 1);
ok(frames.length == 1, "Got one frame");
let response = await threadClient.eval(frames[0].actor, text);
ok(response.type == "resumed", "Got resume response from eval");
await threadClient.addOneTimeListener("paused", function(event, packet) {
ok(packet.type == "paused" &&
packet.why.type == "clientEvaluated" &&
"throw" in packet.why.frameFinished, "Eval threw an exception");
});
}
// Return a pathname that can be used for a new recording file.
function newRecordingFile() {
ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
return OS.Path.join(OS.Constants.Path.tmpDir,
"MochitestRecording" + Math.round(Math.random() * 1000000000));
}
async function warpToMessage(hud, threadClient, text) {
let messages = await waitForMessages(hud, text);
ok(messages.length == 1, "Found one message");
let message = messages.pop();
let menuPopup = await openConsoleContextMenu(hud, message);
console.log(`.>> menu`, menuPopup);
let timeWarpItem = menuPopup.querySelector("#console-menu-time-warp");
ok(timeWarpItem, "Time warp menu item is available");
timeWarpItem.click();
await Promise.all([
hideConsoleContextMenu(hud),
once(Services.ppmm, "TimeWarpFinished"),
waitForThreadEvents(threadClient, 'paused')
]);
messages = findMessages(hud, "", ".paused");
ok(messages.length == 1, "Found one paused message");
return message;
}
function findMessage(hud, text, selector = ".message") {
return findMessages(hud, text, selector)[0]
}
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 waitForMessages(hud, text, selector = ".message") {
return waitUntilPredicate(() => findMessages(hud, text, selector))
}
async function openConsoleContextMenu(hud, element) {
const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open");
synthesizeContextMenuEvent(element);
await onConsoleMenuOpened;
const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
return doc.getElementById("webconsole-menu");
}
function hideConsoleContextMenu(hud) {
const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
const popup = doc.getElementById("webconsole-menu");
if (!popup) {
return Promise.resolve();
}
const onPopupHidden = once(popup, "popuphidden");
popup.hidePopup();
return onPopupHidden;
}

View File

@ -1462,3 +1462,75 @@ async function editExpression(dbg, input) {
pressKey(dbg, "Enter");
await evaluated;
}
async function waitUntilPredicate(predicate) {
let result;
await waitUntil(() => {
result = predicate();
return result;
})
return result;
}
// Return a promise with a reference to jsterm, opening the split
// console if necessary. This cleans up the split console pref so
// it won't pollute other tests.
async function getDebuggerSplitConsole(dbg) {
const { toolbox, win } = dbg;
if (!win) {
win = toolbox.win;
}
if (!toolbox.splitConsole) {
pressKey(dbg, "Escape");
}
await toolbox.openSplitConsole();
return toolbox.getPanel("webconsole");
}
async function openConsoleContextMenu(hud, element) {
const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open");
synthesizeContextMenuEvent(element);
await onConsoleMenuOpened;
const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
return doc.getElementById("webconsole-menu");
}
function hideConsoleContextMenu(hud) {
const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
const popup = doc.getElementById("webconsole-menu");
if (!popup) {
return Promise.resolve();
}
const onPopupHidden = once(popup, "popuphidden");
popup.hidePopup();
return onPopupHidden;
}
// Return a promise that resolves with the result of a thread evaluating a
// string in the topmost frame.
async function evaluateInTopFrame(threadClient, text) {
const {frames} = await threadClient.getFrames(0, 1);
ok(frames.length == 1, "Got one frame");
const response = await threadClient.eval(frames[0].actor, text);
ok(response.type == "resumed", "Got resume response from eval");
let rval;
await threadClient.addOneTimeListener("paused", function(event, packet) {
ok(packet.type == "paused" &&
packet.why.type == "clientEvaluated" &&
"return" in packet.why.frameFinished, "Eval returned a value");
rval = packet.why.frameFinished.return;
});
return (rval.type == "undefined") ? undefined : rval;
}
// Return a promise that resolves when a thread evaluates a string in the
// topmost frame, ensuring the result matches the expected value.
async function checkEvaluateInTopFrame(threadClient, text, expected) {
const rval = await evaluateInTopFrame(threadClient, text);
ok(rval == expected, "Eval returned " + expected);
}

View File

@ -0,0 +1,38 @@
[DEFAULT]
tags = devtools
subsuite = devtools
# Feel free to set this to true if an impending change breaks Web Replay and
# fixing it would be annoying or difficult. This will avoid running all tests
# that use Web Replay; we don't want this experimental feature to impede
# development in the rest of Gecko.
#
# Please file a bug against the 'Core > Web Replay' component if you do so,
# so that the problem can be fixed and tests reenabled.
skip-if = os != "mac" || debug || !nightly_build
support-files =
head.js
examples/doc_rr_basic.html
examples/doc_rr_continuous.html
examples/doc_rr_logs.html
examples/doc_rr_recovery.html
examples/doc_rr_error.html
[browser_dbg_rr_breakpoints-01.js]
[browser_dbg_rr_breakpoints-02.js]
[browser_dbg_rr_breakpoints-03.js]
[browser_dbg_rr_breakpoints-04.js]
[browser_dbg_rr_breakpoints-05.js]
[browser_dbg_rr_record.js]
[browser_dbg_rr_stepping-01.js]
[browser_dbg_rr_stepping-02.js]
[browser_dbg_rr_stepping-03.js]
[browser_dbg_rr_stepping-04.js]
[browser_dbg_rr_recovery-01.js]
skip-if = true # See bug 1481009
[browser_dbg_rr_replay-01.js]
[browser_dbg_rr_replay-02.js]
[browser_dbg_rr_replay-03.js]
[browser_dbg_rr_console_warp-01.js]
[browser_dbg_rr_console_warp-02.js]

View File

@ -2,11 +2,14 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test basic breakpoint functionality in web replay.
async function test() {
waitForExplicitFinish();
add_task(async function() {
const dbg = await attachRecordingDebugger(
"doc_rr_basic.html",
{ waitForRecording: true }
@ -38,5 +41,4 @@ async function test() {
await toolbox.closeToolbox();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -2,12 +2,16 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test unhandled divergence while evaluating at a breakpoint with Web Replay.
async function test() {
waitForExplicitFinish();
const dbg = await attachRecordingDebugger("doc_rr_basic.html", { waitForRecording: true });
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_basic.html",
{ waitForRecording: true });
const {threadClient, tab, toolbox} = dbg;
await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
@ -21,5 +25,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -2,11 +2,14 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test some issues when stepping around after hitting a breakpoint while recording.
async function test() {
waitForExplicitFinish();
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
const {threadClient, tab, toolbox} = dbg;
@ -14,7 +17,9 @@ async function test() {
await setBreakpoint(threadClient, "doc_rr_continuous.html", 19);
await resumeToLine(threadClient, 19);
await reverseStepOverToLine(threadClient, 18);
await checkEvaluateInTopFrame(threadClient, "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)", undefined);
await checkEvaluateInTopFrame(threadClient,
"SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)",
undefined);
await stepInToLine(threadClient, 22);
await setBreakpoint(threadClient, "doc_rr_continuous.html", 24);
await resumeToLine(threadClient, 24);
@ -23,5 +28,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -2,18 +2,21 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test navigating back to earlier breakpoints while recording, then resuming
// recording.
async function test() {
waitForExplicitFinish();
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
const {threadClient, tab, toolbox} = dbg;
await setBreakpoint(threadClient, "doc_rr_continuous.html", 14);
await resumeToLine(threadClient, 14);
let value = await evaluateInTopFrame(threadClient, "number");
const value = await evaluateInTopFrame(threadClient, "number");
await resumeToLine(threadClient, 14);
await checkEvaluateInTopFrame(threadClient, "number", value + 1);
await rewindToLine(threadClient, 14);
@ -29,5 +32,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -2,14 +2,17 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test hitting breakpoints when rewinding past the point where the breakpoint
// script was created.
async function test() {
waitForExplicitFinish();
add_task(async function() {
const dbg = await attachRecordingDebugger(
"doc_rr_basic.html",
"doc_rr_basic.html",
{ waitForRecording: true }
);
@ -26,5 +29,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -2,37 +2,24 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
// This functionality was copied from devtools/client/webconsole/test/mochitest/head.js,
// since this test straddles both the web console and the debugger. I couldn't
// figure out how to load that script directly here.
function waitForThreadEvents(threadClient, eventName) {
info(`Waiting for thread event '${eventName}' to fire.`);
return new Promise(function(resolve, reject) {
threadClient.addListener(eventName, function onEvent(eventName, ...args) {
info(`Thread event '${eventName}' fired.`);
threadClient.removeListener(eventName, onEvent);
resolve.apply(resolve, args);
});
});
}
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test basic console time warping functionality in web replay.
async function test() {
waitForExplicitFinish();
add_task(async function() {
const dbg = await attachRecordingDebugger(
"doc_rr_error.html",
"doc_rr_error.html",
{ waitForRecording: true }
);
const {tab, toolbox, threadClient} = dbg;
const console = await getSplitConsole(dbg);
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
await warpToMessage(hud, threadClient, "Number 5");
await warpToMessage(hud, dbg, "Number 5");
await threadClient.interrupt();
await checkEvaluateInTopFrame(threadClient, "number", 5);
@ -49,5 +36,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -0,0 +1,33 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test basic console time warping functionality in web replay.
add_task(async function() {
const dbg = await attachRecordingDebugger(
"doc_rr_logs.html",
{ waitForRecording: true }
);
const {tab, toolbox, threadClient} = dbg;
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
let message = await warpToMessage(hud, dbg, "number: 1");
ok(!message.classList.contains("paused-before"), "paused before message is not shown");
await stepOverToLine(threadClient, 18);
await reverseStepOverToLine(threadClient, 17);
message = findMessage(hud, "number: 1");
ok(message.classList.contains("paused-before"), "paused before message is shown");
await toolbox.destroy();
await gBrowser.removeTab(tab);
});

View File

@ -2,12 +2,15 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test basic recording of a tab without any debugging.
async function test() {
waitForExplicitFinish();
var recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
add_task(async function() {
const recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = recordingTab;
openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
await once(Services.ppmm, "RecordingFinished");
@ -15,5 +18,4 @@ async function test() {
await gBrowser.removeTab(recordingTab);
ok(true, "Finished");
finish();
}
});

View File

@ -2,27 +2,32 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test basic recovery of crashed child processes in web replay.
async function test() {
waitForExplicitFinish();
let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
add_task(async function() {
const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = tab;
openTrustedLinkIn(EXAMPLE_URL + "doc_rr_recovery.html", "current");
await once(Services.ppmm, "RecordingFinished");
let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
await client.interrupt();
await setBreakpoint(client, "doc_rr_recovery.html", 21);
await rewindToLine(client, 21);
await checkEvaluateInTopFrame(client, "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1)", undefined);
await checkEvaluateInTopFrame(client,
"SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1)",
undefined);
await stepOverToLine(client, 22);
await stepOverToLine(client, 23);
await checkEvaluateInTopFrame(client, "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1); " +
"SpecialPowers.Cu.recordReplayDirective(/* MaybeCrash */ 2)", undefined);
await checkEvaluateInTopFrame(client,
"SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1); " +
"SpecialPowers.Cu.recordReplayDirective(/* MaybeCrash */ 2)",
undefined);
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -2,27 +2,32 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Basic test for saving a recording and then replaying it in a new tab.
async function test() {
waitForExplicitFinish();
let recordingFile = newRecordingFile();
let recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
add_task(async function() {
const recordingFile = newRecordingFile();
const recordingTab = BrowserTestUtils.addTab(gBrowser, null,
{ recordExecution: "*" });
gBrowser.selectedTab = recordingTab;
openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
await once(Services.ppmm, "RecordingFinished");
let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
ok(tabParent, "Found recording tab parent");
ok(tabParent.saveRecording(recordingFile), "Saved recording");
await once(Services.ppmm, "SaveRecordingFinished");
let replayingTab = BrowserTestUtils.addTab(gBrowser, null, { replayExecution: recordingFile });
const replayingTab = BrowserTestUtils.addTab(gBrowser, null,
{ replayExecution: recordingFile });
gBrowser.selectedTab = replayingTab;
await once(Services.ppmm, "HitRecordingEndpoint");
let toolbox = await attachDebugger(replayingTab), client = toolbox.threadClient;
const toolbox = await attachDebugger(replayingTab), client = toolbox.threadClient;
await client.interrupt();
await setBreakpoint(client, "doc_rr_basic.html", 21);
await rewindToLine(client, 21);
@ -35,5 +40,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(recordingTab);
await gBrowser.removeTab(replayingTab);
finish();
}
});

View File

@ -2,13 +2,18 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test ending a recording at a breakpoint and then separately replaying to the end.
async function test() {
add_task(async function() {
waitForExplicitFinish();
let recordingFile = newRecordingFile();
let recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
const recordingFile = newRecordingFile();
const recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = recordingTab;
openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
@ -18,9 +23,9 @@ async function test() {
await resumeToLine(client, 14);
await resumeToLine(client, 14);
await reverseStepOverToLine(client, 13);
let lastNumberValue = await evaluateInTopFrame(client, "number");
const lastNumberValue = await evaluateInTopFrame(client, "number");
let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
ok(tabParent, "Found recording tab parent");
ok(tabParent.saveRecording(recordingFile), "Saved recording");
await once(Services.ppmm, "SaveRecordingFinished");
@ -28,7 +33,8 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(recordingTab);
let replayingTab = BrowserTestUtils.addTab(gBrowser, null, { replayExecution: recordingFile });
const replayingTab = BrowserTestUtils.addTab(gBrowser, null,
{ replayExecution: recordingFile });
gBrowser.selectedTab = replayingTab;
await once(Services.ppmm, "HitRecordingEndpoint");
@ -45,5 +51,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(replayingTab);
finish();
}
});

View File

@ -2,25 +2,30 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
// Test for saving a recording and then replaying it in a new tab, with rewinding disabled.
async function test() {
waitForExplicitFinish();
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test for saving a recording and then replaying it in a new tab,
// with rewinding disabled.
add_task(async function() {
await pushPref("devtools.recordreplay.enableRewinding", false);
let recordingFile = newRecordingFile();
let recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
const recordingFile = newRecordingFile();
const recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = recordingTab;
openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
await once(Services.ppmm, "RecordingFinished");
let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
ok(tabParent, "Found recording tab parent");
ok(tabParent.saveRecording(recordingFile), "Saved recording");
await once(Services.ppmm, "SaveRecordingFinished");
let replayingTab = BrowserTestUtils.addTab(gBrowser, null, { replayExecution: recordingFile });
const replayingTab = BrowserTestUtils.addTab(gBrowser, null,
{ replayExecution: recordingFile });
gBrowser.selectedTab = replayingTab;
await once(Services.ppmm, "HitRecordingEndpoint");
@ -28,5 +33,4 @@ async function test() {
await gBrowser.removeTab(recordingTab);
await gBrowser.removeTab(replayingTab);
finish();
}
});

View File

@ -2,17 +2,20 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test basic step-over/back functionality in web replay.
async function test() {
waitForExplicitFinish();
let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
add_task(async function() {
const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = tab;
openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
await once(Services.ppmm, "RecordingFinished");
let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
await client.interrupt();
await setBreakpoint(client, "doc_rr_basic.html", 21);
await rewindToLine(client, 21);
@ -25,5 +28,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -2,17 +2,20 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test fixes for some simple stepping bugs.
async function test() {
waitForExplicitFinish();
let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
add_task(async function() {
const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = tab;
openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
await once(Services.ppmm, "RecordingFinished");
let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
await client.interrupt();
await setBreakpoint(client, "doc_rr_basic.html", 22);
await rewindToLine(client, 22);
@ -26,5 +29,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -2,20 +2,23 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Test stepping back while recording, then resuming recording.
async function test() {
waitForExplicitFinish();
let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
add_task(async function() {
const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = tab;
openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
await client.interrupt();
await setBreakpoint(client, "doc_rr_continuous.html", 13);
await resumeToLine(client, 13);
let value = await evaluateInTopFrame(client, "number");
const value = await evaluateInTopFrame(client, "number");
await reverseStepOverToLine(client, 12);
await checkEvaluateInTopFrame(client, "number", value - 1);
await resumeToLine(client, 13);
@ -24,5 +27,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -2,17 +2,20 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// To disable all Web Replay tests, see browser.ini
// Stepping past the beginning or end of a frame should act like a step-out.
async function test() {
waitForExplicitFinish();
let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
add_task(async function() {
const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = tab;
openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
await once(Services.ppmm, "RecordingFinished");
let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
await client.interrupt();
await setBreakpoint(client, "doc_rr_basic.html", 21);
await rewindToLine(client, 21);
@ -38,5 +41,4 @@ async function test() {
await toolbox.destroy();
await gBrowser.removeTab(tab);
finish();
}
});

View File

@ -0,0 +1,144 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
this
);
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/debugger/new/test/mochitest/helpers.js",
this
);
const EXAMPLE_URL =
"http://example.com/browser/devtools/client/webreplay/mochitest/examples/";
// Attach a debugger to a tab, returning a promise that resolves with the
// debugger's toolbox.
async function attachDebugger(tab) {
const target = await TargetFactory.forTab(tab);
const toolbox = await gDevTools.showToolbox(target, "jsdebugger");
return toolbox;
}
async function attachRecordingDebugger(url,
{ waitForRecording } = { waitForRecording: false }) {
const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = tab;
openTrustedLinkIn(EXAMPLE_URL + url, "current");
if (waitForRecording) {
await once(Services.ppmm, "RecordingFinished");
}
const toolbox = await attachDebugger(tab);
const dbg = createDebuggerContext(toolbox);
const threadClient = dbg.toolbox.threadClient;
await threadClient.interrupt();
return {...dbg, tab, threadClient};
}
// Return a promise that resolves when a breakpoint has been set.
async function setBreakpoint(threadClient, expectedFile, lineno) {
const {sources} = await threadClient.getSources();
ok(sources.length == 1, "Got one source");
ok(RegExp(expectedFile).test(sources[0].url), "Source is " + expectedFile);
const sourceClient = threadClient.source(sources[0]);
await sourceClient.setBreakpoint({ line: lineno });
}
function resumeThenPauseAtLineFunctionFactory(method) {
return async function(threadClient, lineno) {
threadClient[method]();
await threadClient.addOneTimeListener("paused", async function(event, packet) {
const {frames} = await threadClient.getFrames(0, 1);
const frameLine = frames[0] ? frames[0].where.line : undefined;
ok(frameLine == lineno, "Paused at line " + frameLine + " expected " + lineno);
});
};
}
// Define various methods that resume a thread in a specific way and ensure it
// pauses at a specified line.
var rewindToLine = resumeThenPauseAtLineFunctionFactory("rewind");
var resumeToLine = resumeThenPauseAtLineFunctionFactory("resume");
var reverseStepOverToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOver");
var stepOverToLine = resumeThenPauseAtLineFunctionFactory("stepOver");
var reverseStepInToLine = resumeThenPauseAtLineFunctionFactory("reverseStepIn");
var stepInToLine = resumeThenPauseAtLineFunctionFactory("stepIn");
var reverseStepOutToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOut");
var stepOutToLine = resumeThenPauseAtLineFunctionFactory("stepOut");
// Return a promise that resolves when a thread evaluates a string in the
// topmost frame, with the result throwing an exception.
async function checkEvaluateInTopFrameThrows(threadClient, text) {
const {frames} = await threadClient.getFrames(0, 1);
ok(frames.length == 1, "Got one frame");
const response = await threadClient.eval(frames[0].actor, text);
ok(response.type == "resumed", "Got resume response from eval");
await threadClient.addOneTimeListener("paused", function(event, packet) {
ok(packet.type == "paused" &&
packet.why.type == "clientEvaluated" &&
"throw" in packet.why.frameFinished, "Eval threw an exception");
});
}
// Return a pathname that can be used for a new recording file.
function newRecordingFile() {
ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
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];
}
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 waitForMessages(hud, text, selector = ".message") {
return waitUntilPredicate(() => findMessages(hud, text, selector));
}
async function warpToMessage(hud, threadClient, text) {
let messages = await waitForMessages(hud, text);
ok(messages.length == 1, "Found one message");
const message = messages.pop();
const menuPopup = await openConsoleContextMenu(hud, message);
console.log(`.>> menu`, menuPopup);
const timeWarpItem = menuPopup.querySelector("#console-menu-time-warp");
ok(timeWarpItem, "Time warp menu item is available");
timeWarpItem.click();
await Promise.all([
hideConsoleContextMenu(hud),
once(Services.ppmm, "TimeWarpFinished"),
waitForThreadEvents(threadClient, "paused"),
]);
messages = findMessages(hud, "", ".paused");
ok(messages.length == 1, "Found one paused message");
return message;
}

View File

@ -14,3 +14,5 @@ DevToolsModules(
with Files('**'):
BUG_COMPONENT = ('Core', 'Web Replay')
BROWSER_CHROME_MANIFESTS += [ 'mochitest/browser.ini' ]