Files
sousa-gecko/devtools/client/framework/test/browser_browser_toolbox_debugger.js
T
2018-06-01 12:36:09 +02:00

166 lines
6.6 KiB
JavaScript

/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// This test asserts that the new debugger works from the browser toolbox process
// Its pass a big piece of Javascript string to the browser toolbox process via
// MOZ_TOOLBOX_TEST_SCRIPT env variable. It does that as test resources fetched from
// chrome://mochitests/ package isn't available from browser toolbox process.
// There are shutdown issues for which multiple rejections are left uncaught.
// See bug 1018184 for resolving these issues.
const { PromiseTestUtils } = scopedCuImport("resource://testing-common/PromiseTestUtils.jsm");
PromiseTestUtils.whitelistRejectionsGlobally(/File closed/);
// On debug test runner, it takes about 50s to run the test.
requestLongerTimeout(4);
const { fetch } = require("devtools/shared/DevToolsUtils");
const debuggerHeadURL = CHROME_URL_ROOT + "../../debugger/new/test/mochitest/head.js";
const helpersURL = CHROME_URL_ROOT + "../../debugger/new/test/mochitest/helpers.js";
const testScriptURL = CHROME_URL_ROOT + "test_browser_toolbox_debugger.js";
add_task(async function runTest() {
await new Promise(done => {
const options = {"set": [
["devtools.debugger.prompt-connection", false],
["devtools.debugger.remote-enabled", true],
["devtools.chrome.enabled", true],
// Test-only pref to allow passing `testScript` argument to the browser
// toolbox
["devtools.browser-toolbox.allow-unsafe-script", true],
// On debug test runner, it takes more than the default time (20s)
// to get a initialized console
["devtools.debugger.remote-timeout", 120000]
]};
SpecialPowers.pushPrefEnv(options, done);
});
const s = Cu.Sandbox("http://mozilla.org");
// Use a unique id for the fake script name in order to be able to run
// this test more than once. That's because the Sandbox is not immediately
// destroyed and so the debugger would display only one file but not necessarily
// connected to the latest sandbox.
const id = new Date().getTime();
// Pass a fake URL to evalInSandbox. If we just pass a filename,
// Debugger is going to fail and only display root folder (`/`) listing.
// But it won't try to fetch this url and use sandbox content as expected.
const testUrl = `http://mozilla.org/browser-toolbox-test-${id}.js`;
Cu.evalInSandbox("(" + function() {
this.plop = function plop() {
return 1;
};
} + ").call(this)", s, "1.8", testUrl, 0);
// Execute the function every second in order to trigger the breakpoint
const interval = setInterval(s.plop, 1000);
// Be careful, this JS function is going to be executed in the browser toolbox,
// which lives in another process. So do not try to use any scope variable!
const env = Cc["@mozilla.org/process/environment;1"]
.getService(Ci.nsIEnvironment);
// First inject a very minimal head, with simplest assertion methods
// and very common globals
/* eslint-disable no-unused-vars */
const testHead = (function() {
const info = msg => dump(msg + "\n");
const is = (a, b, description) => {
let msg = "'" + JSON.stringify(a) + "' is equal to '" + JSON.stringify(b) + "'";
if (description) {
msg += " - " + description;
}
if (a !== b) {
msg = "FAILURE: " + msg;
dump(msg + "\n");
throw new Error(msg);
} else {
msg = "SUCCESS: " + msg;
dump(msg + "\n");
}
};
const ok = (a, description) => {
let msg = "'" + JSON.stringify(a) + "' is true";
if (description) {
msg += " - " + description;
}
if (!a) {
msg = "FAILURE: " + msg;
dump(msg + "\n");
throw new Error(msg);
} else {
msg = "SUCCESS: " + msg;
dump(msg + "\n");
}
};
const registerCleanupFunction = () => {};
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
// Copied from shared-head.js:
// test_browser_toolbox_debugger.js uses waitForPaused, which relies on waitUntil
// which is normally provided by shared-head.js
const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm", {});
function waitUntil(predicate, interval = 10) {
if (predicate()) {
return Promise.resolve(true);
}
return new Promise(resolve => {
// TODO: fixme.
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(function() {
waitUntil(predicate, interval).then(() => resolve(true));
}, interval);
});
}
}).toSource().replace(/^\(function\(\) \{|\}\)$/g, "");
/* eslint-enable no-unused-vars */
// Stringify testHead's function and remove `(function {` prefix and `})` suffix
// to ensure inner symbols gets exposed to next pieces of code
// Then inject new debugger head file
let { content: debuggerHead } = await fetch(debuggerHeadURL);
// We remove its import of shared-head, which isn't available in browser toolbox process
// And isn't needed thanks to testHead's symbols
debuggerHead = debuggerHead.replace(/Services.scriptloader.loadSubScript[^\)]*\);/g, "");
// Also include the debugger helpers which are separated from debugger's head to be
// reused in other modules.
const { content: debuggerHelpers } = await fetch(helpersURL);
debuggerHead = debuggerHead + debuggerHelpers;
// Finally, fetch the debugger test script that is going to be execute in the browser
// toolbox process
const testScript = (await fetch(testScriptURL)).content;
const source =
"try { let testUrl = \"" + testUrl + "\";" + testHead + debuggerHead + testScript + "} catch (e) {" +
" dump('Exception: '+ e + ' at ' + e.fileName + ':' + " +
" e.lineNumber + '\\nStack: ' + e.stack + '\\n');" +
"}";
env.set("MOZ_TOOLBOX_TEST_SCRIPT", source);
registerCleanupFunction(() => {
env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
});
const { BrowserToolboxProcess } = ChromeUtils.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
// Use two promises, one for each BrowserToolboxProcess.init callback
// arguments, to ensure that we wait for toolbox run and close events.
let closePromise;
await new Promise(onRun => {
closePromise = new Promise(onClose => {
info("Opening the browser toolbox\n");
BrowserToolboxProcess.init(onClose, onRun);
});
});
ok(true, "Browser toolbox started\n");
await closePromise;
ok(true, "Browser toolbox process just closed");
clearInterval(interval);
});