Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streams: improve test coverage #49447

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions streams/readable-byte-streams/general.any.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,29 @@ promise_test(t => {
});
}, 'ReadableStream with byte source: Test that erroring a stream does not release a BYOB reader automatically');

promise_test(async t => {
const rs = new ReadableStream({
type: 'bytes',
start(c) {
c.enqueue(new Uint8Array([1, 2, 3]));
}
});

const reader1 = rs.getReader({mode: 'byob'});
reader1.releaseLock();

const reader2 = rs.getReader({mode: 'byob'});

// Should be a no-op
reader1.releaseLock();

const result = await reader2.read(new Uint8Array([0, 0, 0]));
assert_typed_array_equals(result.value, new Uint8Array([1, 2, 3]),
'read() should still work on reader2 even after reader1 is released');
assert_false(result.done, 'done');

}, 'ReadableStream with byte source: cannot use an already-released BYOB reader to unlock a stream again');

promise_test(async t => {
const stream = new ReadableStream({
type: 'bytes'
Expand Down Expand Up @@ -992,6 +1015,69 @@ promise_test(() => {
}, 'ReadableStream with byte source: respond(3) to read(view) with 2 element Uint16Array enqueues the 1 byte ' +
'remainder');

promise_test(() => {
let pullCount = 0;

let controller;
let byobRequest;
let viewInfo;

const stream = new ReadableStream({
start(c) {
controller = c;
},
pull() {
++pullCount;

byobRequest = controller.byobRequest;
const view = byobRequest.view;
viewInfo = extractViewInfo(view);

view[0] = 0x01;
view[1] = 0x02;
view[2] = 0x03;

controller.byobRequest.respond(3);
},
type: 'bytes'
});

const reader = stream.getReader({ mode: 'byob' });
const read1 = reader.read(new Uint16Array(2));
const read2 = reader.read(new Uint8Array(1));

return read1.then(result => {
assert_equals(pullCount, 1);

assert_false(result.done, 'done');

const view = result.value;
assert_equals(view.byteOffset, 0, 'byteOffset');
assert_equals(view.byteLength, 2, 'byteLength');

const dataView = new DataView(view.buffer, view.byteOffset, view.byteLength);
assert_equals(dataView.getUint16(0), 0x0102);

return read2;
}).then(result => {
assert_equals(pullCount, 1);
assert_not_equals(byobRequest, null, 'byobRequest must not be null');
assert_equals(viewInfo.constructor, Uint8Array, 'view.constructor should be Uint8Array');
assert_equals(viewInfo.bufferByteLength, 4, 'view.buffer.byteLength should be 4');
assert_equals(viewInfo.byteOffset, 0, 'view.byteOffset should be 0');
assert_equals(viewInfo.byteLength, 4, 'view.byteLength should be 4');

assert_false(result.done, 'done');

const view = result.value;
assert_equals(view.byteOffset, 0, 'byteOffset');
assert_equals(view.byteLength, 1, 'byteLength');

assert_equals(view[0], 0x03);
});
}, 'ReadableStream with byte source: respond(3) to read(view) with 2 element Uint16Array fulfills second read(view) ' +
'with the 1 byte remainder');

promise_test(t => {
const stream = new ReadableStream({
start(controller) {
Expand Down
24 changes: 24 additions & 0 deletions streams/readable-byte-streams/templated.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// META: global=window,worker,shadowrealm
// META: script=../resources/test-utils.js
// META: script=../resources/rs-test-templates.js
'use strict';

templatedRSEmpty('ReadableStream with byte source (empty)', () => {
return new ReadableStream({ type: 'bytes' });
});

templatedRSEmptyReader('ReadableStream with byte source (empty) default reader', () => {
const stream = new ReadableStream({ type: 'bytes' });
const reader = stream.getReader();
return { stream, reader, read: () => reader.read() };
});

templatedRSEmptyReader('ReadableStream with byte source (empty) BYOB reader', () => {
const stream = new ReadableStream({ type: 'bytes' });
const reader = stream.getReader({ mode: 'byob' });
return { stream, reader, read: () => reader.read(new Uint8Array([0])) };
});

templatedRSThrowAfterCloseOrError('ReadableStream with byte source', (extras) => {
return new ReadableStream({ type: 'bytes', ...extras });
});
151 changes: 151 additions & 0 deletions streams/readable-streams/from.any.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ const badIterables = [
['an object with a non-callable @@asyncIterator method', {
[Symbol.asyncIterator]: 42
}],
['an object with an @@iterator method returning a non-object', {
[Symbol.iterator]: () => 42
}],
['an object with an @@asyncIterator method returning a non-object', {
[Symbol.asyncIterator]: () => 42
}],
];

for (const [label, iterable] of badIterables) {
Expand Down Expand Up @@ -244,6 +250,64 @@ promise_test(async t => {

}, `ReadableStream.from: stream errors when next() rejects`);

promise_test(async t => {
const theError = new Error('a unique string');

const iterable = {
next() {
throw theError;
},
[Symbol.asyncIterator]: () => iterable
};

const rs = ReadableStream.from(iterable);
const reader = rs.getReader();

await Promise.all([
promise_rejects_exactly(t, theError, reader.read()),
promise_rejects_exactly(t, theError, reader.closed)
]);

}, 'ReadableStream.from: stream errors when next() throws synchronously');

promise_test(async t => {

const iterable = {
next() {
return 42; // not a promise or an iterator result
},
[Symbol.asyncIterator]: () => iterable
};

const rs = ReadableStream.from(iterable);
const reader = rs.getReader();

await Promise.all([
promise_rejects_js(t, TypeError, reader.read()),
promise_rejects_js(t, TypeError, reader.closed)
]);

}, 'ReadableStream.from: stream errors when next() returns a non-object');

promise_test(async t => {

const iterable = {
next() {
return Promise.resolve(42); // not an iterator result
},
[Symbol.asyncIterator]: () => iterable
};

const rs = ReadableStream.from(iterable);
const reader = rs.getReader();

await Promise.all([
promise_rejects_js(t, TypeError, reader.read()),
promise_rejects_js(t, TypeError, reader.closed)
]);

}, 'ReadableStream.from: stream errors when next() fulfills with a non-object');

promise_test(async t => {

const iterable = {
Expand Down Expand Up @@ -360,6 +424,93 @@ promise_test(async t => {

}, `ReadableStream.from: return() is not called when iterator completes normally`);

promise_test(async t => {

const theError = new Error('a unique string');

const iterable = {
next: t.unreached_func('next() should not be called'),
throw: t.unreached_func('throw() should not be called'),
// no return method
[Symbol.asyncIterator]: () => iterable
};

const rs = ReadableStream.from(iterable);
const reader = rs.getReader();

await Promise.all([
reader.cancel(theError),
reader.closed
]);

}, `ReadableStream.from: cancel() resolves when return() method is missing`);

promise_test(async t => {

const theError = new Error('a unique string');

const iterable = {
next: t.unreached_func('next() should not be called'),
throw: t.unreached_func('throw() should not be called'),
return: 42,
[Symbol.asyncIterator]: () => iterable
};

const rs = ReadableStream.from(iterable);
const reader = rs.getReader();

await promise_rejects_js(t, TypeError, reader.cancel(theError), 'cancel() should reject with a TypeError');

await reader.closed;

}, `ReadableStream.from: cancel() rejects when return() is not a method`);

promise_test(async t => {

const cancelReason = new Error('cancel reason');
const rejectError = new Error('reject error');

const iterable = {
next: t.unreached_func('next() should not be called'),
throw: t.unreached_func('throw() should not be called'),
async return() {
throw rejectError;
},
[Symbol.asyncIterator]: () => iterable
};

const rs = ReadableStream.from(iterable);
const reader = rs.getReader();

await promise_rejects_exactly(t, rejectError, reader.cancel(cancelReason), 'cancel() should reject with error from return()');

await reader.closed;

}, `ReadableStream.from: cancel() rejects when return() rejects`);

promise_test(async t => {

const cancelReason = new Error('cancel reason');
const rejectError = new Error('reject error');

const iterable = {
next: t.unreached_func('next() should not be called'),
throw: t.unreached_func('throw() should not be called'),
return() {
throw rejectError;
},
[Symbol.asyncIterator]: () => iterable
};

const rs = ReadableStream.from(iterable);
const reader = rs.getReader();

await promise_rejects_exactly(t, rejectError, reader.cancel(cancelReason), 'cancel() should reject with error from return()');

await reader.closed;

}, `ReadableStream.from: cancel() rejects when return() throws synchronously`);

promise_test(async t => {

const theError = new Error('a unique string');
Expand Down
8 changes: 7 additions & 1 deletion streams/readable-streams/templated.any.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ templatedRSEmpty('ReadableStream (empty)', () => {
});

templatedRSEmptyReader('ReadableStream (empty) reader', () => {
return streamAndDefaultReader(new ReadableStream());
const stream = new ReadableStream();
const reader = stream.getReader();
return { stream, reader, read: () => reader.read() };
});

templatedRSClosed('ReadableStream (closed via call in start)', () => {
Expand Down Expand Up @@ -138,6 +140,10 @@ templatedRSTwoChunksClosedReader('ReadableStream (two chunks enqueued, then clos
return result;
}, chunks);

templatedRSThrowAfterCloseOrError('ReadableStream', (extras) => {
return new ReadableStream({ ...extras });
});

function streamAndDefaultReader(stream) {
return { stream, reader: stream.getReader() };
}
Loading
Loading