260 lines
8.7 KiB
HTML
260 lines
8.7 KiB
HTML
<!DOCTYPE html>
|
|
<meta charset="utf-8">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="resources/helpers.js"></script>
|
|
<script src="../resources/recording-streams.js"></script>
|
|
<script src="../resources/test-utils.js"></script>
|
|
<script>
|
|
'use strict';
|
|
|
|
promise_test(async () => {
|
|
const rs = await createTransferredReadableStream({
|
|
start(controller) {
|
|
controller.enqueue('a');
|
|
controller.close();
|
|
}
|
|
});
|
|
const reader = rs.getReader();
|
|
{
|
|
const {value, done} = await reader.read();
|
|
assert_false(done, 'should not be done yet');
|
|
assert_equals(value, 'a', 'first chunk should be a');
|
|
}
|
|
{
|
|
const {done} = await reader.read();
|
|
assert_true(done, 'should be done now');
|
|
}
|
|
}, 'sending one chunk through a transferred stream should work');
|
|
|
|
promise_test(async () => {
|
|
let controller;
|
|
const rs = await createTransferredReadableStream({
|
|
start(c) {
|
|
controller = c;
|
|
}
|
|
});
|
|
for (let i = 0; i < 10; ++i) {
|
|
controller.enqueue(i);
|
|
}
|
|
controller.close();
|
|
const reader = rs.getReader();
|
|
for (let i = 0; i < 10; ++i) {
|
|
const {value, done} = await reader.read();
|
|
assert_false(done, 'should not be done yet');
|
|
assert_equals(value, i, 'chunk content should match index');
|
|
}
|
|
const {done} = await reader.read();
|
|
assert_true(done, 'should be done now');
|
|
}, 'sending ten chunks through a transferred stream should work');
|
|
|
|
promise_test(async () => {
|
|
let controller;
|
|
const rs = await createTransferredReadableStream({
|
|
start(c) {
|
|
controller = c;
|
|
}
|
|
});
|
|
const reader = rs.getReader();
|
|
for (let i = 0; i < 10; ++i) {
|
|
controller.enqueue(i);
|
|
const {value, done} = await reader.read();
|
|
assert_false(done, 'should not be done yet');
|
|
assert_equals(value, i, 'chunk content should match index');
|
|
}
|
|
controller.close();
|
|
const {done} = await reader.read();
|
|
assert_true(done, 'should be done now');
|
|
}, 'sending ten chunks one at a time should work');
|
|
|
|
promise_test(async () => {
|
|
let controller;
|
|
const rs = await createTransferredReadableStream({
|
|
start() {
|
|
this.counter = 0;
|
|
},
|
|
pull(controller) {
|
|
controller.enqueue(this.counter);
|
|
++this.counter;
|
|
if (this.counter === 10)
|
|
controller.close();
|
|
}
|
|
});
|
|
const reader = rs.getReader();
|
|
for (let i = 0; i < 10; ++i) {
|
|
const {value, done} = await reader.read();
|
|
assert_false(done, 'should not be done yet');
|
|
assert_equals(value, i, 'chunk content should match index');
|
|
}
|
|
const {done} = await reader.read();
|
|
assert_true(done, 'should be done now');
|
|
}, 'sending ten chunks on demand should work');
|
|
|
|
promise_test(async () => {
|
|
const rs = recordingReadableStream({}, { highWaterMark: 0 });
|
|
await delay(0);
|
|
assert_array_equals(rs.events, [], 'pull() should not have been called');
|
|
// Eat the message so it can't interfere with other tests.
|
|
addEventListener('message', () => {}, {once: true});
|
|
// The transfer is done manually to verify that it is posting the stream that
|
|
// relieves backpressure, not receiving it.
|
|
postMessage(rs, '*', [rs]);
|
|
await delay(0);
|
|
assert_array_equals(rs.events, ['pull'], 'pull() should have been called');
|
|
}, 'transferring a stream should relieve backpressure');
|
|
|
|
promise_test(async () => {
|
|
const rs = await recordingTransferredReadableStream({
|
|
pull(controller) {
|
|
controller.enqueue('a');
|
|
}
|
|
}, { highWaterMark: 2 });
|
|
await delay(0);
|
|
assert_array_equals(rs.events, ['pull', 'pull', 'pull'],
|
|
'pull() should have been called three times');
|
|
}, 'transferring a stream should add one chunk to the queue size');
|
|
|
|
promise_test(async () => {
|
|
const rs = await recordingTransferredReadableStream({
|
|
start(controller) {
|
|
controller.enqueue(new Uint8Array(1024));
|
|
controller.enqueue(new Uint8Array(1024));
|
|
}
|
|
}, new ByteLengthQueuingStrategy({highWaterMark: 512}));
|
|
await delay(0);
|
|
// At this point the queue contains 1024/512 bytes and 1/1 chunk, so it's full
|
|
// and pull() is not called.
|
|
assert_array_equals(rs.events, [], 'pull() should not have been called');
|
|
const reader = rs.getReader();
|
|
const {value, done} = await reader.read();
|
|
assert_false(done, 'we should not be done');
|
|
assert_equals(value.byteLength, 1024, 'expected chunk should be returned');
|
|
// Now the queue contains 0/512 bytes and 1/1 chunk, so pull() is called. If
|
|
// the implementation erroneously counted the extra queue space in bytes, then
|
|
// the queue would contain 1024/513 bytes and pull() wouldn't be called.
|
|
assert_array_equals(rs.events, ['pull'], 'pull() should have been called');
|
|
}, 'the extra queue from transferring is counted in chunks');
|
|
|
|
async function transferredReadableStreamWithCancelPromise() {
|
|
let resolveCancelCalled;
|
|
const cancelCalled = new Promise(resolve => {
|
|
resolveCancelCalled = resolve;
|
|
});
|
|
const rs = await recordingTransferredReadableStream({
|
|
cancel() {
|
|
resolveCancelCalled();
|
|
}
|
|
});
|
|
return { rs, cancelCalled };
|
|
}
|
|
|
|
promise_test(async () => {
|
|
const { rs, cancelCalled } = await transferredReadableStreamWithCancelPromise();
|
|
rs.cancel('message');
|
|
await cancelCalled;
|
|
assert_array_equals(rs.events, ['pull', 'cancel', 'message'],
|
|
'cancel() should have been called');
|
|
const reader = rs.getReader();
|
|
// Check the stream really got closed.
|
|
await reader.closed;
|
|
}, 'cancel should be propagated to the original');
|
|
|
|
promise_test(async () => {
|
|
const { rs, cancelCalled } = await transferredReadableStreamWithCancelPromise();
|
|
const reader = rs.getReader();
|
|
const readPromise = reader.read();
|
|
reader.cancel('done');
|
|
const { done } = await readPromise;
|
|
assert_true(done, 'should be done');
|
|
await cancelCalled;
|
|
assert_array_equals(rs.events, ['pull', 'cancel', 'done'],
|
|
'events should match');
|
|
}, 'cancel should abort a pending read()');
|
|
|
|
promise_test(async () => {
|
|
let cancelComplete = false;
|
|
const rs = await createTransferredReadableStream({
|
|
async cancel() {
|
|
await flushAsyncEvents();
|
|
cancelComplete = true;
|
|
}
|
|
});
|
|
await rs.cancel();
|
|
assert_false(cancelComplete,
|
|
'cancel() on the underlying sink should not have completed');
|
|
}, 'stream cancel should not wait for underlying source cancel');
|
|
|
|
promise_test(async t => {
|
|
const rs = await recordingTransferredReadableStream();
|
|
const reader = rs.getReader();
|
|
let serializationHappened = false;
|
|
rs.controller.enqueue({
|
|
get getter() {
|
|
serializationHappened = true;
|
|
return 'a';
|
|
}
|
|
});
|
|
await flushAsyncEvents();
|
|
assert_false(serializationHappened,
|
|
'serialization should not have happened yet');
|
|
const {value, done} = await reader.read();
|
|
assert_false(done, 'should not be done');
|
|
assert_equals(value.getter, 'a', 'getter should be a');
|
|
assert_true(serializationHappened,
|
|
'serialization should have happened');
|
|
}, 'serialization should not happen until the value is read');
|
|
|
|
promise_test(async t => {
|
|
const rs = await recordingTransferredReadableStream();
|
|
const reader = rs.getReader();
|
|
rs.controller.enqueue(new ReadableStream());
|
|
await promise_rejects_dom(t, 'DataCloneError', reader.read(),
|
|
'closed promise should reject');
|
|
assert_throws_js(TypeError, () => rs.controller.enqueue(),
|
|
'original stream should be errored');
|
|
}, 'transferring a non-serializable chunk should error both sides');
|
|
|
|
promise_test(async t => {
|
|
const rs = await createTransferredReadableStream({
|
|
start(controller) {
|
|
controller.error('foo');
|
|
}
|
|
});
|
|
const reader = rs.getReader();
|
|
return promise_rejects_exactly(t, 'foo', reader.read(),
|
|
'error should be passed through');
|
|
}, 'errors should be passed through');
|
|
|
|
promise_test(async () => {
|
|
const rs = await recordingTransferredReadableStream();
|
|
await delay(0);
|
|
const reader = rs.getReader();
|
|
reader.cancel();
|
|
rs.controller.error();
|
|
const {done} = await reader.read();
|
|
assert_true(done, 'should be done');
|
|
assert_throws_js(TypeError, () => rs.controller.enqueue(),
|
|
'enqueue should throw');
|
|
}, 'race between cancel() and error() should leave sides in different states');
|
|
|
|
promise_test(async () => {
|
|
const rs = await recordingTransferredReadableStream();
|
|
await delay(0);
|
|
const reader = rs.getReader();
|
|
reader.cancel();
|
|
rs.controller.close();
|
|
const {done} = await reader.read();
|
|
assert_true(done, 'should be done');
|
|
}, 'race between cancel() and close() should be benign');
|
|
|
|
promise_test(async () => {
|
|
const rs = await recordingTransferredReadableStream();
|
|
await delay(0);
|
|
const reader = rs.getReader();
|
|
reader.cancel();
|
|
rs.controller.enqueue('a');
|
|
const {done} = await reader.read();
|
|
assert_true(done, 'should be done');
|
|
}, 'race between cancel() and enqueue() should be benign');
|
|
|
|
</script>
|