Commit 43086cf6 authored by Julian Descottes's avatar Julian Descottes
Browse files

Bug 1598259 - Migrate existing BrowserToolbox tests to ToolboxTask r=ochameau

Differential Revision: https://phabricator.services.mozilla.com/D54118

--HG--
extra : moz-landing-system : lando
parent 40b8004e
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -6,7 +6,6 @@

BROWSER_CHROME_MANIFESTS += [
    'test/allocations/browser_allocations_target.ini',
    'test/browser-rtl.ini',
    'test/browser-telemetry-startup.ini',
    'test/browser.ini',
    'test/metrics/browser_metrics_debugger.ini',
+0 −14
Original line number Diff line number Diff line
[DEFAULT]
tags = devtools
subsuite = devtools
prefs =
  # This test suite is dedicated to tests that need to run with the browser in RTL mode.
  # This mode cannot be dynamically changed between tests reusing the browser instance and
  # window.
  intl.uidirection=1
support-files =
  head.js
  !/devtools/client/shared/test/shared-head.js
  !/devtools/client/shared/test/telemetry-test-helpers.js

[browser_browser_toolbox_rtl.js]
+5 −4
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@ support-files =
  serviceworker.js
  sjs_code_reload.sjs
  sjs_code_bundle_reload_map.sjs
  test_browser_toolbox_debugger.js
  test_chrome_page.html
  !/devtools/client/debugger/test/mochitest/head.js
  !/devtools/client/debugger/test/mochitest/helpers.js
@@ -59,11 +58,13 @@ prefs =
[browser_about-devtools-toolbox_load.js]
[browser_about-devtools-toolbox_reload.js]
[browser_browser_toolbox.js]
skip-if = coverage # Bug 1387827
skip-if = coverage || asan # Bug 1387827, Bug 1591064
[browser_browser_toolbox_debugger.js]
skip-if = os == 'win' || debug || (bits == 64 && !debug && (os == 'mac' || os == 'linux')) # Bug 1282269, 1448084, Bug 1270731
skip-if = os == 'win' || debug || (bits == 64 && !debug && (os == 'mac' || os == 'linux')) || asan # Bug 1282269, Bug 1448084, Bug 1270731, Bug 1591064
[browser_browser_toolbox_fission_inspector.js]
skip-if = coverage # Bug 1387827
skip-if = coverage || asan # Bug 1387827, Bug 1591064
[browser_browser_toolbox_rtl.js]
skip-if = asan # Bug 1591064
[browser_devtools_api_destroy.js]
[browser_dynamic_tool_enabling.js]
[browser_front_parentFront.js]
+12 −76
Original line number Diff line number Diff line
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

/* import-globals-from helpers.js */
Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/devtools/client/framework/test/helpers.js",
  this
);

// There are shutdown issues for which multiple rejections are left uncaught.
// See bug 1018184 for resolving these issues.
const { PromiseTestUtils } = ChromeUtils.import(
@@ -12,84 +18,14 @@ PromiseTestUtils.whitelistRejectionsGlobally(/File closed/);
requestLongerTimeout(4);

add_task(async function() {
  await setupPreferencesForBrowserToolbox();

  // Wait for a notification sent by a script evaluated in the webconsole
  // of the browser toolbox.
  const onCustomMessage = new Promise(done => {
    Services.obs.addObserver(function listener(target, aTop, data) {
      Services.obs.removeObserver(listener, "browser-toolbox-console-works");
      done(data === "true");
    }, "browser-toolbox-console-works");
  });
  const ToolboxTask = await initBrowserToolboxTask();
  await ToolboxTask.importFunctions({});

  // Be careful, this JS function is going to be executed in the addon 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
  );
  /* global toolbox */
  const testScript = function() {
    toolbox
      .selectTool("webconsole")
      .then(console => {
        // This is for checking Browser Toolbox doesn't have a close button.
        const hasCloseButton = !!toolbox.doc.getElementById("toolbox-close");
        const { wrapper } = console.hud.ui;
        const js = `Services.obs.notifyObservers(null, 'browser-toolbox-console-works', ${hasCloseButton} )`;
        const onResult = new Promise(resolve => {
          const onNewMessages = messages => {
            for (const message of messages) {
              if (message.node.classList.contains("result")) {
                console.hud.ui.off("new-messages", onNewMessages);
                resolve();
              }
            }
          };
          console.hud.ui.on("new-messages", onNewMessages);
        });
        wrapper.dispatchEvaluateExpression(js);
        return onResult;
      })
      .then(() => toolbox.destroy());
  };
  env.set("MOZ_TOOLBOX_TEST_SCRIPT", "new " + testScript);
  registerCleanupFunction(() => {
    env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
  const hasCloseButton = await ToolboxTask.spawn(null, async () => {
    /* global gToolbox */
    return !!gToolbox.doc.getElementById("toolbox-close");
  });

  const { BrowserToolboxProcess } = ChromeUtils.import(
    "resource://devtools/client/framework/ToolboxProcess.jsm"
  );
  is(
    BrowserToolboxProcess.getBrowserToolboxSessionState(),
    false,
    "No session state initially"
  );

  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");
  is(
    BrowserToolboxProcess.getBrowserToolboxSessionState(),
    true,
    "Has session state"
  );

  const hasCloseButton = await onCustomMessage;
  ok(true, "Received the custom message");
  ok(!hasCloseButton, "Browser toolbox doesn't have a close button");

  await closePromise;
  ok(true, "Browser toolbox process just closed");
  is(
    BrowserToolboxProcess.getBrowserToolboxSessionState(),
    false,
    "No session state after closing"
  );
  await ToolboxTask.destroy();
});
+76 −93
Original line number Diff line number Diff line
@@ -2,9 +2,12 @@
   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.

/* import-globals-from helpers.js */
Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/devtools/client/framework/test/helpers.js",
  this
);

// There are shutdown issues for which multiple rejections are left uncaught.
// See bug 1018184 for resolving these issues.
@@ -23,11 +26,8 @@ const debuggerHeadURL =
const helpersURL = CHROME_URL_ROOT + "../../debugger/test/mochitest/helpers.js";
const helpersContextURL =
  CHROME_URL_ROOT + "../../debugger/test/mochitest/helpers/context.js";
const testScriptURL = CHROME_URL_ROOT + "test_browser_toolbox_debugger.js";

add_task(async function runTest() {
  await setupPreferencesForBrowserToolbox();

  const s = Cu.Sandbox("http://mozilla.org");

  // Use a unique id for the fake script name in order to be able to run
@@ -57,17 +57,25 @@ add_task(async function runTest() {
  // 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
  let { content: debuggerHead } = await fetch(debuggerHeadURL);

  // Also include the debugger helpers which are separated from debugger's head to be
  // reused in other modules.
  const { content: debuggerHelpers } = await fetch(helpersURL);
  const { content: debuggerContextHelpers } = await fetch(helpersContextURL);
  debuggerHead = debuggerHead + debuggerContextHelpers + debuggerHelpers;

  // 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,
    ""
  );
  // 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) => {
  const ToolboxTask = await initBrowserToolboxTask();
  await ToolboxTask.importScript(debuggerHead);
  await ToolboxTask.importFunctions({
    info: msg => dump(msg + "\n"),
    is: (a, b, description) => {
      let msg =
        "'" + JSON.stringify(a) + "' is equal to '" + JSON.stringify(b) + "'";
      if (description) {
@@ -81,8 +89,8 @@ add_task(async function runTest() {
        msg = "SUCCESS: " + msg;
        dump(msg + "\n");
      }
    };
    const ok = (a, description) => {
    },
    ok: (a, description) => {
      let msg = "'" + JSON.stringify(a) + "' is true";
      if (description) {
        msg += " - " + description;
@@ -95,92 +103,67 @@ add_task(async function runTest() {
        msg = "SUCCESS: " + msg;
        dump(msg + "\n");
      }
    };

    const registerCleanupFunction = () => {};
    },
    waitUntil,
  });

    const { require } = ChromeUtils.import(
      "resource://devtools/shared/Loader.jsm"
    );
  await ToolboxTask.spawn(`"${testUrl}"`, async _testUrl => {
    /* global createDebuggerContext, waitForSources,
          waitForPaused, addBreakpoint, assertPausedLocation, stepIn,
          findSource, removeBreakpoint, resume, selectSource */
    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);
    Services.prefs.clearUserPref("devtools.debugger.tabs");
    Services.prefs.clearUserPref("devtools.debugger.pending-selected-location");

  // Also include the debugger helpers which are separated from debugger's head to be
  // reused in other modules.
  const { content: debuggerHelpers } = await fetch(helpersURL);
  const { content: debuggerContextHelpers } = await fetch(helpersContextURL);
  debuggerHead = debuggerHead + debuggerContextHelpers + debuggerHelpers;
    info("Waiting for debugger load");
    /* global gToolbox */
    await gToolbox.selectTool("jsdebugger");
    const dbg = createDebuggerContext(gToolbox);
    const window = dbg.win;
    const document = window.document;

  // 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,
    ""
  );
    await waitForSources(dbg, _testUrl);

  // 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", "");
    info("Loaded, selecting the test script to debug");
    // First expand the domain
    const domain = [...document.querySelectorAll(".tree-node")].find(node => {
      return node.querySelector(".label").textContent.trim() == "mozilla.org";
    });
    const arrow = domain.querySelector(".arrow");
    arrow.click();

  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);
    });
    const fileName = _testUrl.match(/browser-toolbox-test.*\.js/)[0];

    let script = [...document.querySelectorAll(".tree-node")].find(node => {
      return node.textContent.includes(fileName);
    });
  ok(true, "Browser toolbox started\n");
    script = script.querySelector(".node");
    script.click();

    const onPaused = waitForPaused(dbg);
    await selectSource(dbg, fileName);
    await addBreakpoint(dbg, fileName, 2);

    await onPaused;

  await closePromise;
  ok(true, "Browser toolbox process just closed");
    assertPausedLocation(dbg, fileName, 2);

    await stepIn(dbg);

    assertPausedLocation(dbg, fileName, 3);

    // Remove the breakpoint before resuming in order to prevent hitting the breakpoint
    // again during test closing.
    const source = findSource(dbg, fileName);
    await removeBreakpoint(dbg, source.id, 2);

    await resume(dbg);
  });

  clearInterval(interval);

  await ToolboxTask.destroy();
});
Loading