diff options
Diffstat (limited to 'testing/web-platform/tests/web-locks/signal.tentative.https.any.js')
-rw-r--r-- | testing/web-platform/tests/web-locks/signal.tentative.https.any.js | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/testing/web-platform/tests/web-locks/signal.tentative.https.any.js b/testing/web-platform/tests/web-locks/signal.tentative.https.any.js new file mode 100644 index 0000000000..5a37e3ae87 --- /dev/null +++ b/testing/web-platform/tests/web-locks/signal.tentative.https.any.js @@ -0,0 +1,261 @@ +// META: title=Web Locks API: AbortSignal integration +// META: script=resources/helpers.js +// META: global=window,dedicatedworker,sharedworker,serviceworker + +'use strict'; + +promise_test(async t => { + const res = uniqueName(t); + + // These cases should not work: + for (const signal of ['string', 12.34, false, {}, Symbol(), () => {}, self]) { + await promise_rejects_js( + t, TypeError, + navigator.locks.request( + res, {signal}, t.unreached_func('callback should not run')), + 'Bindings should throw if the signal option is a not an AbortSignal'); + } +}, 'The signal option must be an AbortSignal'); + +promise_test(async t => { + const res = uniqueName(t); + const controller = new AbortController(); + controller.abort(); + + await promise_rejects_dom( + t, 'AbortError', + navigator.locks.request(res, {signal: controller.signal}, + t.unreached_func('callback should not run')), + 'Request should reject with AbortError'); +}, 'Passing an already aborted signal aborts'); + +promise_test(async t => { + const res = uniqueName(t); + + const controller = new AbortController(); + const reason = 'My dog ate it.'; + controller.abort(reason); + + const promise = + navigator.locks.request(res, {signal: controller.signal}, + t.unreached_func('callback should not run')); + + await promise_rejects_exactly( + t, reason, promise, "Rejection should give the abort reason"); +}, 'Passing an already aborted signal rejects with the custom abort reason.'); + +promise_test(async t => { + const res = uniqueName(t); + + const controller = new AbortController(); + controller.abort(); + + const promise = + navigator.locks.request(res, {signal: controller.signal}, + t.unreached_func('callback should not run')); + + await promise_rejects_exactly( + t, controller.signal.reason, promise, + "Rejection should give the abort reason"); +}, 'Passing an already aborted signal rejects with the default abort reason.'); + +promise_test(async t => { + const res = uniqueName(t); + + // Grab a lock and hold it until this subtest completes. + requestLockAndHold(t, res); + + const controller = new AbortController(); + + const promise = + navigator.locks.request(res, {signal: controller.signal}, + t.unreached_func('callback should not run')); + + // Verify the request is enqueued: + const state = await navigator.locks.query(); + assert_equals(state.held.filter(lock => lock.name === res).length, 1, + 'Number of held locks'); + assert_equals(state.pending.filter(lock => lock.name === res).length, 1, + 'Number of pending locks'); + + const rejected = promise_rejects_dom( + t, 'AbortError', promise, 'Request should reject with AbortError'); + + controller.abort(); + + await rejected; + +}, 'An aborted request results in AbortError'); + +promise_test(async t => { + const res = uniqueName(t); + + // Grab a lock and hold it until this subtest completes. + requestLockAndHold(t, res); + + const controller = new AbortController(); + + const promise = + navigator.locks.request(res, {signal: controller.signal}, lock => {}); + + // Verify the request is enqueued: + const state = await navigator.locks.query(); + assert_equals(state.held.filter(lock => lock.name === res).length, 1, + 'Number of held locks'); + assert_equals(state.pending.filter(lock => lock.name === res).length, 1, + 'Number of pending locks'); + + const rejected = promise_rejects_dom( + t, 'AbortError', promise, 'Request should reject with AbortError'); + + let callback_called = false; + t.step_timeout(() => { + callback_called = true; + controller.abort(); + }, 10); + + await rejected; + assert_true(callback_called, 'timeout should have caused the abort'); + +}, 'Abort after a timeout'); + +promise_test(async t => { + const res = uniqueName(t); + + const controller = new AbortController(); + + let got_lock = false; + await navigator.locks.request( + res, {signal: controller.signal}, async lock => { got_lock = true; }); + + assert_true(got_lock, 'Lock should be acquired if abort is not signaled.'); + +}, 'Signal that is not aborted'); + +promise_test(async t => { + const res = uniqueName(t); + + const controller = new AbortController(); + + let got_lock = false; + const p = navigator.locks.request( + res, {signal: controller.signal}, lock => { got_lock = true; }); + + // Even though lock is grantable, this abort should be processed synchronously. + controller.abort(); + + await promise_rejects_dom(t, 'AbortError', p, 'Request should abort'); + + assert_false(got_lock, 'Request should be aborted if signal is synchronous'); + + await navigator.locks.request(res, lock => { got_lock = true; }); + assert_true(got_lock, 'Subsequent request should not be blocked'); + +}, 'Synchronously signaled abort'); + +promise_test(async t => { + const res = uniqueName(t); + + const controller = new AbortController(); + + // Make a promise that resolves when the lock is acquired. + const [acquired_promise, acquired_func] = makePromiseAndResolveFunc(); + + // Request the lock. + let release_func; + const released_promise = navigator.locks.request( + res, {signal: controller.signal}, lock => { + acquired_func(); + + // Hold lock until release_func is called. + const [waiting_promise, waiting_func] = makePromiseAndResolveFunc(); + release_func = waiting_func; + return waiting_promise; + }); + + // Wait for the lock to be acquired. + await acquired_promise; + + // Signal an abort. + controller.abort(); + + // Release the lock. + release_func('resolved ok'); + + assert_equals(await released_promise, 'resolved ok', + 'Lock released promise should not reject'); + +}, 'Abort signaled after lock granted'); + +promise_test(async t => { + const res = uniqueName(t); + + const controller = new AbortController(); + + // Make a promise that resolves when the lock is acquired. + const [acquired_promise, acquired_func] = makePromiseAndResolveFunc(); + + // Request the lock. + let release_func; + const released_promise = navigator.locks.request( + res, {signal: controller.signal}, lock => { + acquired_func(); + + // Hold lock until release_func is called. + const [waiting_promise, waiting_func] = makePromiseAndResolveFunc(); + release_func = waiting_func; + return waiting_promise; + }); + + // Wait for the lock to be acquired. + await acquired_promise; + + // Release the lock. + release_func('resolved ok'); + + // Signal an abort. + controller.abort(); + + assert_equals(await released_promise, 'resolved ok', + 'Lock released promise should not reject'); + +}, 'Abort signaled after lock released'); + +promise_test(async t => { + const res = uniqueName(t); + + const controller = new AbortController(); + const first = requestLockAndHold(t, res, { signal: controller.signal }); + const next = navigator.locks.request(res, () => "resolved"); + controller.abort(); + + await promise_rejects_dom(t, "AbortError", first, "Request should abort"); + assert_equals( + await next, + "resolved", + "The next request is processed after abort" + ); +}, "Abort should process the next pending lock request"); + +promise_test(async t => { + const res = uniqueName(t); + + const controller = new AbortController(); + const promise = requestLockAndHold(t, res, { signal: controller.signal }); + + const reason = "My cat handled it"; + controller.abort(reason); + + await promise_rejects_exactly(t, reason, promise, "Rejection should give the abort reason"); +}, "Aborted promise should reject with the custom abort reason"); + +promise_test(async t => { + const res = uniqueName(t); + + const controller = new AbortController(); + const promise = requestLockAndHold(t, res, { signal: controller.signal }); + + controller.abort(); + + await promise_rejects_exactly(t, controller.signal.reason, promise, "Should be the same reason"); +}, "Aborted promise should reject with the default abort reason"); |