diff options
Diffstat (limited to 'testing/web-platform/tests/streams/readable-streams/default-reader.any.js')
-rw-r--r-- | testing/web-platform/tests/streams/readable-streams/default-reader.any.js | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/testing/web-platform/tests/streams/readable-streams/default-reader.any.js b/testing/web-platform/tests/streams/readable-streams/default-reader.any.js new file mode 100644 index 0000000000..59d7ab2f74 --- /dev/null +++ b/testing/web-platform/tests/streams/readable-streams/default-reader.any.js @@ -0,0 +1,539 @@ +// META: global=window,worker +// META: script=../resources/rs-utils.js +'use strict'; + +test(() => { + + assert_throws_js(TypeError, () => new ReadableStreamDefaultReader('potato')); + assert_throws_js(TypeError, () => new ReadableStreamDefaultReader({})); + assert_throws_js(TypeError, () => new ReadableStreamDefaultReader()); + +}, 'ReadableStreamDefaultReader constructor should get a ReadableStream object as argument'); + +test(() => { + + const rsReader = new ReadableStreamDefaultReader(new ReadableStream()); + assert_equals(rsReader.closed, rsReader.closed, 'closed should return the same promise'); + +}, 'ReadableStreamDefaultReader closed should always return the same promise object'); + +test(() => { + + const rs = new ReadableStream(); + new ReadableStreamDefaultReader(rs); // Constructing directly the first time should be fine. + assert_throws_js(TypeError, () => new ReadableStreamDefaultReader(rs), + 'constructing directly the second time should fail'); + +}, 'Constructing a ReadableStreamDefaultReader directly should fail if the stream is already locked (via direct ' + + 'construction)'); + +test(() => { + + const rs = new ReadableStream(); + new ReadableStreamDefaultReader(rs); // Constructing directly should be fine. + assert_throws_js(TypeError, () => rs.getReader(), 'getReader() should fail'); + +}, 'Getting a ReadableStreamDefaultReader via getReader should fail if the stream is already locked (via direct ' + + 'construction)'); + +test(() => { + + const rs = new ReadableStream(); + rs.getReader(); // getReader() should be fine. + assert_throws_js(TypeError, () => new ReadableStreamDefaultReader(rs), 'constructing directly should fail'); + +}, 'Constructing a ReadableStreamDefaultReader directly should fail if the stream is already locked (via getReader)'); + +test(() => { + + const rs = new ReadableStream(); + rs.getReader(); // getReader() should be fine. + assert_throws_js(TypeError, () => rs.getReader(), 'getReader() should fail'); + +}, 'Getting a ReadableStreamDefaultReader via getReader should fail if the stream is already locked (via getReader)'); + +test(() => { + + const rs = new ReadableStream({ + start(c) { + c.close(); + } + }); + + new ReadableStreamDefaultReader(rs); // Constructing directly should not throw. + +}, 'Constructing a ReadableStreamDefaultReader directly should be OK if the stream is closed'); + +test(() => { + + const theError = new Error('don\'t say i didn\'t warn ya'); + const rs = new ReadableStream({ + start(c) { + c.error(theError); + } + }); + + new ReadableStreamDefaultReader(rs); // Constructing directly should not throw. + +}, 'Constructing a ReadableStreamDefaultReader directly should be OK if the stream is errored'); + +promise_test(() => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + const reader = rs.getReader(); + + const promise = reader.read().then(result => { + assert_object_equals(result, { value: 'a', done: false }, 'read() should fulfill with the enqueued chunk'); + }); + + controller.enqueue('a'); + return promise; + +}, 'Reading from a reader for an empty stream will wait until a chunk is available'); + +promise_test(() => { + + let cancelCalled = false; + const passedReason = new Error('it wasn\'t the right time, sorry'); + const rs = new ReadableStream({ + cancel(reason) { + assert_true(rs.locked, 'the stream should still be locked'); + assert_throws_js(TypeError, () => rs.getReader(), 'should not be able to get another reader'); + assert_equals(reason, passedReason, 'the cancellation reason is passed through to the underlying source'); + cancelCalled = true; + } + }); + + const reader = rs.getReader(); + return reader.cancel(passedReason).then(() => assert_true(cancelCalled)); + +}, 'cancel() on a reader does not release the reader'); + +promise_test(() => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const reader = rs.getReader(); + const promise = reader.closed; + + controller.close(); + return promise; + +}, 'closed should be fulfilled after stream is closed (.closed access before acquiring)'); + +promise_test(t => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const reader1 = rs.getReader(); + + reader1.releaseLock(); + + const reader2 = rs.getReader(); + controller.close(); + + return Promise.all([ + promise_rejects_js(t, TypeError, reader1.closed), + reader2.closed + ]); + +}, 'closed should be rejected after reader releases its lock (multiple stream locks)'); + +promise_test(t => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const reader = rs.getReader(); + const promise1 = reader.closed; + + controller.close(); + + reader.releaseLock(); + const promise2 = reader.closed; + + assert_not_equals(promise1, promise2, '.closed should be replaced'); + return Promise.all([ + promise1, + promise_rejects_js(t, TypeError, promise2, '.closed after releasing lock'), + ]); + +}, 'closed is replaced when stream closes and reader releases its lock'); + +promise_test(t => { + + const theError = { name: 'unique error' }; + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const reader = rs.getReader(); + const promise1 = reader.closed; + + controller.error(theError); + + reader.releaseLock(); + const promise2 = reader.closed; + + assert_not_equals(promise1, promise2, '.closed should be replaced'); + return Promise.all([ + promise_rejects_exactly(t, theError, promise1, '.closed before releasing lock'), + promise_rejects_js(t, TypeError, promise2, '.closed after releasing lock') + ]); + +}, 'closed is replaced when stream errors and reader releases its lock'); + +promise_test(() => { + + const rs = new ReadableStream({ + start(c) { + c.enqueue('a'); + c.enqueue('b'); + c.close(); + } + }); + + const reader1 = rs.getReader(); + const promise1 = reader1.read().then(r => { + assert_object_equals(r, { value: 'a', done: false }, 'reading the first chunk from reader1 works'); + }); + reader1.releaseLock(); + + const reader2 = rs.getReader(); + const promise2 = reader2.read().then(r => { + assert_object_equals(r, { value: 'b', done: false }, 'reading the second chunk from reader2 works'); + }); + reader2.releaseLock(); + + return Promise.all([promise1, promise2]); + +}, 'Multiple readers can access the stream in sequence'); + +promise_test(() => { + const rs = new ReadableStream({ + start(c) { + c.enqueue('a'); + } + }); + + const reader1 = rs.getReader(); + reader1.releaseLock(); + + const reader2 = rs.getReader(); + + // Should be a no-op + reader1.releaseLock(); + + return reader2.read().then(result => { + assert_object_equals(result, { value: 'a', done: false }, + 'read() should still work on reader2 even after reader1 is released'); + }); + +}, 'Cannot use an already-released reader to unlock a stream again'); + +promise_test(t => { + + const rs = new ReadableStream({ + start(c) { + c.enqueue('a'); + }, + cancel() { + assert_unreached('underlying source cancel should not be called'); + } + }); + + const reader = rs.getReader(); + reader.releaseLock(); + const cancelPromise = reader.cancel(); + + const reader2 = rs.getReader(); + const readPromise = reader2.read().then(r => { + assert_object_equals(r, { value: 'a', done: false }, 'a new reader should be able to read a chunk'); + }); + + return Promise.all([ + promise_rejects_js(t, TypeError, cancelPromise), + readPromise + ]); + +}, 'cancel() on a released reader is a no-op and does not pass through'); + +promise_test(t => { + + const promiseAsserts = []; + + let controller; + const theError = { name: 'unique error' }; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const reader1 = rs.getReader(); + + promiseAsserts.push( + promise_rejects_exactly(t, theError, reader1.closed), + promise_rejects_exactly(t, theError, reader1.read()) + ); + + assert_throws_js(TypeError, () => rs.getReader(), 'trying to get another reader before erroring should throw'); + + controller.error(theError); + + reader1.releaseLock(); + + const reader2 = rs.getReader(); + + promiseAsserts.push( + promise_rejects_exactly(t, theError, reader2.closed), + promise_rejects_exactly(t, theError, reader2.read()) + ); + + return Promise.all(promiseAsserts); + +}, 'Getting a second reader after erroring the stream and releasing the reader should succeed'); + +promise_test(t => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const promise = rs.getReader().closed.then( + t.unreached_func('closed promise should not be fulfilled when stream is errored'), + err => { + assert_equals(err, undefined, 'passed error should be undefined as it was'); + } + ); + + controller.error(); + return promise; + +}, 'ReadableStreamDefaultReader closed promise should be rejected with undefined if that is the error'); + + +promise_test(t => { + + const rs = new ReadableStream({ + start() { + return Promise.reject(); + } + }); + + return rs.getReader().read().then( + t.unreached_func('read promise should not be fulfilled when stream is errored'), + err => { + assert_equals(err, undefined, 'passed error should be undefined as it was'); + } + ); + +}, 'ReadableStreamDefaultReader: if start rejects with no parameter, it should error the stream with an undefined ' + + 'error'); + +promise_test(t => { + + const theError = { name: 'unique string' }; + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const promise = promise_rejects_exactly(t, theError, rs.getReader().closed); + + controller.error(theError); + return promise; + +}, 'Erroring a ReadableStream after checking closed should reject ReadableStreamDefaultReader closed promise'); + +promise_test(t => { + + const theError = { name: 'unique string' }; + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + controller.error(theError); + + // Let's call getReader twice for extra test coverage of this code path. + rs.getReader().releaseLock(); + + return promise_rejects_exactly(t, theError, rs.getReader().closed); + +}, 'Erroring a ReadableStream before checking closed should reject ReadableStreamDefaultReader closed promise'); + +promise_test(() => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + const reader = rs.getReader(); + + const promise = Promise.all([ + reader.read().then(result => { + assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (1)'); + }), + reader.read().then(result => { + assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (2)'); + }), + reader.closed + ]); + + controller.close(); + return promise; + +}, 'Reading twice on a stream that gets closed'); + +promise_test(() => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + controller.close(); + const reader = rs.getReader(); + + return Promise.all([ + reader.read().then(result => { + assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (1)'); + }), + reader.read().then(result => { + assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (2)'); + }), + reader.closed + ]); + +}, 'Reading twice on a closed stream'); + +promise_test(t => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const myError = { name: 'mashed potatoes' }; + controller.error(myError); + + const reader = rs.getReader(); + + return Promise.all([ + promise_rejects_exactly(t, myError, reader.read()), + promise_rejects_exactly(t, myError, reader.read()), + promise_rejects_exactly(t, myError, reader.closed) + ]); + +}, 'Reading twice on an errored stream'); + +promise_test(t => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const myError = { name: 'mashed potatoes' }; + const reader = rs.getReader(); + + const promise = Promise.all([ + promise_rejects_exactly(t, myError, reader.read()), + promise_rejects_exactly(t, myError, reader.read()), + promise_rejects_exactly(t, myError, reader.closed) + ]); + + controller.error(myError); + return promise; + +}, 'Reading twice on a stream that gets errored'); + +test(() => { + const rs = new ReadableStream(); + let toStringCalled = false; + const mode = { + toString() { + toStringCalled = true; + return ''; + } + }; + assert_throws_js(TypeError, () => rs.getReader({ mode }), 'getReader() should throw'); + assert_true(toStringCalled, 'toString() should be called'); +}, 'getReader() should call ToString() on mode'); + +promise_test(() => { + const rs = new ReadableStream({ + pull(controller) { + controller.close(); + } + }); + + const reader = rs.getReader(); + return reader.read().then(() => { + // The test passes if releaseLock() does not throw. + reader.releaseLock(); + }); +}, 'controller.close() should clear the list of pending read requests'); + +promise_test(t => { + + let controller; + const rs = new ReadableStream({ + start(c) { + controller = c; + } + }); + + const reader1 = rs.getReader(); + const promise1 = promise_rejects_js(t, TypeError, reader1.read(), 'read() from reader1 should reject when reader1 is released'); + reader1.releaseLock(); + + controller.enqueue('a'); + + const reader2 = rs.getReader(); + const promise2 = reader2.read().then(r => { + assert_object_equals(r, { value: 'a', done: false }, 'read() from reader2 should resolve with enqueued chunk'); + }) + reader2.releaseLock(); + + return Promise.all([promise1, promise2]); + +}, 'Second reader can read chunks after first reader was released with pending read requests'); |