[service-workers] Use asynchronous cleanup (#13164)

Previously, many tests un-registered service workers only after all
assertions had been satisfied. This meant that failing tests would not
un-register workers. In order to account for workers that persisted from
previous test failures, the tests included "setup" code to defensively
un-register such workers.

The `Test#add_cleanup` method was recently extended to support
asynchronous "clean up" operations [1]. Use that API to schedule service
worker un-registration so that it occurs regardless of the result of
each test.

[1] https://github.com/web-platform-tests/wpt/pull/8748
diff --git a/service-workers/service-worker/ServiceWorkerGlobalScope/postmessage.https.html b/service-workers/service-worker/ServiceWorkerGlobalScope/postmessage.https.html
index 17046ee..99dedeb 100644
--- a/service-workers/service-worker/ServiceWorkerGlobalScope/postmessage.https.html
+++ b/service-workers/service-worker/ServiceWorkerGlobalScope/postmessage.https.html
@@ -12,7 +12,12 @@
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           registration = r;
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() {
@@ -28,7 +33,6 @@
         })
       .then(function(result) {
           assert_equals(result, 'OK');
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Post loopback messages');
 
@@ -41,6 +45,10 @@
 
     return service_worker_unregister_and_register(t, script1, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           registration = r;
           return wait_for_state(t, registration.installing, 'activated');
         })
@@ -69,7 +77,6 @@
       .then(function(result) {
           assert_equals(result, 'OK');
           frame.remove();
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Post messages among service workers');
 
diff --git a/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html b/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html
index 3133091..1a124d7 100644
--- a/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html
+++ b/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html
@@ -11,6 +11,10 @@
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'redundant');
         })
       .then(function() {
@@ -21,7 +25,6 @@
             result,
             undefined,
             'After unregister(), the registration should not found');
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Unregister on script evaluation');
 
@@ -31,6 +34,10 @@
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'redundant');
         })
       .then(function() {
@@ -41,7 +48,6 @@
             result,
             undefined,
             'After unregister(), the registration should not found');
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Unregister on installing event');
 
@@ -51,6 +57,10 @@
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'redundant');
         })
       .then(function() {
@@ -61,7 +71,6 @@
             result,
             undefined,
             'After unregister(), the registration should not found');
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Unregister on activate event');
 
@@ -74,6 +83,10 @@
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() { return with_iframe(scope); })
@@ -120,7 +133,6 @@
 
           frame.remove();
           new_frame.remove();
-          return service_worker_unregister_and_done(t, scope);
         })
   }, 'Unregister controlling service worker');
 
diff --git a/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html b/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html
index a9285a1..a7dde22 100644
--- a/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html
+++ b/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html
@@ -13,6 +13,10 @@
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           registration = r;
           return wait_for_state(t, registration.installing, 'activated');
         })
@@ -38,7 +42,6 @@
               'events seen by the worker');
           frame1.remove();
           frame2.remove();
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Update a registration on ServiceWorkerGlobalScope');
 
diff --git a/service-workers/service-worker/about-blank-replacement.https.html b/service-workers/service-worker/about-blank-replacement.https.html
index e1fefaf..b6efe3e 100644
--- a/service-workers/service-worker/about-blank-replacement.https.html
+++ b/service-workers/service-worker/about-blank-replacement.https.html
@@ -60,6 +60,9 @@
 
 async function doAsyncTest(t, scope) {
   let reg = await service_worker_unregister_and_register(t, worker, scope);
+
+  t.add_cleanup(() => service_worker_unregister(t, scope));
+
   await wait_for_state(t, reg.installing, 'activated');
 
   // Load the scope as a frame.  We expect this in turn to have a nested
@@ -96,7 +99,6 @@
   }
 
   frame.remove();
-  await service_worker_unregister_and_done(t, scope);
 }
 
 promise_test(async function(t) {
@@ -126,6 +128,9 @@
   const scope = 'resources/about-blank-replacement-uncontrolled-nested-frame.html';
 
   let reg = await service_worker_unregister_and_register(t, worker, scope);
+
+  t.add_cleanup(() => service_worker_unregister(t, scope));
+
   await wait_for_state(t, reg.installing, 'activated');
 
   // Load the scope as a frame.  We expect this in turn to have a nested
@@ -147,7 +152,6 @@
                 'nested frame should not be controlled');
 
   frame.remove();
-  await service_worker_unregister_and_done(t, scope);
 }, 'Initial about:blank is controlled, exposed to clients.matchAll(), and ' +
    'final Client is not controlled by a service worker.');
 
diff --git a/service-workers/service-worker/activate-event-after-install-state-change.https.html b/service-workers/service-worker/activate-event-after-install-state-change.https.html
index 57fccf1..016a52c 100644
--- a/service-workers/service-worker/activate-event-after-install-state-change.https.html
+++ b/service-workers/service-worker/activate-event-after-install-state-change.https.html
@@ -11,6 +11,10 @@
 
   return service_worker_unregister_and_register(t, script, scope)
     .then(function(registration) {
+        t.add_cleanup(function() {
+            return service_worker_unregister(t, scope);
+          });
+
         var sw = registration.installing;
 
         return new Promise(t.step_func(function(resolve) {
@@ -23,9 +27,6 @@
           });
         }));
       })
-    .then(function() {
-        return service_worker_unregister_and_done(t, scope);
-      })
     .catch(unreached_rejection(t));
   }, 'installed event should be fired before activating service worker');
 
diff --git a/service-workers/service-worker/claim-fetch.https.html b/service-workers/service-worker/claim-fetch.https.html
index 050c1ea..6b7d353 100644
--- a/service-workers/service-worker/claim-fetch.https.html
+++ b/service-workers/service-worker/claim-fetch.https.html
@@ -32,9 +32,13 @@
 
     // Register a service worker.
     .then(() => service_worker_unregister_and_register(t, script, scope))
-    .then(r => worker = r.installing)
-    .then(() => wait_for_state(t, worker, 'activated'))
+    .then(r => {
+        t.add_cleanup(() => service_worker_unregister(t, scope));
 
+        worker = r.installing;
+
+        return wait_for_state(t, worker, 'activated');
+      })
     // Let the service worker claim the iframe.
     .then(() => {
       var channel = new MessageChannel();
@@ -62,7 +66,6 @@
 
     // Cleanup this testcase.
     .then(() => frame.remove())
-    .then(() => service_worker_unregister_and_done(t, scope));
 }, 'fetch() should be intercepted after the client is claimed.')
 
 </script>
diff --git a/service-workers/service-worker/claim-not-using-registration.https.html b/service-workers/service-worker/claim-not-using-registration.https.html
index 1138b74..fd61d05 100644
--- a/service-workers/service-worker/claim-not-using-registration.https.html
+++ b/service-workers/service-worker/claim-not-using-registration.https.html
@@ -15,6 +15,10 @@
     return service_worker_unregister_and_register(
         t, init_worker_url, init_scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, init_scope);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() {
@@ -35,6 +39,10 @@
                                                   {scope: claim_scope});
         })
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, claim_scope);
+            });
+
           claim_worker = registration.installing;
           claim_registration = registration;
           return wait_for_state(t, registration.installing, 'activated');
@@ -67,9 +75,6 @@
           frame1.remove();
           frame2.remove();
           return claim_registration.unregister();
-        })
-      .then(function() {
-          return service_worker_unregister_and_done(t, init_scope);
         });
   }, 'Test claim client which is not using registration');
 
@@ -86,6 +91,10 @@
               claim_worker_url, {scope: claim_scope});
         })
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, claim_scope);
+            });
+
           claim_worker = registration.installing;
           return wait_for_state(t, registration.installing, 'activated');
         })
@@ -94,6 +103,10 @@
               installing_worker_url, {scope: scope});
         })
       .then(function() {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           var channel = new MessageChannel();
           var saw_message = new Promise(function(resolve) {
               channel.port1.onmessage = t.step_func(function(e) {
@@ -111,10 +124,6 @@
               'Frame should not be claimed when a longer-matched ' +
               'registration exists');
           frame.remove();
-          return service_worker_unregister(t, claim_scope);
-        })
-      .then(function() {
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Test claim client when there\'s a longer-matched registration not ' +
      'already used by the page');
diff --git a/service-workers/service-worker/claim-shared-worker-fetch.https.html b/service-workers/service-worker/claim-shared-worker-fetch.https.html
index a07db7b..f5f4488 100644
--- a/service-workers/service-worker/claim-shared-worker-fetch.https.html
+++ b/service-workers/service-worker/claim-shared-worker-fetch.https.html
@@ -30,9 +30,13 @@
                                          'fetch() should not be intercepted.'))
     // Register a service worker.
     .then(() => service_worker_unregister_and_register(t, script, scope))
-    .then(r => worker = r.installing)
-    .then(() => wait_for_state(t, worker, 'activated'))
+    .then(r => {
+        t.add_cleanup(() => service_worker_unregister(t, scope));
 
+        worker = r.installing;
+
+        return wait_for_state(t, worker, 'activated')
+      })
     // Let the service worker claim the iframe and the shared worker.
     .then(() => {
       var channel = new MessageChannel();
@@ -60,8 +64,7 @@
                         'fetch() in the shared worker should be intercepted.'))
 
     // Cleanup this testcase.
-    .then(() => frame.remove())
-    .then(() => service_worker_unregister_and_done(t, scope));
+    .then(() => frame.remove());
 }, 'fetch() in SharedWorker should be intercepted after the client is claimed.')
 
 </script>
diff --git a/service-workers/service-worker/claim-using-registration.https.html b/service-workers/service-worker/claim-using-registration.https.html
index 7d77d38..8a2a6ff 100644
--- a/service-workers/service-worker/claim-using-registration.https.html
+++ b/service-workers/service-worker/claim-using-registration.https.html
@@ -13,6 +13,10 @@
     var worker, sw_registration, frame;
     return service_worker_unregister_and_register(t, url1, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() {
@@ -50,9 +54,6 @@
               'Frame1 controller scriptURL should be changed to url2');
           frame.remove();
           return sw_registration.unregister();
-        })
-      .then(function() {
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Test worker claims client which is using another registration');
 
@@ -63,6 +64,10 @@
     var frame, worker;
     return service_worker_unregister_and_register(t, url1, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() {
@@ -91,7 +96,6 @@
         })
       .then(function() {
           frame.remove();
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Test for the waiting worker claims a client which is using the the ' +
      'same registration');
diff --git a/service-workers/service-worker/clients-matchall-client-types.https.html b/service-workers/service-worker/clients-matchall-client-types.https.html
index 2496717..a2a5681 100644
--- a/service-workers/service-worker/clients-matchall-client-types.https.html
+++ b/service-workers/service-worker/clients-matchall-client-types.https.html
@@ -54,6 +54,10 @@
     return service_worker_unregister_and_register(
         t, 'resources/clients-matchall-worker.js', scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() { return with_iframe(iframe_url); })
@@ -66,9 +70,7 @@
         })
       .then(function() {
           frame.remove();
-          return service_worker_unregister_and_done(t, scope);
-        })
-      .catch(unreached_rejection(t));
+        });
   }, 'Verify matchAll() with window client type');
 
 promise_test(function(t) {
@@ -76,6 +78,10 @@
     return service_worker_unregister_and_register(
         t, 'resources/clients-matchall-worker.js', scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() { return with_iframe(iframe_url); })
@@ -112,7 +118,6 @@
         })
       .then(function() {
           frame.remove();
-          return service_worker_unregister_and_done(t, scope);
         });
 }, 'Verify matchAll() with {window, sharedworker, worker} client types');
 
diff --git a/service-workers/service-worker/clients-matchall-order.https.html b/service-workers/service-worker/clients-matchall-order.https.html
index 0596050..ec650f2 100644
--- a/service-workers/service-worker/clients-matchall-order.https.html
+++ b/service-workers/service-worker/clients-matchall-order.https.html
@@ -123,6 +123,8 @@
   let frameResultList;
   let extraWindowResult;
   return service_worker_unregister_and_register(t, script, opts.scope).then(swr => {
+    t.add_cleanup(() => service_worker_unregister(t, opts.scope));
+
     worker = swr.installing;
     return wait_for_state(t, worker, 'activated');
   }).then(_ => {
@@ -143,8 +145,6 @@
   }).then(_ => {
     frameResultList.forEach(result => result.top.remove());
     extraWindowResult.top.remove();
-  }).then(_ => {
-    return service_worker_unregister_and_done(t, opts.scope);
   }).catch(e => {
     if (frameResultList) {
       frameResultList.forEach(result => result.top.remove());
diff --git a/service-workers/service-worker/controller-on-reload.https.html b/service-workers/service-worker/controller-on-reload.https.html
index e0beb72..2e966d4 100644
--- a/service-workers/service-worker/controller-on-reload.https.html
+++ b/service-workers/service-worker/controller-on-reload.https.html
@@ -13,6 +13,10 @@
     var controller;
     return service_worker_unregister(t, scope)
       .then(function() {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return with_iframe(scope);
         })
       .then(function(f) {
@@ -48,7 +52,6 @@
       .then(function(frameRegistration) {
           assert_equals(frameRegistration.active, controller);
           frame.remove();
-          service_worker_unregister_and_done(t, scope);
         });
   }, 'controller is set upon reload after registration');
 </script>
diff --git a/service-workers/service-worker/fetch-csp.https.html b/service-workers/service-worker/fetch-csp.https.html
index 91a774a..4f17622 100644
--- a/service-workers/service-worker/fetch-csp.https.html
+++ b/service-workers/service-worker/fetch-csp.https.html
@@ -32,6 +32,10 @@
 
     return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, SCOPE);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() {
@@ -105,7 +109,6 @@
         })
       .then(function() {
           frame.remove();
-          service_worker_unregister_and_done(t, SCOPE);
         });
   }, 'Verify CSP control of fetch() in a Service Worker');
 </script>
diff --git a/service-workers/service-worker/fetch-event-after-navigation-within-page.https.html b/service-workers/service-worker/fetch-event-after-navigation-within-page.https.html
index dce1f79..4812d8a 100644
--- a/service-workers/service-worker/fetch-event-after-navigation-within-page.https.html
+++ b/service-workers/service-worker/fetch-event-after-navigation-within-page.https.html
@@ -15,6 +15,10 @@
 
     return service_worker_unregister_and_register(t, worker, scope)
       .then(function(reg) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, reg.installing, 'activated');
         })
       .then(function() { return with_iframe(scope); })
@@ -30,7 +34,6 @@
       .then(function(response) {
           assert_equals(response, 'intercepted by service worker');
           frame.remove();
-          return service_worker_unregister_and_done(t, scope);
         })
   }, 'Service Worker should respond to fetch event after the hash changes');
 
@@ -43,6 +46,10 @@
 
     return service_worker_unregister_and_register(t, worker, scope)
       .then(function(reg) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, reg.installing, 'activated');
         })
       .then(function() { return with_iframe(scope); })
@@ -58,7 +65,6 @@
       .then(function(response) {
           assert_equals(response, 'intercepted by service worker');
           frame.remove();
-          return service_worker_unregister_and_done(t, scope);
         })
   }, 'Service Worker should respond to fetch event after the pushState');
 
diff --git a/service-workers/service-worker/fetch-event-async-respond-with.https.html b/service-workers/service-worker/fetch-event-async-respond-with.https.html
index a2b93ac..87fa046 100644
--- a/service-workers/service-worker/fetch-event-async-respond-with.https.html
+++ b/service-workers/service-worker/fetch-event-async-respond-with.https.html
@@ -9,6 +9,10 @@
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() {
@@ -27,7 +31,6 @@
         })
       .then(function(message) {
           assert_equals(message, 'PASS');
-          return service_worker_unregister_and_done(t, scope);
         })
   }, 'Calling respondWith asynchronously throws an exception');
 </script>
diff --git a/service-workers/service-worker/fetch-event-network-error.https.html b/service-workers/service-worker/fetch-event-network-error.https.html
index 254919e..fea2ad1 100644
--- a/service-workers/service-worker/fetch-event-network-error.https.html
+++ b/service-workers/service-worker/fetch-event-network-error.https.html
@@ -22,6 +22,10 @@
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() {
@@ -34,7 +38,6 @@
       .then(function(result) {
           frame.remove();
           assert_equals(result, 'PASS');
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Rejecting the fetch event or using preventDefault() causes a network ' +
      'error');
diff --git a/service-workers/service-worker/fetch-event-respond-with-argument.https.html b/service-workers/service-worker/fetch-event-respond-with-argument.https.html
index c78fb78..05e2210 100644
--- a/service-workers/service-worker/fetch-event-respond-with-argument.https.html
+++ b/service-workers/service-worker/fetch-event-respond-with-argument.https.html
@@ -22,6 +22,10 @@
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() {
@@ -34,7 +38,6 @@
       .then(function(result) {
           frame.remove();
           assert_equals(result, 'PASS');
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'respondWith() takes either a Response or a promise that resolves ' +
      'with a Response. Other values should raise a network error.');
diff --git a/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html b/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html
index cd6861a..31fd616 100644
--- a/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html
+++ b/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html
@@ -10,6 +10,10 @@
 
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, registration.installing, 'activated');
         })
       .then(function() {
@@ -28,7 +32,6 @@
         })
       .then(function(message) {
           assert_equals(message, 'PASS');
-          return service_worker_unregister_and_done(t, scope);
         })
   }, 'respondWith() invokes stopImmediatePropagation()');
 </script>
diff --git a/service-workers/service-worker/fetch-event-throws-after-respond-with.https.html b/service-workers/service-worker/fetch-event-throws-after-respond-with.https.html
index 969a3c9..d98fb22 100644
--- a/service-workers/service-worker/fetch-event-throws-after-respond-with.https.html
+++ b/service-workers/service-worker/fetch-event-throws-after-respond-with.https.html
@@ -12,6 +12,10 @@
     var iframe;
     return service_worker_unregister_and_register(t, workerscript, scope)
       .then(function(reg) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, reg.installing, 'activated')
             .then(() => reg.active);
         })
@@ -27,7 +31,6 @@
         })
       .then(function(frame) {
         assert_true(frame.contentDocument.body.innerHTML.includes("intercepted"));
-        service_worker_unregister_and_done(t, scope);
       })
   }, 'Fetch event handler throws after a successful respondWith()');
 
diff --git a/service-workers/service-worker/fetch-request-redirect.https.html b/service-workers/service-worker/fetch-request-redirect.https.html
index e8e90cf..57fb8eb 100644
--- a/service-workers/service-worker/fetch-request-redirect.https.html
+++ b/service-workers/service-worker/fetch-request-redirect.https.html
@@ -74,6 +74,8 @@
     var frame;
     return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
       .then(function(registration) {
+          t.add_cleanup(() => service_worker_unregister(t, SCOPE));
+
           worker = registration.installing;
           return wait_for_state(t, worker, 'activated');
         })
@@ -181,7 +183,6 @@
         })
       .then(function() {
           frame.remove();
-          service_worker_unregister_and_done(t, SCOPE);
         });
   }, 'Verify redirect mode of Fetch API and ServiceWorker FetchEvent.');
 
@@ -208,6 +209,8 @@
     var frame;
     return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
       .then(function(registration) {
+          t.add_cleanup(() => service_worker_unregister(t, SCOPE));
+
           worker = registration.installing;
           return wait_for_state(t, worker, 'activated');
         })
@@ -275,7 +278,6 @@
         })
       .then(function() {
           frame.remove();
-          service_worker_unregister_and_done(t, SCOPE);
         });
   }, 'Verify redirected of Response(Fetch API) and ServiceWorker FetchEvent.');
 
@@ -302,6 +304,8 @@
     var frame;
     return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
       .then(function(registration) {
+          t.add_cleanup(() => service_worker_unregister(t, SCOPE));
+
           worker = registration.installing;
           return wait_for_state(t, worker, 'activated');
         })
@@ -375,7 +379,6 @@
         })
       .then(function() {
           frame.remove();
-          service_worker_unregister_and_done(t, SCOPE);
         });
   }, 'Verify redirected of Response(Fetch API), Cache API and ServiceWorker ' +
      'FetchEvent.');
diff --git a/service-workers/service-worker/multiple-update.https.html b/service-workers/service-worker/multiple-update.https.html
index 84aac95..6a83f73 100644
--- a/service-workers/service-worker/multiple-update.https.html
+++ b/service-workers/service-worker/multiple-update.https.html
@@ -15,6 +15,10 @@
 
     return service_worker_unregister_and_register(t, expected_url, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           registration = r;
           return wait_for_state(t, registration.installing, 'activated');
         })
@@ -85,8 +89,6 @@
                         'waiting should be null after activated.');
           assert_equals(registration.active.scriptURL, expected_url,
                         'active should still exist after update found.');
-
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Trigger multiple updates.');
 </script>
diff --git a/service-workers/service-worker/performance-timeline.https.html b/service-workers/service-worker/performance-timeline.https.html
index b66d4a8..1fe19da 100644
--- a/service-workers/service-worker/performance-timeline.https.html
+++ b/service-workers/service-worker/performance-timeline.https.html
@@ -17,7 +17,11 @@
   let slowURL = url + '&slow';
   let frame;
   return service_worker_unregister_and_register(t, script, scope)
-    .then(reg => wait_for_state(t, reg.installing, 'activated'))
+    .then(reg => {
+        t.add_cleanup(() => service_worker_unregister(t, scope));
+
+        return wait_for_state(t, reg.installing, 'activated');
+      })
     .then(_ => with_iframe(scope))
     .then(f => {
       frame = f;
@@ -39,7 +43,6 @@
       assert_greater_than(slowURLTime, urlTime + 1000,
                   'Slow service worker request should measure increased delay.');
       frame.remove();
-      return service_worker_unregister_and_done(t, scope);
     })
 }, 'empty service worker fetch event included in performance timings');
 
diff --git a/service-workers/service-worker/register-default-scope.https.html b/service-workers/service-worker/register-default-scope.https.html
index dc136d4..1d86548 100644
--- a/service-workers/service-worker/register-default-scope.https.html
+++ b/service-workers/service-worker/register-default-scope.https.html
@@ -52,8 +52,11 @@
       })
       .then(
         function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, registration.scope);
+            });
+
           assert_unreached('register should fail');
-          service_worker_unregister_and_done(t, registration.scope);
         },
         function(error) {
           assert_equals(error.name, 'SecurityError',
diff --git a/service-workers/service-worker/register-wait-forever-in-install-worker.https.html b/service-workers/service-worker/register-wait-forever-in-install-worker.https.html
index e23f9f4..0920b5c 100644
--- a/service-workers/service-worker/register-wait-forever-in-install-worker.https.html
+++ b/service-workers/service-worker/register-wait-forever-in-install-worker.https.html
@@ -16,6 +16,10 @@
 
     return navigator.serviceWorker.register(bad_script, {scope: scope})
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           registration = r;
           assert_equals(registration.installing.scriptURL,
                         normalizeURL(bad_script));
@@ -47,9 +51,6 @@
                         normalizeURL(good_script));
           return wait_for_state(t, registration.installing, 'activated');
         })
-      .then(function() {
-          return service_worker_unregister_and_done(t, scope);
-        })
   }, 'register worker that calls waitUntil with a promise that never ' +
      'resolves in oninstall');
 
diff --git a/service-workers/service-worker/serviceworker-message-event-historical.https.html b/service-workers/service-worker/serviceworker-message-event-historical.https.html
index 2f780a6..fac8f20 100644
--- a/service-workers/service-worker/serviceworker-message-event-historical.https.html
+++ b/service-workers/service-worker/serviceworker-message-event-historical.https.html
@@ -10,6 +10,10 @@
     var url = 'resources/postmessage-to-client-worker.js';
     return service_worker_unregister_and_register(t, url, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, r.installing, 'activated');
         })
       .then(function() {
@@ -35,9 +39,6 @@
                 });
               worker.postMessage('PING');
             });
-        })
-      .then(function() {
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Test MessageEvent supplants ServiceWorkerMessageEvent.');
 
diff --git a/service-workers/service-worker/shared-worker-controlled.https.html b/service-workers/service-worker/shared-worker-controlled.https.html
index 33d52e0..0320c02 100644
--- a/service-workers/service-worker/shared-worker-controlled.https.html
+++ b/service-workers/service-worker/shared-worker-controlled.https.html
@@ -12,6 +12,10 @@
 
     return service_worker_unregister_and_register(t, service_worker, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, r.installing, 'activated');
         })
       .then(function() {
@@ -24,7 +28,6 @@
         })
       .then(function(data) {
           assert_equals(data, 'intercepted by service worker');
-          service_worker_unregister_and_done(t, scope);
         });
   }, 'Verify subresource loads in SharedWorker are controlled by a Service Worker');
 
@@ -35,6 +38,10 @@
 
     return service_worker_unregister_and_register(t, service_worker, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, r.installing, 'activated');
         })
       .then(function() {
@@ -47,7 +54,6 @@
         })
       .then(function(data) {
           assert_equals(data, 'worker loading intercepted by service worker');
-          service_worker_unregister_and_done(t, scope);
         });
   }, 'Verify SharedWorker construction is controlled by a Service Worker');
 
@@ -58,6 +64,10 @@
 
     return service_worker_unregister_and_register(t, service_worker, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, r.installing, 'activated');
         })
       .then(function() {
@@ -70,7 +80,6 @@
         })
       .then(function(data) {
           assert_equals(data, 'worker loading intercepted by service worker');
-          service_worker_unregister_and_done(t, scope);
         });
   }, 'Verify importScripts from SharedWorker is controlled by a Service Worker');
 </script>
diff --git a/service-workers/service-worker/skip-waiting-installed.https.html b/service-workers/service-worker/skip-waiting-installed.https.html
index 21e26be..b604f65 100644
--- a/service-workers/service-worker/skip-waiting-installed.https.html
+++ b/service-workers/service-worker/skip-waiting-installed.https.html
@@ -33,6 +33,10 @@
       });
     return service_worker_unregister_and_register(t, url1, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, r.installing, 'activated');
         })
       .then(function() {
@@ -60,7 +64,6 @@
         })
       .then(function() {
           frame.remove();
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Test skipWaiting when a installed worker is waiting');
 
diff --git a/service-workers/service-worker/skip-waiting.https.html b/service-workers/service-worker/skip-waiting.https.html
index 48b5a8c..f8392fc 100644
--- a/service-workers/service-worker/skip-waiting.https.html
+++ b/service-workers/service-worker/skip-waiting.https.html
@@ -14,6 +14,10 @@
     var sw_registration, activated_worker, waiting_worker;
     return service_worker_unregister_and_register(t, url1, scope)
       .then(function(registration) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           sw_registration = registration;
           return wait_for_state(t, registration.installing, 'activated');
         })
@@ -48,7 +52,6 @@
                         'Worker with url2 should be redundant');
           assert_equals(sw_registration.active.scriptURL, normalizeURL(url3),
                         'Worker with url3 should be activated');
-          return service_worker_unregister_and_done(t, scope);
         });
   }, 'Test skipWaiting with both active and waiting workers');
 
diff --git a/service-workers/service-worker/update-after-oneday.https.html b/service-workers/service-worker/update-after-oneday.https.html
index 08065d2..d9b9fcf 100644
--- a/service-workers/service-worker/update-after-oneday.https.html
+++ b/service-workers/service-worker/update-after-oneday.https.html
@@ -17,6 +17,10 @@
 
     return service_worker_unregister_and_register(t, expected_url, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           registration = r;
           return wait_for_state(t, registration.installing, 'activated');
         })
@@ -41,7 +45,6 @@
        })
        .then(function() {
           frame.remove();
-          return service_worker_unregister_and_done(t, scope);
        })
   }, 'Update should be triggered after a functional event when last update time is over 24 hours');
 
diff --git a/service-workers/service-worker/update-bytecheck.https.html b/service-workers/service-worker/update-bytecheck.https.html
index ec3d15a..be41435 100644
--- a/service-workers/service-worker/update-bytecheck.https.html
+++ b/service-workers/service-worker/update-bytecheck.https.html
@@ -43,8 +43,11 @@
     return Promise.resolve()
       // Register a service worker.
       .then(_ => service_worker_unregister_and_register(t, script, scope))
-      .then(r => swr = r)
-      .then(_ => wait_for_update(t, swr))
+      .then(r => {
+        t.add_cleanup(() => service_worker_unregister(t, scope));
+        swr = r;
+        return wait_for_update(t, swr);
+      })
       .then(w => sw = w)
       .then(_ => wait_for_state(t, sw, 'activated'))
       .then(_ => assert_array_equals([swr.active,
@@ -64,10 +67,7 @@
                               swr.waiting,
                               swr.installing],
                              [sw, null, null]);
-      })
-
-      // Unregister the service worker.
-      .then(_ => service_worker_unregister_and_done(t, scope));
+      });
   }, `Test(cors: ${s.cors}, main: ${s.main}, imported: ${s.imported})`));
 }, Promise.resolve());
 
diff --git a/service-workers/service-worker/windowclient-navigate.https.html b/service-workers/service-worker/windowclient-navigate.https.html
index 8ea279e..e62c1ac 100644
--- a/service-workers/service-worker/windowclient-navigate.https.html
+++ b/service-workers/service-worker/windowclient-navigate.https.html
@@ -109,7 +109,7 @@
         });
     }
 
-    var cleanup = function() {
+    test.add_cleanup(function() {
       if (client_frame && client_frame) {
         client_frame.remove();
       }
@@ -127,9 +127,9 @@
       if (registration) {
         return registration.unregister();
       }
-    };
+    });
 
-    var test_body = with_iframe(parameters.src_url)
+    return with_iframe(parameters.src_url)
       .then(function(frame) {
           client_frame = frame;
           return service_worker_unregister_and_register(
@@ -161,17 +161,6 @@
       .then(function(response) {
           assert_equals(response.data, parameters.expected);
         });
-
-    // Ensure that test "clean up" is deferred until after the test body
-    // executes. `Test#add_cleanup` cannot be used for this purpose because the
-    // operation is asynchronous, and `add_cleanup` does not support
-    // asynchronous operations at the time of this writing. See
-    // https://github.com/web-platform-tests/wpt/issues/6075
-    // Ensure also that test failure is not hidden by successful cleanup
-    // operation.
-    return test_body
-      .then(cleanup, cleanup)
-      .then(function() { return test_body; });
   }, parameters.description);
 }
 </script>
diff --git a/service-workers/service-worker/worker-interception.https.html b/service-workers/service-worker/worker-interception.https.html
index bf976a2..f9ba656 100644
--- a/service-workers/service-worker/worker-interception.https.html
+++ b/service-workers/service-worker/worker-interception.https.html
@@ -12,6 +12,10 @@
 
     return service_worker_unregister_and_register(t, service_worker, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, r.installing, 'activated');
         })
       .then(function() {
@@ -28,7 +32,6 @@
         })
       .then(function(data) {
           assert_equals(data, 'worker loading intercepted by service worker');
-          service_worker_unregister_and_done(t, scope);
         });
   }, 'Verify worker script from uncontrolled document is intercepted by Service Worker');
 
@@ -39,6 +42,10 @@
 
     return service_worker_unregister_and_register(t, service_worker, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, r.installing, 'activated');
         })
       .then(function() {
@@ -55,7 +62,6 @@
         })
       .then(function(data) {
           assert_equals(data, 'dummy-worker-script loaded');
-          service_worker_unregister_and_done(t, scope);
         });
   }, 'Verify worker script intercepted by same-origin response succeeds');
 
@@ -72,10 +78,7 @@
           var w = new Worker(worker_url);
           var watcher = new EventWatcher(t, w, ['message', 'error']);
           return watcher.wait_for('error');
-        })
-      .then(function() {
-          service_worker_unregister_and_done(t, scope);
-       });
+        });
   }, 'Verify worker script intercepted by cors response fails');
 
 promise_test(function(t) {
@@ -85,16 +88,17 @@
 
     return service_worker_unregister_and_register(t, service_worker, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, r.installing, 'activated');
         })
       .then(function() {
           var w = new Worker(worker_url);
           var watcher = new EventWatcher(t, w, ['message', 'error']);
           return watcher.wait_for('error');
-        })
-      .then(function() {
-          service_worker_unregister_and_done(t, scope);
-       });
+        });
   }, 'Verify worker script intercepted by no-cors cross-origin response fails');
 
 promise_test(function(t) {
@@ -110,6 +114,10 @@
 
     return service_worker_unregister_and_register(t, service_worker, scope)
       .then(function(r) {
+          t.add_cleanup(function() {
+              return service_worker_unregister(t, scope);
+            });
+
           return wait_for_state(t, r.installing, 'activated');
         })
       .then(function() { return with_iframe(subdoc_url); })
@@ -126,7 +134,6 @@
         })
       .then(function(data) {
           assert_equals(data.results, 'finish');
-          service_worker_unregister_and_done(t, scope);
         });
   }, 'Verify worker loads from controlled document are intercepted by Service Worker');