Bug 1512152 - Use threadFrontTest instead of initTestDebuggerServer when possible, r=ochameau.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 15 Nov 2019 19:16:10 +0000
changeset 502266 6b3e85d6a39fc22cfe75106920cd004d71c6b617
parent 502265 e61b2935e9d500263c9d73a5be6767499c3abb75
child 502267 224557872391ae18c2885ca91e98f07e3bb48983
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau
bugs1512152
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 1512152 - Use threadFrontTest instead of initTestDebuggerServer when possible, r=ochameau. Differential Revision: https://phabricator.services.mozilla.com/D52205
devtools/server/tests/unit/head_dbg.js
devtools/server/tests/unit/test_blackboxing-01.js
devtools/server/tests/unit/test_blackboxing-02.js
devtools/server/tests/unit/test_blackboxing-03.js
devtools/server/tests/unit/test_blackboxing-04.js
devtools/server/tests/unit/test_blackboxing-05.js
devtools/server/tests/unit/test_blackboxing-07.js
devtools/server/tests/unit/test_breakpoint-15.js
devtools/server/tests/unit/test_breakpoint-20.js
devtools/server/tests/unit/test_conditional_breakpoint-01.js
devtools/server/tests/unit/test_conditional_breakpoint-02.js
devtools/server/tests/unit/test_conditional_breakpoint-03.js
devtools/server/tests/unit/test_conditional_breakpoint-04.js
devtools/server/tests/unit/test_frameactor-01.js
devtools/server/tests/unit/test_frameactor-02.js
devtools/server/tests/unit/test_frameactor-03.js
devtools/server/tests/unit/test_frameactor-04.js
devtools/server/tests/unit/test_frameactor-05.js
devtools/server/tests/unit/test_frameactor_wasm-01.js
devtools/server/tests/unit/test_framearguments-01.js
devtools/server/tests/unit/test_framebindings-01.js
devtools/server/tests/unit/test_framebindings-02.js
devtools/server/tests/unit/test_framebindings-03.js
devtools/server/tests/unit/test_framebindings-04.js
devtools/server/tests/unit/test_framebindings-05.js
devtools/server/tests/unit/test_framebindings-06.js
devtools/server/tests/unit/test_framebindings-07.js
devtools/server/tests/unit/test_functiongrips-01.js
devtools/server/tests/unit/test_listsources-01.js
devtools/server/tests/unit/test_listsources-02.js
devtools/server/tests/unit/test_listsources-03.js
devtools/server/tests/unit/test_logpoint-01.js
devtools/server/tests/unit/test_logpoint-02.js
devtools/server/tests/unit/test_logpoint-03.js
devtools/server/tests/unit/test_longstringgrips-01.js
devtools/server/tests/unit/test_nesting-01.js
devtools/server/tests/unit/test_nesting-02.js
devtools/server/tests/unit/test_new_source-01.js
devtools/server/tests/unit/test_new_source-02.js
devtools/server/tests/unit/test_objectgrips-10.js
devtools/server/tests/unit/test_objectgrips-11.js
devtools/server/tests/unit/test_objectgrips-12.js
devtools/server/tests/unit/test_objectgrips-13.js
devtools/server/tests/unit/test_objectgrips-14.js
devtools/server/tests/unit/test_objectgrips-15.js
devtools/server/tests/unit/test_pause_exceptions-01.js
devtools/server/tests/unit/test_pause_exceptions-02.js
devtools/server/tests/unit/test_pauselifetime-01.js
devtools/server/tests/unit/test_pauselifetime-02.js
devtools/server/tests/unit/test_pauselifetime-03.js
devtools/server/tests/unit/test_pauselifetime-04.js
devtools/server/tests/unit/test_promise_state-01.js
devtools/server/tests/unit/test_promise_state-02.js
devtools/server/tests/unit/test_promise_state-03.js
devtools/server/tests/unit/test_source-01.js
devtools/server/tests/unit/test_source-02.js
devtools/server/tests/unit/test_symbols-01.js
devtools/server/tests/unit/test_symbols-02.js
devtools/server/tests/unit/test_threadlifetime-01.js
devtools/server/tests/unit/test_threadlifetime-02.js
devtools/server/tests/unit/test_threadlifetime-04.js
devtools/server/tests/unit/test_wasm_source-01.js
devtools/server/tests/unit/xpcshell.ini
--- a/devtools/server/tests/unit/head_dbg.js
+++ b/devtools/server/tests/unit/head_dbg.js
@@ -896,22 +896,26 @@ async function setupTestFromUrl(url) {
  *        Optional arguments to tweak test environment
  *        - JSPrincipal principal
  *          Principal to use for the debuggee. Defaults to systemPrincipal.
  *        - boolean doNotRunWorker
  *          If true, do not run this tests in worker debugger context. Defaults to false.
  *        - bool wantXrays
  *          Whether the debuggee wants Xray vision with respect to same-origin objects
  *          outside the sandbox. Defaults to true.
+ *        - bool waitForFinish
+ *          Whether to wait for a call to threadFrontTestFinished after the test
+ *          function finishes.
  */
 function threadFrontTest(test, options = {}) {
   const {
     principal = systemPrincipal,
     doNotRunWorker = false,
     wantXrays = true,
+    waitForFinish = false,
   } = options;
 
   async function runThreadFrontTestWithServer(server, test) {
     // Setup a server and connect a client to it.
     initTestDebuggerServer(server);
 
     // Create a custom debuggee and register it to the server.
     // We are using a custom Sandbox as debuggee. Create a new zone because
@@ -927,17 +931,28 @@ function threadFrontTest(test, options =
     // Attach to the fake tab target and retrieve the ThreadFront instance.
     // Automatically resume as the thread is paused by default after attach.
     const { targetFront, threadFront } = await attachTestTabAndResume(
       client,
       scriptName
     );
 
     // Run the test function
-    await test({ threadFront, debuggee, client, server, targetFront });
+    const args = { threadFront, debuggee, client, server, targetFront };
+    if (waitForFinish) {
+      // Use dispatchToMainThread so that the test function does not have to
+      // finish executing before the test itself finishes.
+      const promise = new Promise(
+        resolve => (threadFrontTestFinished = resolve)
+      );
+      Services.tm.dispatchToMainThread(() => test(args));
+      await promise;
+    } else {
+      await test(args);
+    }
 
     // Cleanup the client after the test ran
     await client.close();
 
     server.removeTestGlobal(debuggee);
 
     // Also cleanup the created server
     server.destroy();
@@ -949,8 +964,15 @@ function threadFrontTest(test, options =
 
     // Skip tests that fail in the worker context
     if (!doNotRunWorker) {
       dump(">>> Run thread front test against a worker DebuggerServer\n");
       await runThreadFrontTestWithServer(WorkerDebuggerServer, test);
     }
   };
 }
+
+// This callback is used in tandem with the waitForFinish option of
+// threadFrontTest to support thread front tests that use promises to
+// asynchronously finish the tests, instead of using async/await.
+// Newly written tests should avoid using this. See bug 1596114 for migrating
+// existing tests to async/await and removing this functionality.
+let threadFrontTestFinished;
--- a/devtools/server/tests/unit/test_blackboxing-01.js
+++ b/devtools/server/tests/unit/test_blackboxing-01.js
@@ -3,35 +3,25 @@
 
 "use strict";
 
 /**
  * Test basic black boxing.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-black-box");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-black-box", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
-      gThreadFront = threadFront;
-      testBlackBox();
-    });
-  });
-  do_test_pending();
-}
+add_task(
+  threadFrontTest(async ({ threadFront, debuggee }) => {
+    gThreadFront = threadFront;
+    gDebuggee = debuggee;
+    await testBlackBox();
+  })
+);
 
 const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
 const SOURCE_URL = "http://example.com/source.js";
 
 const testBlackBox = async function() {
   const packet = await executeOnNextTickAndWaitForPause(evalCode, gThreadFront);
 
   const bpSource = await getSourceById(gThreadFront, packet.frame.where.actor);
@@ -99,18 +89,16 @@ const testBlackBox = async function() {
     },
     async function onDebuggerStatementFrames(frames) {
       for (const frame of frames) {
         const source = await getSourceFormById(gThreadFront, frame.where.actor);
         Assert.ok(!source.isBlackBoxed);
       }
     }
   );
-
-  finishClient(gClient);
 };
 
 function evalCode() {
   /* eslint-disable */
   Cu.evalInSandbox(
     "" + function doStuff(k) { // line 1
       var arg = 15;            // line 2 - Step in here
       k(arg);                  // line 3
--- a/devtools/server/tests/unit/test_blackboxing-02.js
+++ b/devtools/server/tests/unit/test_blackboxing-02.js
@@ -7,35 +7,28 @@
 
 /**
  * Test that we don't hit breakpoints in black boxed sources, and that when we
  * unblack box the source again, the breakpoint hasn't disappeared and we will
  * hit it again.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-black-box");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-black-box", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_black_box();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
 const SOURCE_URL = "http://example.com/source.js";
 
 function test_black_box() {
   gThreadFront.once("paused", async function(packet) {
     gThreadFront.setBreakpoint({ sourceUrl: BLACK_BOXED_URL, line: 2 }, {});
     gThreadFront.resume().then(test_black_box_breakpoint);
@@ -102,14 +95,14 @@ async function test_unblack_box_breakpoi
       "breakpoint",
       "We should hit the breakpoint again"
     );
 
     // We will hit the debugger statement on resume, so do this
     // nastiness to skip over it.
     gThreadFront.once("paused", async () => {
       await gThreadFront.resume();
-      finishClient(gClient);
+      threadFrontTestFinished();
     });
     await gThreadFront.resume();
   });
   gDebuggee.runTest();
 }
--- a/devtools/server/tests/unit/test_blackboxing-03.js
+++ b/devtools/server/tests/unit/test_blackboxing-03.js
@@ -4,35 +4,28 @@
 
 "use strict";
 
 /**
  * Test that we don't stop at debugger statements inside black boxed sources.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-black-box");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-black-box", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_black_box();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
 const SOURCE_URL = "http://example.com/source.js";
 
 function test_black_box() {
   gThreadFront.once("paused", async function(packet) {
     const source = await getSourceById(gThreadFront, packet.frame.where.actor);
     gThreadFront.setBreakpoint({ sourceUrl: source.url, line: 4 }, {});
@@ -97,12 +90,12 @@ async function test_unblack_box_dbg_stat
 
   gThreadFront.once("paused", async function(packet) {
     Assert.equal(
       packet.why.type,
       "debuggerStatement",
       "We should stop at the debugger statement again"
     );
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
   gDebuggee.runTest();
 }
--- a/devtools/server/tests/unit/test_blackboxing-04.js
+++ b/devtools/server/tests/unit/test_blackboxing-04.js
@@ -4,35 +4,28 @@
 
 "use strict";
 
 /**
  * Test behavior of blackboxing sources we are currently paused in.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-black-box");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-black-box", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_black_box();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
 const SOURCE_URL = "http://example.com/source.js";
 
 function test_black_box() {
   gThreadFront.once("paused", function(packet) {
     gThreadFront.setBreakpoint({ sourceUrl: BLACK_BOXED_URL, line: 2 }, {});
     test_black_box_paused();
@@ -81,11 +74,11 @@ function test_black_box_paused() {
     );
 
     const pausedInSource = await blackBox(sourceFront);
     Assert.ok(
       pausedInSource,
       "We should be notified that we are currently paused in this source"
     );
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 }
--- a/devtools/server/tests/unit/test_blackboxing-05.js
+++ b/devtools/server/tests/unit/test_blackboxing-05.js
@@ -4,40 +4,28 @@
 
 "use strict";
 
 /**
  * Test exceptions inside black boxed sources.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-black-box");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-black-box", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
-      // XXX: We have to do an executeSoon so that the error isn't caught and
-      // reported by DebuggerClient.requester (because we are using the local
-      // transport and share a stack) which causes the test to fail.
-      Services.tm.dispatchToMainThread({
-        run: test_black_box,
-      });
-    });
-  });
-  do_test_pending();
-}
+      gDebuggee = debuggee;
+      test_black_box();
+    },
+    { waitForFinish: true }
+  )
+);
 
 const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
 const SOURCE_URL = "http://example.com/source.js";
 
 function test_black_box() {
   gThreadFront.once("paused", test_black_box_exception);
 
   /* eslint-disable no-multi-spaces, no-unreachable, no-undef */
@@ -90,14 +78,14 @@ function test_black_box_exception() {
       );
 
       Assert.equal(
         source.url,
         SOURCE_URL,
         "We shouldn't pause while in the black boxed source."
       );
       await gThreadFront.resume();
-      finishClient(gClient);
+      threadFrontTestFinished();
     });
 
     gThreadFront.resume();
   });
 }
--- a/devtools/server/tests/unit/test_blackboxing-07.js
+++ b/devtools/server/tests/unit/test_blackboxing-07.js
@@ -4,35 +4,25 @@
 "use strict";
 
 /**
  * Test that sources whose URL ends with ".min.js" automatically get black
  * boxed.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-black-box");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-black-box", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
-      gThreadFront = threadFront;
-      testBlackBox();
-    });
-  });
-  do_test_pending();
-}
+add_task(
+  threadFrontTest(async ({ threadFront, debuggee }) => {
+    gThreadFront = threadFront;
+    gDebuggee = debuggee;
+    await testBlackBox();
+  })
+);
 
 const BLACK_BOXED_URL = "http://example.com/black-boxed.min.js";
 const SOURCE_URL = "http://example.com/source.js";
 
 const testBlackBox = async function() {
   await executeOnNextTickAndWaitForPause(evalCode, gThreadFront);
 
   const { sources } = await getSources(gThreadFront);
@@ -40,17 +30,16 @@ const testBlackBox = async function() {
 
   const blackBoxedSource = sources.filter(s => s.url === BLACK_BOXED_URL)[0];
   equal(blackBoxedSource.isBlackBoxed, true);
 
   const regularSource = sources.filter(s => s.url === SOURCE_URL)[0];
   equal(regularSource.isBlackBoxed, false);
 
   await gThreadFront.resume();
-  finishClient(gClient);
 };
 
 function evalCode() {
   Cu.evalInSandbox(
     "" + function blackBoxed() {},
     gDebuggee,
     "1.8",
     BLACK_BOXED_URL,
deleted file mode 100644
--- a/devtools/server/tests/unit/test_breakpoint-15.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-/**
- * Check that adding a breakpoint in the same place returns the same actor.
- */
-
-var gDebuggee;
-var gClient;
-var gThreadFront;
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
-      gThreadFront = threadFront;
-      testSameBreakpoint();
-    });
-  });
-  do_test_pending();
-}
-
-const SOURCE_URL = "http://example.com/source.js";
-
-const testSameBreakpoint = async function() {
-  const packet = await executeOnNextTickAndWaitForPause(evalCode, gThreadFront);
-  const source = await getSourceById(gThreadFront, packet.frame.where.actor);
-
-  // Whole line
-  const wholeLineLocation = {
-    line: 2,
-  };
-
-  let [, firstBpClient] = await setBreakpoint(source, wholeLineLocation);
-  let [, secondBpClient] = await setBreakpoint(source, wholeLineLocation);
-
-  Assert.equal(
-    firstBpClient.actor,
-    secondBpClient.actor,
-    "Should get the same actor w/ whole line breakpoints"
-  );
-
-  // Specific column
-
-  const columnLocation = {
-    line: 2,
-    column: 6,
-  };
-
-  [, firstBpClient] = await setBreakpoint(source, columnLocation);
-  [, secondBpClient] = await setBreakpoint(source, columnLocation);
-
-  Assert.equal(
-    secondBpClient.actor,
-    secondBpClient.actor,
-    "Should get the same actor column breakpoints"
-  );
-
-  finishClient(gClient);
-};
-
-function evalCode() {
-  /* eslint-disable */
-  Cu.evalInSandbox(
-    "" + function doStuff(k) { // line 1
-      let arg = 15;            // line 2 - Step in here
-      k(arg);                  // line 3
-    } + "\n"                   // line 4
-    + "debugger;",             // line 5
-    gDebuggee,
-    "1.8",
-    SOURCE_URL,
-    1
-  );
-  /* eslint-enable */
-}
--- a/devtools/server/tests/unit/test_breakpoint-20.js
+++ b/devtools/server/tests/unit/test_breakpoint-20.js
@@ -4,52 +4,41 @@
 "use strict";
 
 /**
  * Verify that when two of the "same" source are loaded concurrently (like e10s
  * frame scripts), breakpoints get hit in scripts defined by all sources.
  */
 
 var gDebuggee;
-var gClient;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-breakpoints");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestThread(gClient, "test-breakpoints", testBreakpoint);
-  });
-  do_test_pending();
-}
+add_task(
+  threadFrontTest(async ({ threadFront, debuggee }) => {
+    gDebuggee = debuggee;
+    await testBreakpoint(threadFront);
+  })
+);
 
-const testBreakpoint = async function(
-  threadResponse,
-  targetFront,
-  threadFront,
-  tabResponse
-) {
+const testBreakpoint = async function(threadFront) {
   evalSetupCode();
 
   // Load the test source once.
 
   evalTestCode();
   equal(
     gDebuggee.functions.length,
     1,
     "The test code should have added a function."
   );
 
   // Set a breakpoint in the test source.
 
   const source = await getSource(threadFront, "test.js");
   setBreakpoint(threadFront, { sourceUrl: source.url, line: 3 });
 
-  await resume(threadFront);
-
   // Load the test source again.
 
   evalTestCode();
   equal(
     gDebuggee.functions.length,
     2,
     "The test code should have added another function."
   );
@@ -93,18 +82,16 @@ const testBreakpoint = async function(
     () => resume(threadFront),
     threadFront
   );
   equal(
     dbgStmtPause2.why.type,
     "debuggerStatement",
     "And we should hit the debugger statement after the pause."
   );
-
-  finishClient(gClient);
 };
 
 function evalSetupCode() {
   Cu.evalInSandbox("this.functions = [];", gDebuggee, "1.8", "setup.js", 1);
 }
 
 function evalTestCode() {
   Cu.evalInSandbox(
--- a/devtools/server/tests/unit/test_conditional_breakpoint-01.js
+++ b/devtools/server/tests/unit/test_conditional_breakpoint-01.js
@@ -4,35 +4,28 @@
 
 "use strict";
 
 /**
  * Check conditional breakpoint when condition evaluates to true.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-conditional-breakpoint");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-conditional-breakpoint", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_simple_breakpoint();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_simple_breakpoint() {
   let hitBreakpoint = false;
 
   gThreadFront.once("paused", async function(packet) {
     const source = await getSourceById(gThreadFront, packet.frame.where.actor);
     const location = { sourceUrl: source.url, line: 3 };
     gThreadFront.setBreakpoint(location, { condition: "a === 1" });
@@ -43,17 +36,17 @@ function test_simple_breakpoint() {
       // Check the return value.
       Assert.equal(packet.why.type, "breakpoint");
       Assert.equal(packet.frame.where.line, 3);
 
       // Remove the breakpoint.
       gThreadFront.removeBreakpoint(location);
 
       gThreadFront.resume().then(function() {
-        finishClient(gClient);
+        threadFrontTestFinished();
       });
     });
 
     // Continue until the breakpoint is hit.
     gThreadFront.resume();
   });
 
   /* eslint-disable */
--- a/devtools/server/tests/unit/test_conditional_breakpoint-02.js
+++ b/devtools/server/tests/unit/test_conditional_breakpoint-02.js
@@ -4,35 +4,28 @@
 
 "use strict";
 
 /**
  * Check conditional breakpoint when condition evaluates to false.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-conditional-breakpoint");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-conditional-breakpoint", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_simple_breakpoint();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_simple_breakpoint() {
   gThreadFront.once("paused", async function(packet) {
     const source = await getSourceById(gThreadFront, packet.frame.where.actor);
     const location1 = { sourceUrl: source.url, line: 3 };
     gThreadFront.setBreakpoint(location1, { condition: "a === 2" });
     const location2 = { sourceUrl: source.url, line: 4 };
     gThreadFront.setBreakpoint(location2, { condition: "a === 1" });
@@ -40,17 +33,17 @@ function test_simple_breakpoint() {
       // Check the return value.
       Assert.equal(packet.why.type, "breakpoint");
       Assert.equal(packet.frame.where.line, 4);
 
       // Remove the breakpoint.
       gThreadFront.removeBreakpoint(location2);
 
       gThreadFront.resume().then(function() {
-        finishClient(gClient);
+        threadFrontTestFinished();
       });
     });
 
     // Continue until the breakpoint is hit.
     gThreadFront.resume();
   });
 
   /* eslint-disable */
--- a/devtools/server/tests/unit/test_conditional_breakpoint-03.js
+++ b/devtools/server/tests/unit/test_conditional_breakpoint-03.js
@@ -5,35 +5,28 @@
 "use strict";
 
 /**
  * If pauseOnExceptions is checked, when condition throws,
  * make sure conditional breakpoint pauses.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-conditional-breakpoint");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-conditional-breakpoint", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_simple_breakpoint();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_simple_breakpoint() {
   gThreadFront.once("paused", async function(packet) {
     const source = await getSourceById(gThreadFront, packet.frame.where.actor);
 
     gThreadFront.pauseOnExceptions(true, false);
     const location = { sourceUrl: source.url, line: 3 };
     gThreadFront.setBreakpoint(location, { condition: "throw new Error()" });
@@ -49,17 +42,17 @@ function test_simple_breakpoint() {
       // Check the return value.
       Assert.equal(packet.why.type, "breakpointConditionThrown");
       Assert.equal(packet.frame.where.line, 3);
 
       // Remove the breakpoint.
       gThreadFront.removeBreakpoint(location);
 
       gThreadFront.resume().then(function() {
-        finishClient(gClient);
+        threadFrontTestFinished();
       });
     });
 
     // Continue until the breakpoint is hit.
     gThreadFront.resume();
   });
 
   /* eslint-disable */
--- a/devtools/server/tests/unit/test_conditional_breakpoint-04.js
+++ b/devtools/server/tests/unit/test_conditional_breakpoint-04.js
@@ -6,35 +6,28 @@
 
 /**
  * Confirm that we ignore breakpoint condition exceptions
  * unless pause-on-exceptions is set to true.
  *
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-conditional-breakpoint");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-conditional-breakpoint", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_simple_breakpoint();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 async function test_simple_breakpoint() {
   await gThreadFront.setBreakpoint(
     { sourceUrl: "conditional_breakpoint-04.js", line: 3 },
     { condition: "throw new Error()" }
   );
 
   gThreadFront.once("paused", async function(packet) {
@@ -47,17 +40,17 @@ async function test_simple_breakpoint() 
     Assert.equal(pausedPacket.why.type, "debuggerStatement");
 
     // Remove the breakpoint.
     await gThreadFront.removeBreakpoint({
       sourceUrl: "conditional_breakpoint-04.js",
       line: 3,
     });
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   /* eslint-disable */
   Cu.evalInSandbox(
     `debugger;
     var a = 1;
     var b = 2;
     debugger;`,
--- a/devtools/server/tests/unit/test_frameactor-01.js
+++ b/devtools/server/tests/unit/test_frameactor-01.js
@@ -3,47 +3,36 @@
 
 "use strict";
 
 /**
  * Verify that we get a frame actor along with a debugger statement.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", function(packet) {
     Assert.ok(!!packet.frame);
     Assert.ok(!!packet.frame.actor);
     Assert.equal(packet.frame.displayName, "stopMe");
     gThreadFront.resume().then(function() {
-      finishClient(gClient);
+      threadFrontTestFinished();
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe() {
           debugger;
--- a/devtools/server/tests/unit/test_frameactor-02.js
+++ b/devtools/server/tests/unit/test_frameactor-02.js
@@ -4,46 +4,35 @@
 
 "use strict";
 
 /**
  * Verify that two pauses in a row will keep the same frame actor.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", function(packet1) {
     gThreadFront.once("paused", function(packet2) {
       Assert.equal(packet1.frame.actor, packet2.frame.actor);
       gThreadFront.resume().then(function() {
-        finishClient(gClient);
+        threadFrontTestFinished();
       });
     });
     gThreadFront.resume();
   });
 
   gDebuggee.eval(
     "(" +
       function() {
--- a/devtools/server/tests/unit/test_frameactor-03.js
+++ b/devtools/server/tests/unit/test_frameactor-03.js
@@ -4,48 +4,37 @@
 
 "use strict";
 
 /**
  * Verify that a frame actor is properly expired when the frame goes away.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", function(packet1) {
     gThreadFront.once("paused", function(packet2) {
       const poppedFrames = packet2.poppedFrames;
       Assert.equal(typeof poppedFrames, typeof []);
       Assert.ok(poppedFrames.includes(packet1.frame.actorID));
       gThreadFront.resume().then(function() {
-        finishClient(gClient);
+        threadFrontTestFinished();
       });
     });
     gThreadFront.resume();
   });
 
   gDebuggee.eval(
     "(" +
       function() {
--- a/devtools/server/tests/unit/test_frameactor-04.js
+++ b/devtools/server/tests/unit/test_frameactor-04.js
@@ -3,39 +3,28 @@
 
 "use strict";
 
 /**
  * Verify the "frames" request on the thread.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 var frameFixtures = [
   // Function calls...
   { type: "call", displayName: "depth3" },
   { type: "call", displayName: "depth2" },
   { type: "call", displayName: "depth1" },
 
   // Anonymous function call in our eval...
@@ -51,17 +40,17 @@ async function test_frame_packet() {
     const expected = frameFixtures[i];
     const actual = response.frames[i];
 
     Assert.equal(expected.displayname, actual.displayname, "Frame displayname");
     Assert.equal(expected.type, actual.type, "Frame displayname");
   }
 
   await gThreadFront.resume();
-  await finishClient(gClient);
+  threadFrontTestFinished();
 }
 
 function test_pause_frame() {
   gThreadFront.once("paused", function(packet) {
     test_frame_packet();
   });
 
   gDebuggee.eval(
--- a/devtools/server/tests/unit/test_frameactor-05.js
+++ b/devtools/server/tests/unit/test_frameactor-05.js
@@ -5,39 +5,28 @@
 "use strict";
 
 /**
  * Verify that frame actors retrieved with the frames request
  * are included in the pause packet's popped-frames property.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", function(packet) {
     gThreadFront.getFrames(0, null).then(function(frameResponse) {
       Assert.equal(frameResponse.frames.length, 5);
       // Now wait for the next pause, after which the three
       // youngest actors should be popped..
       const expectPopped = frameResponse.frames
@@ -47,17 +36,17 @@ function test_pause_frame() {
 
       gThreadFront.once("paused", function(pausePacket) {
         const popped = pausePacket.poppedFrames.sort();
         Assert.equal(popped.length, 3);
         for (let i = 0; i < 3; i++) {
           Assert.equal(expectPopped[i], popped[i]);
         }
 
-        gThreadFront.resume().then(() => finishClient(gClient));
+        gThreadFront.resume().then(() => threadFrontTestFinished());
       });
       gThreadFront.resume();
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
--- a/devtools/server/tests/unit/test_frameactor_wasm-01.js
+++ b/devtools/server/tests/unit/test_frameactor_wasm-01.js
@@ -4,65 +4,49 @@
 
 "use strict";
 
 /**
  * Verify that wasm frame(s) can be requested from the client.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  if (typeof WebAssembly == "undefined") {
-    // wasm is not enabled for this platform
-    return;
-  }
-
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", async function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       await gThreadFront.reconfigure({
         observeAsmJS: true,
         wasmBinarySource: true,
       });
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", function(packet) {
     gThreadFront.getFrames(0, null).then(async function(frameResponse) {
       Assert.equal(frameResponse.frames.length, 4);
 
       const wasmFrame = frameResponse.frames[1];
       Assert.equal(wasmFrame.type, "wasmcall");
       Assert.equal(wasmFrame.this, undefined);
 
       const location = wasmFrame.where;
       const source = await getSourceById(gThreadFront, location.actor);
       Assert.equal(location.line > 0, true);
       Assert.equal(location.column > 0, true);
       Assert.equal(/^wasm:(?:[^:]*:)*?[0-9a-f]{16}$/.test(source.url), true);
 
-      finishClient(gClient);
+      threadFrontTestFinished();
     });
   });
 
   /* eslint-disable comma-spacing, max-len */
   gDebuggee.eval(
     "(" +
       function() {
         // WebAssembly bytecode was generated by running:
--- a/devtools/server/tests/unit/test_framearguments-01.js
+++ b/devtools/server/tests/unit/test_framearguments-01.js
@@ -3,55 +3,44 @@
 
 "use strict";
 
 /**
  * Check a frame actor's arguments property.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", function(packet) {
     const args = packet.frame.arguments;
     Assert.equal(args.length, 6);
     Assert.equal(args[0], 42);
     Assert.equal(args[1], true);
     Assert.equal(args[2], "nasu");
     Assert.equal(args[3].type, "null");
     Assert.equal(args[4].type, "undefined");
     Assert.equal(args[5].type, "object");
     Assert.equal(args[5].class, "Object");
     Assert.ok(!!args[5].actor);
 
     gThreadFront.resume().then(function() {
-      finishClient(gClient);
+      threadFrontTestFinished();
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe(number, bool, string, null_, undef, object) {
           debugger;
--- a/devtools/server/tests/unit/test_framebindings-01.js
+++ b/devtools/server/tests/unit/test_framebindings-01.js
@@ -3,41 +3,28 @@
 
 "use strict";
 
 /**
  * Check a frame actor's bindings property.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", async function(packet) {
     const bindings = packet.frame.environment.bindings;
     const args = bindings.arguments;
     const vars = bindings.variables;
 
     Assert.equal(args.length, 6);
@@ -65,17 +52,17 @@ function test_pause_frame() {
 
     Assert.equal(response.ownProperties.b.configurable, true);
     Assert.equal(response.ownProperties.b.enumerable, true);
     Assert.equal(response.ownProperties.b.writable, true);
     Assert.equal(response.ownProperties.b.value.type, "undefined");
     Assert.equal(false, "class" in response.ownProperties.b.value);
 
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   /* eslint-disable */
   gDebuggee.eval("(" + function () {
     function stopMe(number, bool, string, null_, undef, object) {
       var a = 1;
       var b = true;
       var c = { a: "a", b: undefined };
--- a/devtools/server/tests/unit/test_framebindings-02.js
+++ b/devtools/server/tests/unit/test_framebindings-02.js
@@ -3,41 +3,28 @@
 
 "use strict";
 
 /**
  * Check a frame actor's parent bindings.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", async function(packet) {
     let parentEnv = packet.frame.environment.parent;
     const bindings = parentEnv.bindings;
     const args = bindings.arguments;
     const vars = bindings.variables;
     Assert.notEqual(parentEnv, undefined);
@@ -51,17 +38,17 @@ function test_pause_frame() {
     Assert.notEqual(parentEnv, undefined);
     const objClient = gThreadFront.pauseGrip(parentEnv.object);
     const response = await objClient.getPrototypeAndProperties();
     Assert.equal(response.ownProperties.Object.value.type, "object");
     Assert.equal(response.ownProperties.Object.value.class, "Function");
     Assert.ok(!!response.ownProperties.Object.value.actor);
 
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   /* eslint-disable */
   gDebuggee.eval("(" + function () {
     function stopMe(number, bool, string, null_, undef, object) {
       var a = 1;
       var b = true;
       var c = { a: "a" };
--- a/devtools/server/tests/unit/test_framebindings-03.js
+++ b/devtools/server/tests/unit/test_framebindings-03.js
@@ -4,41 +4,28 @@
 /* strict mode code may not contain 'with' statements */
 /* eslint-disable strict */
 
 /**
  * Check a |with| frame actor's bindings.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", async function(packet) {
     const env = packet.frame.environment;
     Assert.notEqual(env, undefined);
 
     const parentEnv = env.parent;
     Assert.notEqual(parentEnv, undefined);
@@ -56,17 +43,17 @@ function test_pause_frame() {
     const objClient = gThreadFront.pauseGrip(env.object);
     const response = await objClient.getPrototypeAndProperties();
     Assert.equal(response.ownProperties.PI.value, Math.PI);
     Assert.equal(response.ownProperties.cos.value.type, "object");
     Assert.equal(response.ownProperties.cos.value.class, "Function");
     Assert.ok(!!response.ownProperties.cos.value.actor);
 
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   /* eslint-disable */
   gDebuggee.eval("(" + function () {
     function stopMe(number) {
       var a;
       var r = number;
       with (Math) {
--- a/devtools/server/tests/unit/test_framebindings-04.js
+++ b/devtools/server/tests/unit/test_framebindings-04.js
@@ -5,41 +5,28 @@
 /* strict mode code may not contain 'with' statements */
 /* eslint-disable strict */
 
 /**
  * Check the environment bindings of a  |with| within a |with|.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", async function(packet) {
     const env = packet.frame.environment;
     Assert.notEqual(env, undefined);
 
     const objClient = gThreadFront.pauseGrip(env.object);
     let response = await objClient.getPrototypeAndProperties();
@@ -67,17 +54,17 @@ function test_pause_frame() {
     Assert.equal(args[0].number.value, 10);
     Assert.equal(vars.r.value, 10);
     Assert.equal(vars.a.value, Math.PI * 100);
     Assert.equal(vars.arguments.value.class, "Arguments");
     Assert.ok(!!vars.arguments.value.actor);
     Assert.equal(vars.foo.value, 2 * Math.PI);
 
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   /* eslint-disable */
   gDebuggee.eval("(" + function () {
     function stopMe(number) {
       var a, obj = { one: 1, two: 2 };
       var r = number;
       with (Math) {
--- a/devtools/server/tests/unit/test_framebindings-05.js
+++ b/devtools/server/tests/unit/test_framebindings-05.js
@@ -4,41 +4,28 @@
 
 "use strict";
 
 /**
  * Check the environment bindings of a |with| in global scope.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", async function(packet) {
     const env = packet.frame.environment;
     Assert.notEqual(env, undefined);
 
     const objClient = gThreadFront.pauseGrip(env.object);
     let response = await objClient.getPrototypeAndProperties();
@@ -55,17 +42,17 @@ function test_pause_frame() {
     response = await parentClient.getPrototypeAndProperties();
     Assert.equal(response.ownProperties.a.value, Math.PI * 100);
     Assert.equal(response.ownProperties.r.value, 10);
     Assert.equal(response.ownProperties.Object.value.type, "object");
     Assert.equal(response.ownProperties.Object.value.class, "Function");
     Assert.ok(!!response.ownProperties.Object.value.actor);
 
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   gDebuggee.eval(
     "var a, r = 10;\n" +
       "with (Math) {\n" +
       "  a = PI * r * r;\n" +
       "  debugger;\n" +
       "}"
--- a/devtools/server/tests/unit/test_framebindings-06.js
+++ b/devtools/server/tests/unit/test_framebindings-06.js
@@ -1,40 +1,26 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_banana_environment();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_banana_environment() {
   gThreadFront.once("paused", async function(packet) {
     const env = packet.frame.environment;
     equal(env.type, "function");
     equal(env.function.name, "banana3");
     let parent = env.parent;
     equal(parent.type, "block");
@@ -45,17 +31,17 @@ function test_banana_environment() {
     parent = parent.parent;
     equal(parent.type, "block");
     ok("banana2" in parent.bindings.variables);
     parent = parent.parent;
     equal(parent.type, "function");
     equal(parent.function.name, "banana");
 
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   gDebuggee.eval(
     "function banana(x) {\n" +
       "  return function banana2(y) {\n" +
       "    return function banana3(z) {\n" +
       '      eval("");\n' +
       "      debugger;\n" +
--- a/devtools/server/tests/unit/test_framebindings-07.js
+++ b/devtools/server/tests/unit/test_framebindings-07.js
@@ -4,40 +4,27 @@
 
 "use strict";
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 const { EnvironmentFront } = require("devtools/shared/fronts/environment");
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-// Test that the EnvironmentFront's getBindings() method works as expected.
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-bindings");
-
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-bindings", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
+      gClient = client;
       test_banana_environment();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_banana_environment() {
   gThreadFront.once("paused", async function(packet) {
     const environment = packet.frame.environment;
     Assert.equal(environment.type, "function");
 
     const parent = environment.parent;
     Assert.equal(parent.type, "block");
@@ -52,17 +39,17 @@ function test_banana_environment() {
     const parentClient = new EnvironmentFront(gClient, parent);
     response = await parentClient.getBindings();
     Assert.equal(response.variables.banana3.value.class, "Function");
 
     const grandpaClient = new EnvironmentFront(gClient, grandpa);
     response = await grandpaClient.getBindings();
     Assert.equal(response.arguments[0].y.value, "y");
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   gDebuggee.eval(
     "function banana(x) {\n" +
       "  return function banana2(y) {\n" +
       "    return function banana3(z) {\n" +
       '      eval("");\n' +
       "      debugger;\n" +
--- a/devtools/server/tests/unit/test_functiongrips-01.js
+++ b/devtools/server/tests/unit/test_functiongrips-01.js
@@ -1,43 +1,26 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-  gDebuggee.eval(
-    function stopMe(arg1) {
-      debugger;
-    }.toString()
-  );
-
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_named_function();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_named_function() {
   gThreadFront.once("paused", async function(packet) {
     const args = packet.frame.arguments;
 
     Assert.equal(args[0].class, "Function");
     Assert.equal(args[0].name, "stopMe");
     Assert.equal(args[0].displayName, "stopMe");
@@ -46,16 +29,21 @@ function test_named_function() {
     const response = await objClient.getParameterNames();
     Assert.equal(response.parameterNames.length, 1);
     Assert.equal(response.parameterNames[0], "arg1");
 
     await gThreadFront.resume();
     test_inferred_name_function();
   });
 
+  gDebuggee.eval(
+    function stopMe(arg1) {
+      debugger;
+    }.toString()
+  );
   gDebuggee.eval("stopMe(stopMe)");
 }
 
 function test_inferred_name_function() {
   gThreadFront.once("paused", async function(packet) {
     const args = packet.frame.arguments;
 
     Assert.equal(args[0].class, "Function");
@@ -89,13 +77,13 @@ function test_anonymous_function() {
     const objClient = gThreadFront.pauseGrip(args[0]);
     const response = await objClient.getParameterNames();
     Assert.equal(response.parameterNames.length, 3);
     Assert.equal(response.parameterNames[0], "foo");
     Assert.equal(response.parameterNames[1], "bar");
     Assert.equal(response.parameterNames[2], "baz");
 
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   gDebuggee.eval("stopMe(function(foo, bar, baz) { })");
 }
--- a/devtools/server/tests/unit/test_listsources-01.js
+++ b/devtools/server/tests/unit/test_listsources-01.js
@@ -1,47 +1,39 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
  * Check basic getSources functionality.
  */
-
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
 var gNumTimesSourcesSent = 0;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.request = (function(origRequest) {
-    return function(request, onResponse) {
-      if (request.type === "sources") {
-        ++gNumTimesSourcesSent;
-      }
-      return origRequest.call(this, request, onResponse);
-    };
-  })(gClient.request);
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
+      client.request = (function(origRequest) {
+        return function(request, onResponse) {
+          if (request.type === "sources") {
+            ++gNumTimesSourcesSent;
+          }
+          return origRequest.call(this, request, onResponse);
+        };
+      })(client.request);
       test_simple_listsources();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_simple_listsources() {
   gThreadFront.once("paused", function(packet) {
     gThreadFront.getSources().then(function(response) {
       Assert.ok(
         response.sources.some(function(s) {
           return s.url && s.url.match(/test_listsources-01.js/);
         })
@@ -49,17 +41,17 @@ function test_simple_listsources() {
 
       Assert.ok(
         gNumTimesSourcesSent <= 1,
         "Should only send one sources request at most, even though we" +
           " might have had to send one to determine feature support."
       );
 
       gThreadFront.resume().then(function() {
-        finishClient(gClient);
+        threadFrontTestFinished();
       });
     });
   });
 
   /* eslint-disable */
   Cu.evalInSandbox("var line0 = Error().lineNumber;\n" +
                   "debugger;\n" +   // line0 + 1
                   "var a = 1;\n" +  // line0 + 2
--- a/devtools/server/tests/unit/test_listsources-02.js
+++ b/devtools/server/tests/unit/test_listsources-02.js
@@ -2,53 +2,45 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
  * Check getting sources before there are any.
  */
 
-var gClient;
 var gThreadFront;
 
 var gNumTimesSourcesSent = 0;
 
-function run_test() {
-  initTestDebuggerServer();
-  addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.request = (function(origRequest) {
-    return function(request, onResponse) {
-      if (request.type === "sources") {
-        ++gNumTimesSourcesSent;
-      }
-      return origRequest.call(this, request, onResponse);
-    };
-  })(gClient.request);
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      client.request = (function(origRequest) {
+        return function(request, onResponse) {
+          if (request.type === "sources") {
+            ++gNumTimesSourcesSent;
+          }
+          return origRequest.call(this, request, onResponse);
+        };
+      })(client.request);
       test_listing_zero_sources();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_listing_zero_sources() {
   gThreadFront.getSources().then(function(packet) {
     Assert.ok(!packet.error);
     Assert.ok(!!packet.sources);
     Assert.equal(packet.sources.length, 0);
 
     Assert.ok(
       gNumTimesSourcesSent <= 1,
       "Should only send one sources request at most, even though we" +
         " might have had to send one to determine feature support."
     );
 
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 }
--- a/devtools/server/tests/unit/test_listsources-03.js
+++ b/devtools/server/tests/unit/test_listsources-03.js
@@ -3,56 +3,45 @@
 
 "use strict";
 
 /**
  * Check getSources functionality when there are lots of sources.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-sources");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-sources", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_simple_listsources();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_simple_listsources() {
   gThreadFront.once("paused", function(packet) {
     gThreadFront.getSources().then(function(response) {
       Assert.ok(
         !response.error,
         "There shouldn't be an error fetching large amounts of sources."
       );
 
       Assert.ok(
         response.sources.some(function(s) {
           return s.url.match(/foo-999.js$/);
         })
       );
 
       gThreadFront.resume().then(function() {
-        finishClient(gClient);
+        threadFrontTestFinished();
       });
     });
   });
 
   for (let i = 0; i < 1000; i++) {
     Cu.evalInSandbox(
       "function foo###() {return ###;}".replace(/###/g, i),
       gDebuggee,
--- a/devtools/server/tests/unit/test_logpoint-01.js
+++ b/devtools/server/tests/unit/test_logpoint-01.js
@@ -7,32 +7,27 @@
 /**
  * Check that logpoints generate console messages.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-logpoint");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-logpoint", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
+      gClient = client;
       test_simple_breakpoint();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_simple_breakpoint() {
   const rootActor = gClient.transport._serverConnection.rootActor;
   const threadActor =
     rootActor._parameters.tabList._targetActors[0].threadActor;
 
   let lastMessage;
   threadActor._parent._consoleActor = {
@@ -53,17 +48,17 @@ function test_simple_breakpoint() {
       { logValue: "a" }
     );
     await gClient.waitForRequestsToSettle();
 
     // Execute the rest of the code.
     await gThreadFront.resume();
     Assert.equal(lastMessage.level, "logPoint");
     Assert.equal(lastMessage.arguments[0], "three");
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   /* eslint-disable */
   Cu.evalInSandbox("debugger;\n" + // 1
                    "var a = 'three';\n" +  // 2
                    "var b = 2;\n", // 3
                    gDebuggee,
                    "1.8",
--- a/devtools/server/tests/unit/test_logpoint-02.js
+++ b/devtools/server/tests/unit/test_logpoint-02.js
@@ -7,32 +7,27 @@
 /**
  * Check that conditions are respected when specified in a logpoint.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-logpoint");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-logpoint", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
+      gClient = client;
       test_simple_breakpoint();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_simple_breakpoint() {
   const rootActor = gClient.transport._serverConnection.rootActor;
   const threadActor =
     rootActor._parameters.tabList._targetActors[0].threadActor;
 
   let lastMessage;
   threadActor._parent._consoleActor = {
@@ -52,17 +47,17 @@ function test_simple_breakpoint() {
       },
       { logValue: "a", condition: "a === 5" }
     );
     await gClient.waitForRequestsToSettle();
 
     // Execute the rest of the code.
     await gThreadFront.resume();
     Assert.equal(lastMessage.arguments[0], 5);
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   /* eslint-disable */
   Cu.evalInSandbox("debugger;\n" + // 1
                    "var a = 1;\n" +  // 2
                    "while (a < 10) {\n" + // 3
                    "  a++;\n" + // 4
                    "}",
--- a/devtools/server/tests/unit/test_logpoint-03.js
+++ b/devtools/server/tests/unit/test_logpoint-03.js
@@ -7,32 +7,27 @@
 /**
  * Check that logpoints generate console errors if the logpoint statement is invalid.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-logpoint");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-logpoint", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
+      gClient = client;
       test_simple_breakpoint();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_simple_breakpoint() {
   const rootActor = gClient.transport._serverConnection.rootActor;
   const threadActor =
     rootActor._parameters.tabList._targetActors[0].threadActor;
 
   let lastMessage;
   threadActor._parent._consoleActor = {
@@ -52,17 +47,17 @@ function test_simple_breakpoint() {
       },
       { logValue: "c" }
     );
 
     // Execute the rest of the code.
     await gThreadFront.resume();
     Assert.equal(lastMessage.level, "logPointError");
     Assert.equal(lastMessage.arguments[0], "c is not defined");
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   /* eslint-disable */
   Cu.evalInSandbox(
     "debugger;\n" + // 1
     "var a = 'three';\n" + // 2
       "var b = 2;\n", // 3
     gDebuggee,
--- a/devtools/server/tests/unit/test_longstringgrips-01.js
+++ b/devtools/server/tests/unit/test_longstringgrips-01.js
@@ -2,44 +2,27 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-  gDebuggee.eval(
-    function stopMe(arg1) {
-      debugger;
-    }.toString()
-  );
-
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
+      gClient = client;
       test_longstring_grip();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_longstring_grip() {
   const longString =
     "All I want is to be a monkey of moderate intelligence who" +
     " wears a suit... that's why I'm transferring to business school! Maybe I" +
     " love you so much, I love you no matter who you are pretending to be." +
     " Enough about your promiscuous mother, Hermes! We have bigger problems." +
     " For example, if you killed your grandfather, you'd cease to exist! What" +
@@ -77,10 +60,16 @@ function test_longstring_grip() {
     } catch (error) {
       gThreadFront.resume().then(function() {
         finishClient(gClient);
         do_throw(error);
       });
     }
   });
 
+  gDebuggee.eval(
+    function stopMe(arg1) {
+      debugger;
+    }.toString()
+  );
+
   gDebuggee.eval('stopMe("' + longString + '")');
 }
--- a/devtools/server/tests/unit/test_nesting-01.js
+++ b/devtools/server/tests/unit/test_nesting-01.js
@@ -1,43 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that we can nest event loops when needed in
 // ThreadActor.prototype.unsafeSynchronize.
 
-var gClient;
-var gThreadActor;
+add_task(
+  threadFrontTest(async ({ threadFront, client }) => {
+    // Reach over the protocol connection and get a reference to the thread actor.
+    // TODO: rewrite the test so we don't do this..
+    const thread = client._transport._serverConnection.getActor(
+      threadFront.actorID
+    );
 
-function run_test() {
-  initTestDebuggerServer();
-  addTestGlobal("test-nesting");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-nesting", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
-      // Reach over the protocol connection and get a reference to the thread actor.
-      // TODO: rewrite the test so we don't do this..
-      gThreadActor = gClient._transport._serverConnection.getActor(
-        threadFront.actorID
-      );
+    test_nesting(thread);
+  })
+);
 
-      test_nesting();
-    });
-  });
-  do_test_pending();
-}
-
-function test_nesting() {
-  const thread = gThreadActor;
+function test_nesting(thread) {
   const { resolve, promise: p } = defer();
 
   let currentStep = 0;
 
   executeSoon(function() {
     // Should be on the first step
     Assert.equal(++currentStep, 1);
     // We should have one nested event loop from unsfeSynchronize
@@ -46,11 +32,9 @@ function test_nesting() {
   });
 
   Assert.equal(thread.unsafeSynchronize(p), true);
 
   // Should be on the second step
   Assert.equal(++currentStep, 2);
   // There shouldn't be any nested event loops anymore
   Assert.equal(thread._nestedEventLoops.size, 0);
-
-  finishClient(gClient);
 }
--- a/devtools/server/tests/unit/test_nesting-02.js
+++ b/devtools/server/tests/unit/test_nesting-02.js
@@ -1,44 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that we can nest event loops and then automatically exit nested event
 // loops when requested.
 
-var gClient;
-var gThreadActor;
+add_task(
+  threadFrontTest(async ({ threadFront, client }) => {
+    // Reach over the protocol connection and get a reference to the thread actor.
+    // TODO: rewrite the test so we don't do this..
+    const thread = client._transport._serverConnection.getActor(
+      threadFront.actorID
+    );
 
-function run_test() {
-  initTestDebuggerServer();
-  addTestGlobal("test-nesting");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-nesting", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
-      // Reach over the protocol connection and get a reference to the thread
-      // actor.
-      // TODO: rewrite tests so we don't do this kind of reaching anymore..
-      gThreadActor = gClient._transport._serverConnection.getActor(
-        threadFront.actorID
-      );
+    test_nesting(thread);
+  })
+);
 
-      test_nesting();
-    });
-  });
-  do_test_pending();
-}
-
-function test_nesting() {
-  const thread = gThreadActor;
+function test_nesting(thread) {
   const { resolve, promise: p } = defer();
 
   // The following things should happen (in order):
   // 1. In the new event loop (created by unsafeSynchronize)
   // 2. Resolve the promise (shouldn't exit any event loops)
   // 3. Exit the event loop (should also then exit unsafeSynchronize's event loop)
   // 4. Be after the unsafeSynchronize call
   let currentStep = 0;
@@ -77,11 +62,9 @@ function test_nesting() {
   });
 
   Assert.equal(thread.unsafeSynchronize(p), true);
 
   // Should be on the fourth step
   Assert.equal(++currentStep, 4);
   // There shouldn't be any nested event loops anymore
   Assert.equal(thread._nestedEventLoops.size, 0);
-
-  finishClient(gClient);
 }
--- a/devtools/server/tests/unit/test_new_source-01.js
+++ b/devtools/server/tests/unit/test_new_source-01.js
@@ -3,42 +3,35 @@
 
 "use strict";
 
 /**
  * Check basic newSource packet sent from server.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_simple_new_source();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_simple_new_source() {
   gThreadFront.once("newSource", function(packet) {
     Assert.ok(!!packet.source);
     Assert.ok(!!packet.source.url.match(/test_new_source-01.js$/));
 
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   Cu.evalInSandbox(
     function inc(n) {
       return n + 1;
     }.toString(),
     gDebuggee
   );
--- a/devtools/server/tests/unit/test_new_source-02.js
+++ b/devtools/server/tests/unit/test_new_source-02.js
@@ -3,55 +3,44 @@
 
 "use strict";
 
 /**
  * Check that sourceURL has the correct effect when using gThreadFront.eval.
  */
 
 var gDebuggee;
-var gClient;
 var gTargetFront;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, targetFront }) => {
       gThreadFront = threadFront;
       gTargetFront = targetFront;
+      gDebuggee = debuggee;
       test_simple_new_source();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_simple_new_source() {
   gThreadFront.once("paused", function() {
     gThreadFront.once("newSource", async function(packet2) {
       // The "stopMe" eval source is emitted first.
       Assert.ok(!!packet2.source);
       Assert.ok(packet2.source.introductionType, "eval");
 
       gThreadFront.once("newSource", function(packet) {
         dump(JSON.stringify(packet, null, 2));
         Assert.ok(!!packet.source);
         Assert.ok(!!packet.source.url.match(/example\.com/));
 
-        finishClient(gClient);
+        threadFrontTestFinished();
       });
 
       const consoleFront = await gTargetFront.getFront("console");
       consoleFront.evaluateJSAsync(
         "function f() { }\n//# sourceURL=http://example.com/code.js"
       );
     });
   });
--- a/devtools/server/tests/unit/test_objectgrips-10.js
+++ b/devtools/server/tests/unit/test_objectgrips-10.js
@@ -1,43 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* eslint-disable no-shadow, max-nested-callbacks */
 
 "use strict";
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
 // Test that closures can be inspected.
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-closures");
-
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-closures", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_object_grip();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_object_grip() {
   gThreadFront.once("paused", async function(packet) {
     const person = packet.frame.environment.bindings.variables.person;
 
     Assert.equal(person.value.class, "Object");
 
     const personClient = gThreadFront.pauseGrip(person.value);
@@ -66,17 +52,17 @@ function test_object_grip() {
     bindings = await response.scope.bindings();
     Assert.equal(bindings.arguments[1].age.value, 58);
 
     response = await getFooClient.getScope();
     bindings = await response.scope.bindings();
     Assert.equal(bindings.variables.foo.value, 10);
 
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
   /* eslint-disable */
   gDebuggee.eval("(" + function () {
     var PersonFactory = function (name, age) {
       var foo = 10;
       return {
         getName: function () { return name; },
--- a/devtools/server/tests/unit/test_objectgrips-11.js
+++ b/devtools/server/tests/unit/test_objectgrips-11.js
@@ -1,60 +1,47 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that we get the magic properties on Error objects.
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-  gDebuggee.eval(
-    function stopMe(arg1) {
-      debugger;
-    }.toString()
-  );
-
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_object_grip();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_object_grip() {
   gThreadFront.once("paused", async function(packet) {
     const args = packet.frame.arguments;
 
     const objClient = gThreadFront.pauseGrip(args[0]);
     const response = await objClient.getOwnPropertyNames();
     const opn = response.ownPropertyNames;
     Assert.equal(opn.length, 4);
     opn.sort();
     Assert.equal(opn[0], "columnNumber");
     Assert.equal(opn[1], "fileName");
     Assert.equal(opn[2], "lineNumber");
     Assert.equal(opn[3], "message");
 
     await gThreadFront.resume();
-    finishClient(gClient);
+    threadFrontTestFinished();
   });
 
+  gDebuggee.eval(
+    function stopMe(arg1) {
+      debugger;
+    }.toString()
+  );
+
   gDebuggee.eval("stopMe(new TypeError('error message text'))");
 }
--- a/devtools/server/tests/unit/test_objectgrips-12.js
+++ b/devtools/server/tests/unit/test_objectgrips-12.js
@@ -6,47 +6,28 @@
 
 // Test getDisplayString.
 
 const { PromiseTestUtils } = ChromeUtils.import(
   "resource://testing-common/PromiseTestUtils.jsm"
 );
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-  gDebuggee.eval(
-    function stopMe(arg1) {
-      debugger;
-    }.toString()
-  );
-
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_display_string();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_display_string() {
   const testCases = [
     {
       input: "new Boolean(true)",
       output: "true",
     },
     {
@@ -166,16 +147,22 @@ function test_display_string() {
     (async function loop() {
       const objClient = gThreadFront.pauseGrip(args.pop());
       const response = await objClient.getDisplayString();
       Assert.equal(response.displayString, testCases.pop().output);
       if (args.length) {
         loop();
       } else {
         await gThreadFront.resume();
-        finishClient(gClient);
+        threadFrontTestFinished();
       }
     })();
   });
 
+  gDebuggee.eval(
+    function stopMe(arg1) {
+      debugger;
+    }.toString()
+  );
+
   const inputs = testCases.map(({ input }) => input).join(",");
   gDebuggee.eval("stopMe(" + inputs + ")");
 }
--- a/devtools/server/tests/unit/test_objectgrips-13.js
+++ b/devtools/server/tests/unit/test_objectgrips-13.js
@@ -2,56 +2,49 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that ObjectFront.prototype.getDefinitionSite and the "definitionSite"
 // request work properly.
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-  Cu.evalInSandbox(
-    function stopMe() {
-      debugger;
-    }.toString(),
-    gDebuggee
-  );
-
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       add_pause_listener();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function add_pause_listener() {
   gThreadFront.once("paused", function(packet) {
     const [funcGrip, objGrip] = packet.frame.arguments;
     const func = gThreadFront.pauseGrip(funcGrip);
     const obj = gThreadFront.pauseGrip(objGrip);
     test_definition_site(func, obj);
   });
 
   eval_code();
 }
 
 function eval_code() {
   Cu.evalInSandbox(
+    function stopMe() {
+      debugger;
+    }.toString(),
+    gDebuggee
+  );
+
+  Cu.evalInSandbox(
     [
       "this.line0 = Error().lineNumber;",
       "function f() {}",
       "stopMe(f, {});",
     ].join("\n"),
     gDebuggee
   );
 }
@@ -65,11 +58,11 @@ async function test_definition_site(func
 
   test_bad_definition_site(obj);
 }
 
 function test_bad_definition_site(obj) {
   try {
     obj._client.request("definitionSite", () => Assert.ok(false));
   } catch (e) {
-    gThreadFront.resume().then(() => finishClient(gClient));
+    gThreadFront.resume().then(() => threadFrontTestFinished());
   }
 }
--- a/devtools/server/tests/unit/test_objectgrips-14.js
+++ b/devtools/server/tests/unit/test_objectgrips-14.js
@@ -3,35 +3,25 @@
 
 "use strict";
 
 /**
  * Test out of scope objects with synchronous functions.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-object-grip");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-object-grip", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
-      gThreadFront = threadFront;
-      testObjectGroup();
-    });
-  });
-  do_test_pending();
-}
+add_task(
+  threadFrontTest(async ({ threadFront, debuggee }) => {
+    gThreadFront = threadFront;
+    gDebuggee = debuggee;
+    await testObjectGroup();
+  })
+);
 
 function evalCode() {
   evalCallback(gDebuggee, function runTest() {
     const ugh = [];
     let i = 0;
 
     (function() {
       (function() {
@@ -55,10 +45,9 @@ const testObjectGroup = async function()
 
   const ugh2 = packet.frame.environment.bindings.variables.ugh;
   const ugh2Client = gThreadFront.pauseGrip(ugh2.value);
 
   packet = await getPrototypeAndProperties(ugh2Client);
   Assert.equal(packet.ownProperties.length.value, 1);
 
   await resume(gThreadFront);
-  finishClient(gClient);
 };
--- a/devtools/server/tests/unit/test_objectgrips-15.js
+++ b/devtools/server/tests/unit/test_objectgrips-15.js
@@ -3,35 +3,25 @@
 
 "use strict";
 
 /**
  * Test out of scope objects with async functions.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-object-grip");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-object-grip", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
-      gThreadFront = threadFront;
-      testObjectGroup();
-    });
-  });
-  do_test_pending();
-}
+add_task(
+  threadFrontTest(async ({ threadFront, debuggee }) => {
+    gThreadFront = threadFront;
+    gDebuggee = debuggee;
+    await testObjectGroup();
+  })
+);
 
 function evalCode() {
   evalCallback(gDebuggee, function runTest() {
     const ugh = [];
     let i = 0;
 
     function foo() {
       ugh.push(i++);
@@ -55,10 +45,9 @@ const testObjectGroup = async function()
   packet = await resumeAndWaitForPause(gThreadFront);
   const ugh2 = packet.frame.environment.parent.bindings.variables.ugh;
   const ugh2Client = gThreadFront.pauseGrip(ugh2.value);
 
   packet = await getPrototypeAndProperties(ugh2Client);
   Assert.equal(packet.ownProperties.length.value, 2);
 
   await resume(gThreadFront);
-  finishClient(gClient);
 };
--- a/devtools/server/tests/unit/test_pause_exceptions-01.js
+++ b/devtools/server/tests/unit/test_pause_exceptions-01.js
@@ -5,48 +5,35 @@
 "use strict";
 
 /**
  * Test that setting pauseOnExceptions to true will cause the debuggee to pause
  * when an exceptions is thrown.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", function(packet) {
     gThreadFront.once("paused", function(packet) {
       Assert.equal(packet.why.type, "exception");
       Assert.equal(packet.why.exception, 42);
-      gThreadFront.resume().then(() => finishClient(gClient));
+      gThreadFront.resume().then(() => threadFrontTestFinished());
     });
 
     gThreadFront.pauseOnExceptions(true, false);
     gThreadFront.resume();
   });
 
   /* eslint-disable */
   gDebuggee.eval("(" + function () {
--- a/devtools/server/tests/unit/test_pause_exceptions-02.js
+++ b/devtools/server/tests/unit/test_pause_exceptions-02.js
@@ -4,48 +4,35 @@
 "use strict";
 
 /**
  * Test that setting pauseOnExceptions to true when the debugger isn't in a
  * paused state will not cause the debuggee to pause when an exceptions is thrown.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.pauseOnExceptions(true, false).then(function() {
     gThreadFront.once("paused", function(packet) {
       Assert.equal(packet.why.type, "exception");
       Assert.equal(packet.why.exception, 42);
-      gThreadFront.resume().then(() => finishClient(gClient));
+      gThreadFront.resume().then(() => threadFrontTestFinished());
     });
 
     /* eslint-disable */
     gDebuggee.eval("(" + function () {   // 1
       function stopMe() {                // 2
         throw 42;                        // 3
       }                                  // 4
       try {                              // 5
--- a/devtools/server/tests/unit/test_pauselifetime-01.js
+++ b/devtools/server/tests/unit/test_pauselifetime-01.js
@@ -7,36 +7,27 @@
 /**
  * Check that pause-lifetime grips go away correctly after a resume.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
+      gClient = client;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", async function(packet) {
     const pauseActor = packet.actor;
 
     // Make a bogus request to the pause-lifetime actor.  Should get
     // unrecognized-packet-type (and not no-such-actor).
     try {
@@ -52,17 +43,17 @@ function test_pause_frame() {
       // same request.
       try {
         await gClient.request({ to: pauseActor, type: "bogusRequest" });
         ok(false, "bogusRequest should throw");
       } catch (e) {
         ok(true, "bogusRequest thrown");
         Assert.equal(e.error, "noSuchActor");
       }
-      finishClient(gClient);
+      threadFrontTestFinished();
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe() {
           debugger;
--- a/devtools/server/tests/unit/test_pauselifetime-02.js
+++ b/devtools/server/tests/unit/test_pauselifetime-02.js
@@ -7,36 +7,27 @@
 /**
  * Check that pause-lifetime grips go away correctly after a resume.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
+      gClient = client;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", async function(packet) {
     const args = packet.frame.arguments;
     const objActor = args[0].actor;
     Assert.equal(args[0].class, "Object");
     Assert.ok(!!objActor);
 
@@ -55,17 +46,17 @@ function test_pause_frame() {
       // same request.
       try {
         await gClient.request({ to: objActor, type: "bogusRequest" });
         ok(false, "bogusRequest should throw");
       } catch (e) {
         ok(true, "bogusRequest thrown");
         Assert.equal(e.error, "noSuchActor");
       }
-      finishClient(gClient);
+      threadFrontTestFinished();
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe(obj) {
           debugger;
--- a/devtools/server/tests/unit/test_pauselifetime-03.js
+++ b/devtools/server/tests/unit/test_pauselifetime-03.js
@@ -7,36 +7,27 @@
 /**
  * Check that pause-lifetime grip clients are marked invalid after a resume.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
+      gClient = client;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", async function(packet) {
     const args = packet.frame.arguments;
     const objActor = args[0].actor;
     Assert.equal(args[0].class, "Object");
     Assert.ok(!!objActor);
 
@@ -62,17 +53,17 @@ function test_pause_frame() {
         const objFront = gClient.getFrontByID(objActor);
         await objFront.request({ to: objActor, type: "bogusRequest" });
         ok(false, "bogusRequest should throw");
       } catch (e) {
         ok(true, "bogusRequest thrown");
         Assert.ok(!!e.match(/noSuchActor/));
       }
       Assert.ok(!objClient.valid);
-      finishClient(gClient);
+      threadFrontTestFinished();
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe(obj) {
           debugger;
--- a/devtools/server/tests/unit/test_pauselifetime-04.js
+++ b/devtools/server/tests/unit/test_pauselifetime-04.js
@@ -4,50 +4,39 @@
 "use strict";
 
 /**
  * Test that requesting a pause actor for the same value multiple
  * times returns the same actor.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_pause_frame();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_pause_frame() {
   gThreadFront.once("paused", function(packet) {
     const args = packet.frame.arguments;
     const objActor1 = args[0].actor;
 
     gThreadFront.getFrames(0, 1).then(function(response) {
       const frame = response.frames[0];
       Assert.equal(objActor1, frame.arguments[0].actor);
       gThreadFront.resume().then(function() {
-        finishClient(gClient);
+        threadFrontTestFinished();
       });
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
         function stopMe(obj) {
--- a/devtools/server/tests/unit/test_promise_state-01.js
+++ b/devtools/server/tests/unit/test_promise_state-01.js
@@ -4,43 +4,29 @@
 
 "use strict";
 
 /**
  * Test that the preview in a Promise's grip is correct when the Promise is
  * pending.
  */
 
-function run_test() {
-  initTestDebuggerServer();
-  const debuggee = addTestGlobal("test-promise-state");
-  const client = new DebuggerClient(DebuggerServer.connectPipe());
-  client.connect().then(function() {
-    attachTestTabAndResume(client, "test-promise-state", function(
-      response,
-      targetFront,
+add_task(
+  threadFrontTest(async ({ threadFront, debuggee }) => {
+    const packet = await executeOnNextTickAndWaitForPause(
+      () => evalCode(debuggee),
       threadFront
-    ) {
-      (async function() {
-        const packet = await executeOnNextTickAndWaitForPause(
-          () => evalCode(debuggee),
-          threadFront
-        );
+    );
 
-        const grip = packet.frame.environment.bindings.variables.p;
-        ok(grip.value.preview);
-        equal(grip.value.class, "Promise");
-        equal(grip.value.promiseState.state, "pending");
-
-        finishClient(client);
-      })();
-    });
-  });
-  do_test_pending();
-}
+    const grip = packet.frame.environment.bindings.variables.p;
+    ok(grip.value.preview);
+    equal(grip.value.class, "Promise");
+    equal(grip.value.promiseState.state, "pending");
+  })
+);
 
 function evalCode(debuggee) {
   /* eslint-disable */
   Cu.evalInSandbox(
     "doTest();\n" +
     function doTest() {
       var p = new Promise(function () {});
       debugger;
--- a/devtools/server/tests/unit/test_promise_state-02.js
+++ b/devtools/server/tests/unit/test_promise_state-02.js
@@ -4,49 +4,35 @@
 
 "use strict";
 
 /**
  * Test that the preview in a Promise's grip is correct when the Promise is
  * fulfilled.
  */
 
-function run_test() {
-  initTestDebuggerServer();
-  const debuggee = addTestGlobal("test-promise-state");
-  const client = new DebuggerClient(DebuggerServer.connectPipe());
-  client.connect().then(function() {
-    attachTestTabAndResume(client, "test-promise-state", function(
-      response,
-      targetFront,
+add_task(
+  threadFrontTest(async ({ threadFront, debuggee }) => {
+    const packet = await executeOnNextTickAndWaitForPause(
+      () => evalCode(debuggee),
       threadFront
-    ) {
-      (async function() {
-        const packet = await executeOnNextTickAndWaitForPause(
-          () => evalCode(debuggee),
-          threadFront
-        );
+    );
 
-        const grip = packet.frame.environment.bindings.variables.p;
-        ok(grip.value.preview);
-        equal(grip.value.class, "Promise");
-        equal(grip.value.promiseState.state, "fulfilled");
-        equal(
-          grip.value.promiseState.value.actorID,
-          packet.frame.arguments[0].actorID,
-          "The promise's fulfilled state value should be the same value passed " +
-            "to the then function"
-        );
-
-        finishClient(client);
-      })();
-    });
-  });
-  do_test_pending();
-}
+    const grip = packet.frame.environment.bindings.variables.p;
+    ok(grip.value.preview);
+    equal(grip.value.class, "Promise");
+    equal(grip.value.promiseState.state, "fulfilled");
+    equal(
+      grip.value.promiseState.value.actorID,
+      packet.frame.arguments[0].actorID,
+      "The promise's fulfilled state value should be the same value passed " +
+        "to the then function"
+    );
+  })
+);
 
 function evalCode(debuggee) {
   /* eslint-disable */
   Cu.evalInSandbox(
     "doTest();\n" +
     function doTest() {
       var resolved = Promise.resolve({});
       resolved.then(() => {
--- a/devtools/server/tests/unit/test_promise_state-03.js
+++ b/devtools/server/tests/unit/test_promise_state-03.js
@@ -4,49 +4,35 @@
 
 "use strict";
 
 /**
  * Test that the preview in a Promise's grip is correct when the Promise is
  * rejected.
  */
 
-function run_test() {
-  initTestDebuggerServer();
-  const debuggee = addTestGlobal("test-promise-state");
-  const client = new DebuggerClient(DebuggerServer.connectPipe());
-  client.connect().then(function() {
-    attachTestTabAndResume(client, "test-promise-state", function(
-      response,
-      targetFront,
+add_task(
+  threadFrontTest(async ({ threadFront, debuggee }) => {
+    const packet = await executeOnNextTickAndWaitForPause(
+      () => evalCode(debuggee),
       threadFront
-    ) {
-      (async function() {
-        const packet = await executeOnNextTickAndWaitForPause(
-          () => evalCode(debuggee),
-          threadFront
-        );
+    );
 
-        const grip = packet.frame.environment.bindings.variables.p;
-        ok(grip.value.preview);
-        equal(grip.value.class, "Promise");
-        equal(grip.value.promiseState.state, "rejected");
-        equal(
-          grip.value.promiseState.reason.actorID,
-          packet.frame.arguments[0].actorID,
-          "The promise's rejected state reason should be the same value passed " +
-            "to the then function"
-        );
-
-        finishClient(client);
-      })();
-    });
-  });
-  do_test_pending();
-}
+    const grip = packet.frame.environment.bindings.variables.p;
+    ok(grip.value.preview);
+    equal(grip.value.class, "Promise");
+    equal(grip.value.promiseState.state, "rejected");
+    equal(
+      grip.value.promiseState.reason.actorID,
+      packet.frame.arguments[0].actorID,
+      "The promise's rejected state reason should be the same value passed " +
+        "to the then function"
+    );
+  })
+);
 
 function evalCode(debuggee) {
   /* eslint-disable */
   Cu.evalInSandbox(
     "doTest();\n" +
     function doTest() {
       var resolved = Promise.reject(new Error("uh oh"));
       resolved.catch(() => {
--- a/devtools/server/tests/unit/test_source-01.js
+++ b/devtools/server/tests/unit/test_source-01.js
@@ -1,48 +1,31 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* eslint-disable no-shadow, max-nested-callbacks */
 
 "use strict";
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
 // This test ensures that we can create SourceActors and SourceFronts properly,
 // and that they can communicate over the protocol to fetch the source text for
 // a given script.
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-  Cu.evalInSandbox(
-    "" +
-      function stopMe(arg1) {
-        debugger;
-      },
-    gDebuggee,
-    "1.8",
-    getFileUrl("test_source-01.js")
-  );
-
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_source();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 const SOURCE_URL = "http://example.com/foobar.js";
 const SOURCE_CONTENT = "stopMe()";
 
 function test_source() {
   DebuggerServer.LONG_STRING_LENGTH = 200;
 
   gThreadFront.once("paused", function(packet) {
@@ -61,16 +44,26 @@ function test_source() {
         Assert.ok(!!response);
         Assert.ok(!!response.contentType);
         Assert.ok(response.contentType.includes("javascript"));
 
         Assert.ok(!!response.source);
         Assert.equal(SOURCE_CONTENT, response.source);
 
         gThreadFront.resume().then(function() {
-          finishClient(gClient);
+          threadFrontTestFinished();
         });
       });
     });
   });
 
+  Cu.evalInSandbox(
+    "" +
+      function stopMe(arg1) {
+        debugger;
+      },
+    gDebuggee,
+    "1.8",
+    getFileUrl("test_source-01.js")
+  );
+
   Cu.evalInSandbox(SOURCE_CONTENT, gDebuggee, "1.8", SOURCE_URL);
 }
--- a/devtools/server/tests/unit/test_source-02.js
+++ b/devtools/server/tests/unit/test_source-02.js
@@ -1,48 +1,31 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* eslint-disable no-shadow, max-nested-callbacks */
 
 "use strict";
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
 // This test ensures that we can create SourceActors and SourceFronts properly,
 // and that they can communicate over the protocol to fetch the source text for
 // a given script.
 
-function run_test() {
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-  Cu.evalInSandbox(
-    "" +
-      function stopMe(arg1) {
-        debugger;
-      },
-    gDebuggee,
-    "1.8",
-    getFileUrl("test_source-02.js")
-  );
-
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee }) => {
       gThreadFront = threadFront;
+      gDebuggee = debuggee;
       test_source();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 const SOURCE_URL = "http://example.com/foobar.js";
 const SOURCE_CONTENT = `
   stopMe();
   for(var i = 0; i < 2; i++) {
     debugger;
   }
 `;
@@ -97,14 +80,24 @@ function test_source() {
       Assert.deepEqual(response, {
         2: [2],
         3: [14, 17, 24],
         4: [4],
         6: [0],
       });
 
       await gThreadFront.resume();
-      finishClient(gClient);
+      threadFrontTestFinished();
     });
   });
 
+  Cu.evalInSandbox(
+    "" +
+      function stopMe(arg1) {
+        debugger;
+      },
+    gDebuggee,
+    "1.8",
+    getFileUrl("test_source-02.js")
+  );
+
   Cu.evalInSandbox(SOURCE_CONTENT, gDebuggee, "1.8", SOURCE_URL);
 }
--- a/devtools/server/tests/unit/test_symbols-01.js
+++ b/devtools/server/tests/unit/test_symbols-01.js
@@ -4,36 +4,23 @@
 "use strict";
 
 /**
  * Test that we can represent ES6 Symbols over the RDP.
  */
 
 const URL = "foo.js";
 
-function run_test() {
-  initTestDebuggerServer();
-  const debuggee = addTestGlobal("test-symbols");
-  const client = new DebuggerClient(DebuggerServer.connectPipe());
+add_task(
+  threadFrontTest(async ({ threadFront, debuggee }) => {
+    await testSymbols(threadFront, debuggee);
+  })
+);
 
-  client.connect().then(function() {
-    attachTestTabAndResume(client, "test-symbols", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
-      add_task(testSymbols.bind(null, threadFront, client, debuggee));
-      run_next_test();
-    });
-  });
-
-  do_test_pending();
-}
-
-async function testSymbols(threadFront, client, debuggee) {
+async function testSymbols(threadFront, debuggee) {
   const evalCode = () => {
     /* eslint-disable */
     Cu.evalInSandbox(
       "(" + function () {
         var symbolWithName = Symbol("Chris");
         var symbolWithoutName = Symbol();
         var iteratorSymbol = Symbol.iterator;
         debugger;
@@ -56,11 +43,9 @@ async function testSymbols(threadFront, 
   equal(symbolWithName.value.type, "symbol");
   equal(symbolWithName.value.name, "Chris");
 
   equal(symbolWithoutName.value.type, "symbol");
   ok(!("name" in symbolWithoutName.value));
 
   equal(iteratorSymbol.value.type, "symbol");
   equal(iteratorSymbol.value.name, "Symbol.iterator");
-
-  finishClient(client);
 }
--- a/devtools/server/tests/unit/test_symbols-02.js
+++ b/devtools/server/tests/unit/test_symbols-02.js
@@ -4,36 +4,23 @@
 "use strict";
 
 /**
  * Test that we don't run debuggee code when getting symbol names.
  */
 
 const URL = "foo.js";
 
-function run_test() {
-  initTestDebuggerServer();
-  const debuggee = addTestGlobal("test-symbols");
-  const client = new DebuggerClient(DebuggerServer.connectPipe());
+add_task(
+  threadFrontTest(async ({ threadFront, debuggee }) => {
+    await testSymbols(threadFront, debuggee);
+  })
+);
 
-  client.connect().then(function() {
-    attachTestTabAndResume(client, "test-symbols", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
-      add_task(testSymbols.bind(null, client, threadFront, debuggee));
-      run_next_test();
-    });
-  });
-
-  do_test_pending();
-}
-
-async function testSymbols(client, threadFront, debuggee) {
+async function testSymbols(threadFront, debuggee) {
   const evalCode = () => {
     /* eslint-disable */
     Cu.evalInSandbox(
       "(" + function () {
         Symbol.prototype.toString = () => {
           throw new Error("lololol");
         };
         var sym = Symbol("le troll");
@@ -47,11 +34,9 @@ async function testSymbols(client, threa
     /* eslint-enable */
   };
 
   const packet = await executeOnNextTickAndWaitForPause(evalCode, threadFront);
   const { sym } = packet.frame.environment.bindings.variables;
 
   equal(sym.value.type, "symbol");
   equal(sym.value.name, "le troll");
-
-  finishClient(client);
 }
--- a/devtools/server/tests/unit/test_threadlifetime-01.js
+++ b/devtools/server/tests/unit/test_threadlifetime-01.js
@@ -7,36 +7,27 @@
 /**
  * Check that thread-lifetime grips last past a resume.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gClient = client;
+      gDebuggee = debuggee;
       test_thread_lifetime();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_thread_lifetime() {
   gThreadFront.once("paused", async function(packet) {
     const pauseGrip = packet.frame.arguments[0];
 
     // Create a thread-lifetime actor for this object.
     const response = await gClient.request({
       to: pauseGrip.actor,
@@ -52,17 +43,17 @@ function test_thread_lifetime() {
       try {
         await gClient.request({ to: pauseGrip.actor, type: "bogusRequest" });
         ok(false, "bogusRequest should throw");
       } catch (e) {
         Assert.equal(e.error, "unrecognizedPacketType");
         ok(true, "bogusRequest thrown");
       }
       gThreadFront.resume().then(function() {
-        finishClient(gClient);
+        threadFrontTestFinished();
       });
     });
     gThreadFront.resume();
   });
 
   gDebuggee.eval(
     "(" +
       function() {
--- a/devtools/server/tests/unit/test_threadlifetime-02.js
+++ b/devtools/server/tests/unit/test_threadlifetime-02.js
@@ -7,36 +7,27 @@
 /**
  * Check that thread-lifetime grips last past a resume.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gClient = client;
+      gDebuggee = debuggee;
       test_thread_lifetime();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_thread_lifetime() {
   gThreadFront.once("paused", async function(packet) {
     const pauseGrip = packet.frame.arguments[0];
 
     // Create a thread-lifetime actor for this object.
     const response = await gClient.request({
       to: pauseGrip.actor,
@@ -53,17 +44,17 @@ function test_thread_lifetime() {
       const objFront2 = new ObjectFront(gClient, pauseGrip);
 
       try {
         await objFront2
           .request({ to: pauseGrip.actor, type: "bogusRequest" })
           .catch(function(response) {
             Assert.ok(!!response.match(/noSuchActor/));
             gThreadFront.resume().then(function() {
-              finishClient(gClient);
+              threadFrontTestFinished();
             });
             throw new Error();
           });
         ok(false, "bogusRequest should throw");
       } catch (e) {
         ok(true, "bogusRequest thrown");
       }
     });
--- a/devtools/server/tests/unit/test_threadlifetime-04.js
+++ b/devtools/server/tests/unit/test_threadlifetime-04.js
@@ -8,36 +8,27 @@
  * Check that requesting a thread-lifetime actor twice for the same
  * value returns the same actor.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadFront;
 
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-grips");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-grips", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
       gThreadFront = threadFront;
+      gClient = client;
+      gDebuggee = debuggee;
       test_thread_lifetime();
-    });
-  });
-  do_test_pending();
-}
+    },
+    { waitForFinish: true }
+  )
+);
 
 function test_thread_lifetime() {
   gThreadFront.once("paused", function(packet) {
     const pauseGrip = packet.frame.arguments[0];
 
     gClient.request({ to: pauseGrip.actor, type: "threadGrip" }, function(
       response
     ) {
@@ -46,17 +37,17 @@ function test_thread_lifetime() {
 
       const threadGrip1 = response.from;
 
       gClient.request({ to: pauseGrip.actor, type: "threadGrip" }, function(
         response
       ) {
         Assert.equal(threadGrip1, response.from);
         gThreadFront.resume().then(function() {
-          finishClient(gClient);
+          threadFrontTestFinished();
         });
       });
     });
   });
 
   gDebuggee.eval(
     "(" +
       function() {
--- a/devtools/server/tests/unit/test_wasm_source-01.js
+++ b/devtools/server/tests/unit/test_wasm_source-01.js
@@ -4,55 +4,34 @@
 
 "use strict";
 
 /**
  * Verify if client can receive binary wasm if 'wasmBinarySource' is set.
  */
 
 var gDebuggee;
-var gClient;
 var gThreadFront;
 
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-function run_test() {
-  if (typeof WebAssembly == "undefined") {
-    // wasm is not enabled for this platform
-    return;
-  }
-
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-wasm-source");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    Assert.ok(gClient.mainRoot.traits.wasmBinarySource);
+add_task(
+  threadFrontTest(
+    async ({ threadFront, debuggee, client }) => {
+      gThreadFront = threadFront;
+      gDebuggee = debuggee;
 
-    attachTestTabAndResume(gClient, "test-wasm-source", function(
-      response,
-      targetFront,
-      threadFront
-    ) {
-      gThreadFront = threadFront;
-      gThreadFront
-        .reconfigure({
-          observeAsmJS: true,
-          wasmBinarySource: true,
-        })
-        .then(function() {
-          test_source();
-        });
-    });
-  });
-  do_test_pending();
-}
+      await gThreadFront.reconfigure({
+        observeAsmJS: true,
+        wasmBinarySource: true,
+      });
+
+      test_source();
+    },
+    { waitForFinish: true, doNotRunWorker: true }
+  )
+);
 
 const EXPECTED_CONTENT = String.fromCharCode(
   0,
   97,
   115,
   109,
   1,
   0,
@@ -130,17 +109,17 @@ function test_source() {
 
         const sourceContent = response.source;
         Assert.ok(!!sourceContent);
         Assert.equal(typeof sourceContent, "object");
         Assert.ok("binary" in sourceContent);
         Assert.equal(EXPECTED_CONTENT, sourceContent.binary);
 
         gThreadFront.resume().then(function() {
-          finishClient(gClient);
+          threadFrontTestFinished();
         });
       });
     });
   });
 
   /* eslint-disable comma-spacing, max-len */
   gDebuggee.eval(
     "(" +
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -106,18 +106,16 @@ skip-if = true # breakpoint sliding is n
 skip-if = true # breakpoint sliding is not supported bug 1525685
 [test_breakpoint-09.js]
 [test_breakpoint-10.js]
 [test_breakpoint-11.js]
 [test_breakpoint-12.js]
 skip-if = true # breakpoint sliding is not supported bug 1525685
 [test_breakpoint-13.js]
 [test_breakpoint-14.js]
-[test_breakpoint-15.js]
-skip-if = true # tests for breakpoint actors are obsolete bug 1524374
 [test_breakpoint-16.js]
 [test_breakpoint-17.js]
 skip-if = true # tests for breakpoint actors are obsolete bug 1524374
 [test_breakpoint-18.js]
 [test_breakpoint-19.js]
 skip-if = true
 reason = bug 1104838
 [test_breakpoint-20.js]