Bug 1598259 - Migrate existing BrowserToolbox tests to ToolboxTask r=ochameau
☠☠ backed out by 3bd8841fab31 ☠ ☠
authorJulian Descottes <jdescottes@mozilla.com>
Fri, 22 Nov 2019 07:53:52 +0000
changeset 503323 70b22c90ea2e2a3294d46a41cb4cb42be065537c
parent 503322 40b16ced3f44e21488df66fc45e5a9f0a9a58b3d
child 503324 38140fd01a52aa9e9f42d98200304eec6fcb6018
push id36833
push userbtara@mozilla.com
push dateFri, 22 Nov 2019 21:40:53 +0000
treeherdermozilla-central@2c912e46295e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau
bugs1598259
milestone72.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 1598259 - Migrate existing BrowserToolbox tests to ToolboxTask r=ochameau Differential Revision: https://phabricator.services.mozilla.com/D54118
devtools/client/framework/moz.build
devtools/client/framework/test/browser-rtl.ini
devtools/client/framework/test/browser.ini
devtools/client/framework/test/browser_browser_toolbox.js
devtools/client/framework/test/browser_browser_toolbox_debugger.js
devtools/client/framework/test/browser_browser_toolbox_fission_inspector.js
devtools/client/framework/test/browser_browser_toolbox_rtl.js
devtools/client/framework/test/head.js
devtools/client/framework/test/helpers.js
devtools/client/framework/test/test_browser_toolbox_debugger.js
--- a/devtools/client/framework/moz.build
+++ b/devtools/client/framework/moz.build
@@ -1,17 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 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',
     'test/metrics/browser_metrics_inspector.ini',
     'test/metrics/browser_metrics_netmonitor.ini',
     'test/metrics/browser_metrics_webconsole.ini',
 ]
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
deleted file mode 100644
--- a/devtools/client/framework/test/browser-rtl.ini
+++ /dev/null
@@ -1,14 +0,0 @@
-[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]
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -37,17 +37,16 @@ support-files =
   helper_disable_cache.js
   doc_theme.css
   doc_viewsource.html
   browser_toolbox_options_enable_serviceworkers_testing_frame_script.js
   browser_toolbox_options_enable_serviceworkers_testing.html
   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
   !/devtools/client/debugger/test/mochitest/helpers/context.js
   !/devtools/client/shared/test/frame-script-utils.js
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/shared-redux-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
@@ -59,16 +58,17 @@ prefs =
 [browser_about-devtools-toolbox_load.js]
 [browser_about-devtools-toolbox_reload.js]
 [browser_browser_toolbox.js]
 skip-if = coverage # Bug 1387827
 [browser_browser_toolbox_debugger.js]
 skip-if = os == 'win' || debug || (bits == 64 && !debug && (os == 'mac' || os == 'linux')) # Bug 1282269, 1448084, Bug 1270731
 [browser_browser_toolbox_fission_inspector.js]
 skip-if = coverage # Bug 1387827
+[browser_browser_toolbox_rtl.js]
 [browser_devtools_api_destroy.js]
 [browser_dynamic_tool_enabling.js]
 [browser_front_parentFront.js]
 [browser_ignore_toolbox_network_requests.js]
 [browser_keybindings_01.js]
 [browser_keybindings_02.js]
 [browser_keybindings_03.js]
 [browser_menu_api.js]
--- a/devtools/client/framework/test/browser_browser_toolbox.js
+++ b/devtools/client/framework/test/browser_browser_toolbox.js
@@ -1,95 +1,31 @@
 /* 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(
   "resource://testing-common/PromiseTestUtils.jsm"
 );
 PromiseTestUtils.whitelistRejectionsGlobally(/File closed/);
 
 // On debug test slave, it takes about 50s to run the test.
 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();
 });
--- a/devtools/client/framework/test/browser_browser_toolbox_debugger.js
+++ b/devtools/client/framework/test/browser_browser_toolbox_debugger.js
@@ -1,15 +1,18 @@
 /* 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.
+
+/* 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(
   "resource://testing-common/PromiseTestUtils.jsm"
 );
 PromiseTestUtils.whitelistRejectionsGlobally(/File closed/);
 
@@ -18,21 +21,18 @@ requestLongerTimeout(4);
 
 const { fetch } = require("devtools/shared/DevToolsUtils");
 
 const debuggerHeadURL =
   CHROME_URL_ROOT + "../../debugger/test/mochitest/head.js";
 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
   // 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();
 
@@ -52,135 +52,118 @@ add_task(async function runTest() {
     "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);
 
   // 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,
     ""
   );
-
-  // 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 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) {
+        msg += " - " + description;
+      }
+      if (a !== b) {
+        msg = "FAILURE: " + msg;
+        dump(msg + "\n");
+        throw new Error(msg);
+      } else {
+        msg = "SUCCESS: " + msg;
+        dump(msg + "\n");
+      }
+    },
+    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");
+      }
+    },
+    waitUntil,
   });
 
-  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);
+  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"
+    );
+
+    Services.prefs.clearUserPref("devtools.debugger.tabs");
+    Services.prefs.clearUserPref("devtools.debugger.pending-selected-location");
+
+    info("Waiting for debugger load");
+    /* global gToolbox */
+    await gToolbox.selectTool("jsdebugger");
+    const dbg = createDebuggerContext(gToolbox);
+    const window = dbg.win;
+    const document = window.document;
+
+    await waitForSources(dbg, _testUrl);
+
+    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";
     });
-  });
-  ok(true, "Browser toolbox started\n");
+    const arrow = domain.querySelector(".arrow");
+    arrow.click();
+
+    const fileName = _testUrl.match(/browser-toolbox-test.*\.js/)[0];
+
+    let script = [...document.querySelectorAll(".tree-node")].find(node => {
+      return node.textContent.includes(fileName);
+    });
+    script = script.querySelector(".node");
+    script.click();
+
+    const onPaused = waitForPaused(dbg);
+    await selectSource(dbg, fileName);
+    await addBreakpoint(dbg, fileName, 2);
 
-  await closePromise;
-  ok(true, "Browser toolbox process just closed");
+    await onPaused;
+
+    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();
 });
--- a/devtools/client/framework/test/browser_browser_toolbox_fission_inspector.js
+++ b/devtools/client/framework/test/browser_browser_toolbox_fission_inspector.js
@@ -1,126 +1,79 @@
 /* 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(
   "resource://testing-common/PromiseTestUtils.jsm"
 );
 PromiseTestUtils.whitelistRejectionsGlobally(/File closed/);
 
 // On debug test slave, it takes about 50s to run the test.
 requestLongerTimeout(4);
 
 // This test is used to test fission-like features via the Browser Toolbox:
 // - computed view is correct when selecting an element in a remote frame
 
 add_task(async function() {
-  await setupPreferencesForBrowserToolbox();
+  const ToolboxTask = await initBrowserToolboxTask({
+    enableBrowserToolboxFission: true,
+  });
+  await ToolboxTask.importFunctions({});
 
   const tab = await addTab(
     `data:text/html,<div id="my-div" style="color: red">Foo</div>`
   );
 
   // Set a custom attribute on the tab's browser, in order to easily select it in the markup view
   tab.linkedBrowser.setAttribute("test-tab", "true");
 
-  // 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
-  );
-  /* global toolbox */
-  const testScript = function() {
-    // Force the fission pref in order to be able to pierce through the remote browser element
-    // Set the pref from the toolbox process as previous test may already have created
-    // the browser toolbox profile folder. Then setting the pref in Firefox process
-    // won't be taken into account for the browser toolbox.
-    const { Services } = ChromeUtils.import(
-      "resource://gre/modules/Services.jsm"
-    );
-    Services.prefs.setBoolPref("devtools.browsertoolbox.fission", true);
+  const color = await ToolboxTask.spawn(null, async () => {
+    /* global gToolbox */
+    const inspector = await gToolbox.selectTool("inspector");
+    const onSidebarSelect = inspector.sidebar.once("select");
+    inspector.sidebar.select("computedview");
+    await onSidebarSelect;
 
-    toolbox
-      .selectTool("inspector")
-      .then(async inspector => {
-        const onSidebarSelect = inspector.sidebar.once("select");
-        inspector.sidebar.select("computedview");
-        await onSidebarSelect;
+    async function select(walker, selector) {
+      const nodeFront = await walker.querySelector(walker.rootNode, selector);
+      const updated = inspector.once("inspector-updated");
+      inspector.selection.setNodeFront(nodeFront);
+      await updated;
+      return nodeFront;
+    }
+    const browser = await select(
+      inspector.walker,
+      'browser[remote="true"][test-tab]'
+    );
+    const browserTarget = await browser.connectToRemoteFrame();
+    const walker = (await browserTarget.getFront("inspector")).walker;
+    await select(walker, "#my-div");
 
-        async function select(walker, selector) {
-          const nodeFront = await walker.querySelector(
-            walker.rootNode,
-            selector
-          );
-          const updated = inspector.once("inspector-updated");
-          inspector.selection.setNodeFront(nodeFront);
-          await updated;
-          return nodeFront;
+    const view = inspector.getPanel("computedview").computedView;
+    function getProperty(name) {
+      const propertyViews = view.propertyViews;
+      for (const propView of propertyViews) {
+        if (propView.name == name) {
+          return propView;
         }
-        const browser = await select(
-          inspector.walker,
-          'browser[remote="true"][test-tab]'
-        );
-        const browserTarget = await browser.connectToRemoteFrame();
-        const walker = (await browserTarget.getFront("inspector")).walker;
-        await select(walker, "#my-div");
-
-        const view = inspector.getPanel("computedview").computedView;
-        function getProperty(name) {
-          const propertyViews = view.propertyViews;
-          for (const propView of propertyViews) {
-            if (propView.name == name) {
-              return propView;
-            }
-          }
-          return null;
-        }
-        const prop = getProperty("color");
-        const color = prop.valueNode.textContent;
-        if (color != "rgb(255, 0, 0)") {
-          throw new Error(
-            "The color property of the <div> within a tab isn't red, got: " +
-              color
-          );
-        }
-
-        Services.prefs.setBoolPref("devtools.browsertoolbox.fission", false);
-      })
-      .then(() => toolbox.destroy());
-  };
-  env.set("MOZ_TOOLBOX_TEST_SCRIPT", "new " + testScript);
-  registerCleanupFunction(() => {
-    env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
+      }
+      return null;
+    }
+    const prop = getProperty("color");
+    return prop.valueNode.textContent;
   });
 
-  const { BrowserToolboxProcess } = ChromeUtils.import(
-    "resource://devtools/client/framework/ToolboxProcess.jsm"
-  );
   is(
-    BrowserToolboxProcess.getBrowserToolboxSessionState(),
-    false,
-    "No session state initially"
+    color,
+    "rgb(255, 0, 0)",
+    "The color property of the <div> within a tab isn't red"
   );
 
-  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"
-  );
-
-  await closePromise;
-  ok(true, "Browser toolbox process just closed");
-  is(
-    BrowserToolboxProcess.getBrowserToolboxSessionState(),
-    false,
-    "No session state after closing"
-  );
+  await ToolboxTask.destroy();
 });
--- a/devtools/client/framework/test/browser_browser_toolbox_rtl.js
+++ b/devtools/client/framework/test/browser_browser_toolbox_rtl.js
@@ -1,94 +1,35 @@
 /* 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(
   "resource://testing-common/PromiseTestUtils.jsm"
 );
 PromiseTestUtils.whitelistRejectionsGlobally(/File closed/);
 
 // On debug test slave, it takes about 50s to run the test.
 requestLongerTimeout(4);
 
 // Test that DevTools panels are rendered in "rtl" (right-to-left) in the Browser Toolbox.
 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(resolve => {
-    Services.obs.addObserver(function listener(target, aTop, data) {
-      Services.obs.removeObserver(listener, "browser-toolbox-inspector-dir");
-      resolve(data);
-    }, "browser-toolbox-inspector-dir");
-  });
+  await pushPref("intl.uidirection", 1);
 
-  const env = Cc["@mozilla.org/process/environment;1"].getService(
-    Ci.nsIEnvironment
-  );
-  env.set(
-    "MOZ_TOOLBOX_TEST_SCRIPT",
-    "new function() {(" + testScript + ")();}"
-  );
-  registerCleanupFunction(() => env.set("MOZ_TOOLBOX_TEST_SCRIPT", ""));
-
-  const { BrowserToolboxProcess } = ChromeUtils.import(
-    "resource://devtools/client/framework/ToolboxProcess.jsm"
-  );
-
-  let closePromise;
-  await new Promise(onRun => {
-    closePromise = new Promise(onClose => {
-      info("Opening the browser toolbox");
-      BrowserToolboxProcess.init(onClose, onRun);
-    });
-  });
-  info("Browser toolbox started");
+  const ToolboxTask = await initBrowserToolboxTask();
+  await ToolboxTask.importFunctions({});
 
-  const inspectorPanelDirection = await onCustomMessage;
-  info("Received the custom message");
-  is(
-    inspectorPanelDirection,
-    "rtl",
-    "Inspector panel has the expected direction"
-  );
-
-  await closePromise;
-  info("Browser toolbox process just closed");
-  is(
-    BrowserToolboxProcess.getBrowserToolboxSessionState(),
-    false,
-    "No session state after closing"
-  );
-});
+  const dir = await ToolboxTask.spawn(null, async () => {
+    /* global gToolbox */
+    const inspector = await gToolbox.selectTool("inspector");
+    return inspector.panelDoc.dir;
+  });
+  is(dir, "rtl", "Inspector panel has the expected direction");
 
-// 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!
-async function testScript() {
-  /* global toolbox */
-  // Get the current direction of the inspector panel.
-  const inspector = await toolbox.selectTool("inspector");
-  const dir = inspector.panelDoc.dir;
-
-  // Switch to the webconsole to send the result to the main test.
-  const webconsole = await toolbox.selectTool("webconsole");
-  const js = `Services.obs.notifyObservers(null, "browser-toolbox-inspector-dir", "${dir}");`;
-
-  const onResult = new Promise(resolve => {
-    const onNewMessages = messages => {
-      for (const message of messages) {
-        if (message.node.classList.contains("result")) {
-          webconsole.hud.ui.off("new-messages", onNewMessages);
-          resolve();
-        }
-      }
-    };
-    webconsole.hud.ui.on("new-messages", onNewMessages);
-  });
-  webconsole.hud.ui.wrapper.dispatchEvaluateExpression(js);
-  await onResult;
-
-  // Destroy the toolbox.
-  await toolbox.destroy();
-}
+  await ToolboxTask.destroy();
+});
--- a/devtools/client/framework/test/head.js
+++ b/devtools/client/framework/test/head.js
@@ -435,38 +435,16 @@ async function openAboutToolbox(params) 
 
   return {
     tab,
     document: browser.contentDocument,
   };
 }
 
 /**
- * Enable temporary preferences useful to run browser toolbox process tests.
- * Returns a promise that will resolve when the preferences are set.
- */
-function setupPreferencesForBrowserToolbox() {
-  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],
-    ],
-  };
-
-  return SpecialPowers.pushPrefEnv(options);
-}
-
-/**
  * Load FTL.
  *
  * @param {Toolbox} toolbox
  *        Toolbox instance.
  * @param {String} path
  *        Path to the FTL file.
  */
 function loadFTL(toolbox, path) {
--- a/devtools/client/framework/test/helpers.js
+++ b/devtools/client/framework/test/helpers.js
@@ -4,35 +4,40 @@
 
 "use strict";
 
 const { BrowserToolboxProcess } = ChromeUtils.import(
   "resource://devtools/client/framework/ToolboxProcess.jsm"
 );
 const { DebuggerClient } = require("devtools/shared/client/debugger-client");
 
-// Open up a browser toolbox and return a ToolboxTask object for interacting
-// with it. ToolboxTask has the following methods:
-//
-// importFunctions(object)
-//
-//   The object contains functions from this process which should be defined in
-//   the global evaluation scope of the toolbox. The toolbox cannot load testing
-//   files directly.
-//
-// spawn(arg, function)
-//
-//   Invoke the given function and argument within the global evaluation scope
-//   of the toolbox. The evaluation scope predefines the name "gToolbox" for the
-//   toolbox itself.
-//
-// destroy()
-//
-//   Destroy the browser toolbox and make sure it exits cleanly.
-async function initBrowserToolboxTask() {
+/**
+ * Open up a browser toolbox and return a ToolboxTask object for interacting
+ * with it. ToolboxTask has the following methods:
+ *
+ * importFunctions(object)
+ *
+ *   The object contains functions from this process which should be defined in
+ *   the global evaluation scope of the toolbox. The toolbox cannot load testing
+ *   files directly.
+ *
+ * spawn(arg, function)
+ *
+ *   Invoke the given function and argument within the global evaluation scope
+ *   of the toolbox. The evaluation scope predefines the name "gToolbox" for the
+ *   toolbox itself.
+ *
+ * destroy()
+ *
+ *   Destroy the browser toolbox and make sure it exits cleanly.
+ *
+ * @param {Object}:
+ *        - {Boolean} enableBrowserToolboxFission: pass true to enable the OBT.
+ */
+async function initBrowserToolboxTask({ enableBrowserToolboxFission } = {}) {
   await pushPref("devtools.chrome.enabled", true);
   await pushPref("devtools.debugger.remote-enabled", true);
   await pushPref("devtools.browser-toolbox.allow-unsafe-script", true);
   await pushPref("devtools.debugger.prompt-connection", false);
 
   // This rejection seems to affect all tests using the browser toolbox.
   ChromeUtils.import(
     "resource://testing-common/PromiseTestUtils.jsm"
@@ -66,39 +71,48 @@ async function initBrowserToolboxTask() 
   ok(true, "Got transport");
 
   const client = new DebuggerClient(transport);
   await client.connect();
 
   ok(true, "Connected");
 
   const target = await client.mainRoot.getMainProcess();
-  const console = await target.getFront("console");
+  const consoleFront = await target.getFront("console");
+  const preferenceFront = await client.mainRoot.getFront("preference");
+
+  if (enableBrowserToolboxFission) {
+    await preferenceFront.setBoolPref("devtools.browsertoolbox.fission", true);
+  }
 
   async function spawn(arg, fn) {
-    const rv = await console.evaluateJSAsync(`(${fn})(${arg})`, {
+    const rv = await consoleFront.evaluateJSAsync(`(${fn})(${arg})`, {
       mapped: { await: true },
     });
     if (rv.exception) {
       throw new Error(`ToolboxTask.spawn failure: ${rv.exception.message}`);
     } else if (rv.topLevelAwaitRejected) {
       throw new Error(`ToolboxTask.spawn await rejected`);
     }
     return rv.result;
   }
 
   async function importFunctions(functions) {
     for (const [key, fn] of Object.entries(functions)) {
-      await console.evaluateJSAsync(`this.${key} = ${fn}`);
+      await consoleFront.evaluateJSAsync(`this.${key} = ${fn}`);
     }
   }
 
+  async function importScript(script) {
+    await consoleFront.evaluateJSAsync(script);
+  }
+
   async function destroy() {
     const closePromise = process._dbgProcess.wait();
-    console.evaluateJSAsync("gToolbox.destroy()");
+    consoleFront.evaluateJSAsync("gToolbox.destroy()");
 
     const { exitCode } = await closePromise;
     ok(true, "Browser toolbox process closed");
 
     is(exitCode, 0, "The remote debugger process died cleanly");
 
     is(
       BrowserToolboxProcess.getBrowserToolboxSessionState(),
@@ -106,12 +120,13 @@ async function initBrowserToolboxTask() 
       "No session state after closing"
     );
 
     await client.close();
   }
 
   return {
     importFunctions,
+    importScript,
     spawn,
     destroy,
   };
 }
deleted file mode 100644
--- a/devtools/client/framework/test/test_browser_toolbox_debugger.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/* global toolbox, createDebuggerContext, waitForSources, testUrl,
-          waitForPaused, addBreakpoint, assertPausedLocation, stepIn,
-          findSource, removeBreakpoint, resume, selectSource */
-
-info(`START: ${new Error().lineNumber}`);
-
-(async function() {
-  Services.prefs.clearUserPref("devtools.debugger.tabs");
-  Services.prefs.clearUserPref("devtools.debugger.pending-selected-location");
-
-  info("Waiting for debugger load");
-  await toolbox.selectTool("jsdebugger");
-  const dbg = createDebuggerContext(toolbox);
-  const window = dbg.win;
-  const document = window.document;
-
-  await waitForSources(dbg, testUrl);
-
-  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 fileName = testUrl.match(/browser-toolbox-test.*\.js/)[0];
-
-  let script = [...document.querySelectorAll(".tree-node")].find(node => {
-    return node.textContent.includes(fileName);
-  });
-  script = script.querySelector(".node");
-  script.click();
-
-  const onPaused = waitForPaused(dbg);
-  await selectSource(dbg, fileName);
-  await addBreakpoint(dbg, fileName, 2);
-
-  await onPaused;
-
-  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);
-
-  info("Close the browser toolbox");
-  toolbox.destroy();
-})();