Bug 1263304 - Add wpt tests for async waitUntil(). r=bkelly
☠☠ backed out by 5ad94ec48c1f ☠ ☠
authorCatalin Badea <catalin.badea392@gmail.com>
Fri, 11 Nov 2016 00:07:15 +0200
changeset 348805 15f15b242acd7d6a8e0355b660cf16e604b68e47
parent 348804 3ded216ab58b2c2f5106d34dcf17e793b154f466
child 348806 829e818cd4c3d7aa0431cedef21d2b0a972fb503
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1263304
milestone52.0a1
Bug 1263304 - Add wpt tests for async waitUntil(). r=bkelly
testing/web-platform/tests/service-workers/service-worker/extendable-event-async-waituntil.https.html
testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
--- a/testing/web-platform/tests/service-workers/service-worker/extendable-event-async-waituntil.https.html
+++ b/testing/web-platform/tests/service-workers/service-worker/extendable-event-async-waituntil.https.html
@@ -1,30 +1,82 @@
 <!DOCTYPE html>
 <script src="/resources/testharness.js"></script>
 <script src="resources/testharness-helpers.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/test-helpers.sub.js"></script>
 <script>
-promise_test(function(t) {
-    var script = 'resources/extendable-event-async-waituntil.js';
-    var scope = 'resources/async-waituntil';
-    var worker;
+
+function sync_message(worker, message, transfer) {
+  let wait = new Promise((res, rej) => {
+    navigator.serviceWorker.addEventListener('message', function(e) {
+        if (e.data === 'ACK') {
+          res();
+        } else {
+          rej();
+        }
+      });
+    });
+  worker.postMessage(message, transfer);
+  return wait;
+}
+
+function runTest(test, step, testBody) {
+  var scope = './resources/' + step;
+  var script = 'resources/extendable-event-async-waituntil.js?' + scope;
+  service_worker_unregister_and_register(test, script, scope)
+    .then(function(registration) {
+        let worker = registration.installing;
+        var channel = new MessageChannel();
+        var saw_message = new Promise(function(resolve) {
+          channel.port1.onmessage = function(e) { resolve(e.data); }
+        });
 
-    return service_worker_unregister_and_register(t, script, scope)
-      .then(function(registration) {
-          worker = registration.installing;
-          return wait_for_state(t, worker, 'activated');
-        })
-      .then(function() {
-          var channel = new MessageChannel();
-          var saw_message = new Promise(function(resolve) {
-              channel.port1.onmessage = function(e) { resolve(e.data); }
-            });
-          worker.postMessage({port: channel.port2}, [channel.port2]);
-          return saw_message;
-        })
-      .then(function(message) {
-          assert_equals(message, 'PASS');
-          return service_worker_unregister_and_done(t, scope);
-        })
-  }, 'Calling waitUntil asynchronously throws an exception');
+        wait_for_state(test, worker, 'activated')
+          .then(function() {
+              return sync_message(worker, { step: 'init', port: channel.port2 },
+                [channel.port2]);
+            })
+          .then(function() { return testBody(worker); })
+          .then(function() { return saw_message; })
+          .then(function(output) {
+              assert_equals(output.result, output.expected);
+            })
+          .then(function() { return sync_message(worker, { step: 'done' }); })
+          .then(() => { service_worker_unregister_and_done(test, scope); })
+          .catch(unreached_rejection(test));
+      });
+}
+
+function msg_event_test(scope, test) {
+  var testBody = function(worker) {
+    return sync_message(worker, { step: scope });
+  };
+  runTest(test, scope, testBody);
+}
+
+async_test(msg_event_test.bind(this, 'no-current-extension-different-task'),
+  'Test calling waitUntil in a different task without an existing extension throws');
+
+async_test(msg_event_test.bind(this, 'no-current-extension-different-microtask'),
+  'Test calling waitUntil in a different microtask without an existing extension throws');
+
+async_test(msg_event_test.bind(this, 'current-extension-different-task'),
+  'Test calling waitUntil in a different task with an existing extension succeeds');
+
+async_test(msg_event_test.bind(this, 'current-extension-expired-same-microtask-turn'),
+  'Test calling waitUntil with an existing extension promise handler succeeds');
+
+// The promise handler will queue a new microtask after the check for new
+// extensions was performed.
+async_test(msg_event_test.bind(this, 'current-extension-expired-same-microtask-turn-extra'),
+  'Test calling waitUntil at the end of the microtask turn throws');
+
+async_test(msg_event_test.bind(this, 'current-extension-expired-different-task'),
+  'Test calling waitUntil after the current extension expired in a different task fails');
+
+async_test(function(t) {
+    var testBody = function(worker) {
+      return with_iframe('./resources/pending-respondwith-async-waituntil/dummy.html');
+    }
+    runTest(t, 'pending-respondwith-async-waituntil', testBody);
+  }, 'Test calling waitUntil asynchronously with pending respondWith promise.');
 </script>
--- a/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
@@ -1,20 +1,100 @@
-var result = 'FAIL: did not throw.';
+// controlled by 'init'/'done' messages.
+var resolveLockPromise;
+var port;
 
 self.addEventListener('message', function(event) {
-    event.data.port.postMessage(result);
-  });
+    var waitPromise;
+    var resolveTestPromise;
 
-self.addEventListener('install', function(event) {
-    self.installEvent = event;
+    switch (event.data.step) {
+      case 'init':
+        event.waitUntil(new Promise((res) => { resolveLockPromise = res; }));
+        port = event.data.port;
+        break;
+      case 'done':
+        resolveLockPromise();
+        break;
+      case 'no-current-extension-different-task':
+        async_task_waituntil(event).then(reportResultExpecting('InvalidStateError'));
+        break;
+      case 'no-current-extension-different-microtask':
+        async_microtask_waituntil(event).then(reportResultExpecting('InvalidStateError'));
+        break;
+      case 'current-extension-different-task':
+        event.waitUntil(new Promise((res) => { resolveTestPromise = res; }));
+        async_task_waituntil(event).then(reportResultExpecting('OK')).then(resolveTestPromise);
+        break;
+      case 'current-extension-expired-same-microtask-turn':
+        waitPromise = Promise.resolve();
+        event.waitUntil(waitPromise);
+        waitPromise.then(() => { return sync_waituntil(event); })
+          .then(reportResultExpecting('OK'))
+        break;
+      case 'current-extension-expired-same-microtask-turn-extra':
+        // The promise handler queues a new microtask *after* the check for new
+        // extensions was performed.
+        waitPromise = Promise.resolve();
+        event.waitUntil(waitPromise);
+        waitPromise.then(() => { return async_microtask_waituntil(event); })
+          .then(reportResultExpecting('InvalidStateError'))
+        break;
+      case 'current-extension-expired-different-task':
+        event.waitUntil(Promise.resolve());
+        async_task_waituntil(event).then(reportResultExpecting('InvalidStateError'));
+        break;
+    }
+    event.source.postMessage('ACK');
   });
 
-self.addEventListener('activate', function(event) {
+self.addEventListener('fetch', function(event) {
+    var resolveFetch;
+    let response = new Promise((res) => { resolveFetch = res; });
+    event.respondWith(response);
+    async_task_waituntil(event)
+      .then(reportResultExpecting('OK'))
+      .then(() => { resolveFetch(new Response('OK')); });
+  });
+
+function reportResultExpecting(expectedResult) {
+  return function (result) {
+    port.postMessage({result : result, expected: expectedResult});
+    return result;
+  };
+}
+
+function sync_waituntil(event) {
+  return new Promise((res, rej) => {
     try {
-      self.installEvent.waitUntil(new Promise(function(){}));
-    } catch (error) {
-      if (error.name == 'InvalidStateError')
-        result = 'PASS';
-      else
-        result = 'FAIL: unexpected exception: ' + error;
-    }
+        event.waitUntil(Promise.resolve());
+        res('OK');
+      } catch (error) {
+        res(error.name);
+      }
   });
+}
+
+function async_microtask_waituntil(event) {
+  return new Promise((res, rej) => {
+    Promise.resolve().then(() => {
+      try {
+        event.waitUntil(Promise.resolve());
+        res('OK');
+      } catch (error) {
+        res(error.name);
+      }
+    });
+  });
+}
+
+function async_task_waituntil(event) {
+  return new Promise((res, rej) => {
+    setTimeout(() => {
+      try {
+        event.waitUntil(Promise.resolve());
+        res('OK');
+      } catch (error) {
+        res(error.name);
+      }
+    }, 0);
+  });
+}