summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/streams/writable-streams/close.any.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/streams/writable-streams/close.any.js')
-rw-r--r--testing/web-platform/tests/streams/writable-streams/close.any.js470
1 files changed, 470 insertions, 0 deletions
diff --git a/testing/web-platform/tests/streams/writable-streams/close.any.js b/testing/web-platform/tests/streams/writable-streams/close.any.js
new file mode 100644
index 0000000000..88855a92ef
--- /dev/null
+++ b/testing/web-platform/tests/streams/writable-streams/close.any.js
@@ -0,0 +1,470 @@
+// META: global=window,worker
+// META: script=../resources/test-utils.js
+// META: script=../resources/recording-streams.js
+'use strict';
+
+const error1 = new Error('error1');
+error1.name = 'error1';
+
+const error2 = new Error('error2');
+error2.name = 'error2';
+
+promise_test(() => {
+ const ws = new WritableStream({
+ close() {
+ return 'Hello';
+ }
+ });
+
+ const writer = ws.getWriter();
+
+ const closePromise = writer.close();
+ return closePromise.then(value => assert_equals(value, undefined, 'fulfillment value must be undefined'));
+}, 'fulfillment value of writer.close() call must be undefined even if the underlying sink returns a non-undefined ' +
+ 'value');
+
+promise_test(() => {
+ let controller;
+ let resolveClose;
+ const ws = new WritableStream({
+ start(c) {
+ controller = c;
+ },
+ close() {
+ return new Promise(resolve => {
+ resolveClose = resolve;
+ });
+ }
+ });
+
+ const writer = ws.getWriter();
+
+ const closePromise = writer.close();
+ return flushAsyncEvents().then(() => {
+ controller.error(error1);
+ return flushAsyncEvents();
+ }).then(() => {
+ resolveClose();
+ return Promise.all([
+ closePromise,
+ writer.closed,
+ flushAsyncEvents().then(() => writer.closed)]);
+ });
+}, 'when sink calls error asynchronously while sink close is in-flight, the stream should not become errored');
+
+promise_test(() => {
+ let controller;
+ const passedError = new Error('error me');
+ const ws = new WritableStream({
+ start(c) {
+ controller = c;
+ },
+ close() {
+ controller.error(passedError);
+ }
+ });
+
+ const writer = ws.getWriter();
+
+ return writer.close().then(() => writer.closed);
+}, 'when sink calls error synchronously while closing, the stream should not become errored');
+
+promise_test(t => {
+ const ws = new WritableStream({
+ close() {
+ throw error1;
+ }
+ });
+
+ const writer = ws.getWriter();
+
+ return Promise.all([
+ writer.write('y'),
+ promise_rejects_exactly(t, error1, writer.close(), 'close() must reject with the error'),
+ promise_rejects_exactly(t, error1, writer.closed, 'closed must reject with the error')
+ ]);
+}, 'when the sink throws during close, and the close is requested while a write is still in-flight, the stream should ' +
+ 'become errored during the close');
+
+promise_test(() => {
+ const ws = new WritableStream({
+ write(chunk, controller) {
+ controller.error(error1);
+ return new Promise(() => {});
+ }
+ });
+
+ const writer = ws.getWriter();
+ writer.write('a');
+
+ return delay(0).then(() => {
+ writer.releaseLock();
+ });
+}, 'releaseLock on a stream with a pending write in which the stream has been errored');
+
+promise_test(() => {
+ let controller;
+ const ws = new WritableStream({
+ start(c) {
+ controller = c;
+ },
+ close() {
+ controller.error(error1);
+ return new Promise(() => {});
+ }
+ });
+
+ const writer = ws.getWriter();
+ writer.close();
+
+ return delay(0).then(() => {
+ writer.releaseLock();
+ });
+}, 'releaseLock on a stream with a pending close in which controller.error() was called');
+
+promise_test(() => {
+ const ws = recordingWritableStream();
+
+ const writer = ws.getWriter();
+
+ return writer.ready.then(() => {
+ assert_equals(writer.desiredSize, 1, 'desiredSize should be 1');
+
+ writer.close();
+ assert_equals(writer.desiredSize, 1, 'desiredSize should be still 1');
+
+ return writer.ready.then(v => {
+ assert_equals(v, undefined, 'ready promise should be fulfilled with undefined');
+ assert_array_equals(ws.events, ['close'], 'write and abort should not be called');
+ });
+ });
+}, 'when close is called on a WritableStream in writable state, ready should return a fulfilled promise');
+
+promise_test(() => {
+ const ws = recordingWritableStream({
+ write() {
+ return new Promise(() => {});
+ }
+ });
+
+ const writer = ws.getWriter();
+
+ return writer.ready.then(() => {
+ writer.write('a');
+
+ assert_equals(writer.desiredSize, 0, 'desiredSize should be 0');
+
+ let calledClose = false;
+ return Promise.all([
+ writer.ready.then(v => {
+ assert_equals(v, undefined, 'ready promise should be fulfilled with undefined');
+ assert_true(calledClose, 'ready should not be fulfilled before writer.close() is called');
+ assert_array_equals(ws.events, ['write', 'a'], 'sink abort() should not be called');
+ }),
+ flushAsyncEvents().then(() => {
+ writer.close();
+ calledClose = true;
+ })
+ ]);
+ });
+}, 'when close is called on a WritableStream in waiting state, ready promise should be fulfilled');
+
+promise_test(() => {
+ let asyncCloseFinished = false;
+ const ws = recordingWritableStream({
+ close() {
+ return flushAsyncEvents().then(() => {
+ asyncCloseFinished = true;
+ });
+ }
+ });
+
+ const writer = ws.getWriter();
+ return writer.ready.then(() => {
+ writer.write('a');
+
+ writer.close();
+
+ return writer.ready.then(v => {
+ assert_false(asyncCloseFinished, 'ready promise should be fulfilled before async close completes');
+ assert_equals(v, undefined, 'ready promise should be fulfilled with undefined');
+ assert_array_equals(ws.events, ['write', 'a', 'close'], 'sink abort() should not be called');
+ });
+ });
+}, 'when close is called on a WritableStream in waiting state, ready should be fulfilled immediately even if close ' +
+ 'takes a long time');
+
+promise_test(t => {
+ const rejection = { name: 'letter' };
+ const ws = new WritableStream({
+ close() {
+ return {
+ then(onFulfilled, onRejected) { onRejected(rejection); }
+ };
+ }
+ });
+ return promise_rejects_exactly(t, rejection, ws.getWriter().close(), 'close() should return a rejection');
+}, 'returning a thenable from close() should work');
+
+promise_test(t => {
+ const ws = new WritableStream();
+ const writer = ws.getWriter();
+ return writer.ready.then(() => {
+ const closePromise = writer.close();
+ const closedPromise = writer.closed;
+ writer.releaseLock();
+ return Promise.all([
+ closePromise,
+ promise_rejects_js(t, TypeError, closedPromise, '.closed promise should be rejected')
+ ]);
+ });
+}, 'releaseLock() should not change the result of sync close()');
+
+promise_test(t => {
+ const ws = new WritableStream({
+ close() {
+ return flushAsyncEvents();
+ }
+ });
+ const writer = ws.getWriter();
+ return writer.ready.then(() => {
+ const closePromise = writer.close();
+ const closedPromise = writer.closed;
+ writer.releaseLock();
+ return Promise.all([
+ closePromise,
+ promise_rejects_js(t, TypeError, closedPromise, '.closed promise should be rejected')
+ ]);
+ });
+}, 'releaseLock() should not change the result of async close()');
+
+promise_test(() => {
+ let resolveClose;
+ const ws = new WritableStream({
+ close() {
+ const promise = new Promise(resolve => {
+ resolveClose = resolve;
+ });
+ return promise;
+ }
+ });
+ const writer = ws.getWriter();
+ const closePromise = writer.close();
+ writer.releaseLock();
+ return delay(0).then(() => {
+ resolveClose();
+ return closePromise.then(() => {
+ assert_equals(ws.getWriter().desiredSize, 0, 'desiredSize should be 0');
+ });
+ });
+}, 'close() should set state to CLOSED even if writer has detached');
+
+promise_test(() => {
+ let resolveClose;
+ const ws = new WritableStream({
+ close() {
+ const promise = new Promise(resolve => {
+ resolveClose = resolve;
+ });
+ return promise;
+ }
+ });
+ const writer = ws.getWriter();
+ writer.close();
+ writer.releaseLock();
+ return delay(0).then(() => {
+ const abortingWriter = ws.getWriter();
+ const abortPromise = abortingWriter.abort();
+ abortingWriter.releaseLock();
+ resolveClose();
+ return abortPromise;
+ });
+}, 'the promise returned by async abort during close should resolve');
+
+// Though the order in which the promises are fulfilled or rejected is arbitrary, we're checking it for
+// interoperability. We can change the order as long as we file bugs on all implementers to update to the latest tests
+// to keep them interoperable.
+
+promise_test(() => {
+ const ws = new WritableStream({});
+
+ const writer = ws.getWriter();
+
+ const closePromise = writer.close();
+
+ const events = [];
+ return Promise.all([
+ closePromise.then(() => {
+ events.push('closePromise');
+ }),
+ writer.closed.then(() => {
+ events.push('closed');
+ })
+ ]).then(() => {
+ assert_array_equals(events, ['closePromise', 'closed'],
+ 'promises must fulfill/reject in the expected order');
+ });
+}, 'promises must fulfill/reject in the expected order on closure');
+
+promise_test(() => {
+ const ws = new WritableStream({});
+
+ // Wait until the WritableStream starts so that the close() call gets processed. Otherwise, abort() will be
+ // processed without waiting for completion of the close().
+ return delay(0).then(() => {
+ const writer = ws.getWriter();
+
+ const closePromise = writer.close();
+ const abortPromise = writer.abort(error1);
+
+ const events = [];
+ return Promise.all([
+ closePromise.then(() => {
+ events.push('closePromise');
+ }),
+ abortPromise.then(() => {
+ events.push('abortPromise');
+ }),
+ writer.closed.then(() => {
+ events.push('closed');
+ })
+ ]).then(() => {
+ assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'],
+ 'promises must fulfill/reject in the expected order');
+ });
+ });
+}, 'promises must fulfill/reject in the expected order on aborted closure');
+
+promise_test(t => {
+ const ws = new WritableStream({
+ close() {
+ return Promise.reject(error1);
+ }
+ });
+
+ // Wait until the WritableStream starts so that the close() call gets processed.
+ return delay(0).then(() => {
+ const writer = ws.getWriter();
+
+ const closePromise = writer.close();
+ const abortPromise = writer.abort(error2);
+
+ const events = [];
+ closePromise.catch(() => events.push('closePromise'));
+ abortPromise.catch(() => events.push('abortPromise'));
+ writer.closed.catch(() => events.push('closed'));
+ return Promise.all([
+ promise_rejects_exactly(t, error1, closePromise,
+ 'closePromise must reject with the error returned from the sink\'s close method'),
+ promise_rejects_exactly(t, error1, abortPromise,
+ 'abortPromise must reject with the error returned from the sink\'s close method'),
+ promise_rejects_exactly(t, error2, writer.closed,
+ 'writer.closed must reject with error2')
+ ]).then(() => {
+ assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'],
+ 'promises must fulfill/reject in the expected order');
+ });
+ });
+}, 'promises must fulfill/reject in the expected order on aborted and errored closure');
+
+promise_test(t => {
+ let resolveWrite;
+ let controller;
+ const ws = new WritableStream({
+ write(chunk, c) {
+ controller = c;
+ return new Promise(resolve => {
+ resolveWrite = resolve;
+ });
+ }
+ });
+ const writer = ws.getWriter();
+ return writer.ready.then(() => {
+ const writePromise = writer.write('c');
+ controller.error(error1);
+ const closePromise = writer.close();
+ let closeRejected = false;
+ closePromise.catch(() => {
+ closeRejected = true;
+ });
+ return flushAsyncEvents().then(() => {
+ assert_false(closeRejected);
+ resolveWrite();
+ return Promise.all([
+ writePromise,
+ promise_rejects_exactly(t, error1, closePromise, 'close() should reject')
+ ]).then(() => {
+ assert_true(closeRejected);
+ });
+ });
+ });
+}, 'close() should not reject until no sink methods are in flight');
+
+promise_test(() => {
+ const ws = new WritableStream();
+ const writer1 = ws.getWriter();
+ return writer1.close().then(() => {
+ writer1.releaseLock();
+ const writer2 = ws.getWriter();
+ const ready = writer2.ready;
+ assert_equals(ready.constructor, Promise);
+ return ready;
+ });
+}, 'ready promise should be initialised as fulfilled for a writer on a closed stream');
+
+promise_test(() => {
+ const ws = new WritableStream();
+ ws.close();
+ const writer = ws.getWriter();
+ return writer.closed;
+}, 'close() on a writable stream should work');
+
+promise_test(t => {
+ const ws = new WritableStream();
+ ws.getWriter();
+ return promise_rejects_js(t, TypeError, ws.close(), 'close should reject');
+}, 'close() on a locked stream should reject');
+
+promise_test(t => {
+ const ws = new WritableStream({
+ start(controller) {
+ controller.error(error1);
+ }
+ });
+ return promise_rejects_exactly(t, error1, ws.close(), 'close should reject with error1');
+}, 'close() on an erroring stream should reject');
+
+promise_test(t => {
+ const ws = new WritableStream({
+ start(controller) {
+ controller.error(error1);
+ }
+ });
+ const writer = ws.getWriter();
+ return promise_rejects_exactly(t, error1, writer.closed, 'closed should reject with the error').then(() => {
+ writer.releaseLock();
+ return promise_rejects_js(t, TypeError, ws.close(), 'close should reject');
+ });
+}, 'close() on an errored stream should reject');
+
+promise_test(t => {
+ const ws = new WritableStream();
+ const writer = ws.getWriter();
+ return writer.close().then(() => {
+ return promise_rejects_js(t, TypeError, ws.close(), 'close should reject');
+ });
+}, 'close() on an closed stream should reject');
+
+promise_test(t => {
+ const ws = new WritableStream({
+ close() {
+ return new Promise(() => {});
+ }
+ });
+
+ const writer = ws.getWriter();
+ writer.close();
+ writer.releaseLock();
+
+ return promise_rejects_js(t, TypeError, ws.close(), 'close should reject');
+}, 'close() on a stream with a pending close should reject');