<!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>