summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/streams/readable-streams/default-reader.any.js
diff options
context:
space:
mode:
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.js539
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..f92862719e
--- /dev/null
+++ b/testing/web-platform/tests/streams/readable-streams/default-reader.any.js
@@ -0,0 +1,539 @@
+// META: global=window,worker,shadowrealm
+// 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');