| <!DOCTYPE html> | |
| <script> | |
| // Silence unhandled rejection messages. | |
| window.addEventListener("unhandledrejection", function(event) { | |
| event.stopImmediatePropagation(); | |
| event.preventDefault(); | |
| }); | |
| </script> | |
| <script src='../../resources/testharness.js'></script> | |
| <script src='../../resources/testharnessreport.js'></script> | |
| <script src='resources/streams-utils.js'></script> | |
| <script> | |
| // This is updated till https://github.com/whatwg/streams/commit/4ba861e6f60c248060811830e11271c84b439cc3 | |
| function templatedRSClosed(label, factory) { | |
| test(function() { | |
| }, 'Running templatedRSClosed with ' + label); | |
| var test1 = async_test(label + ': piping to a WritableStream in the writable state should close the writable stream'); | |
| test1.step(function() { | |
| var closeCalled = false; | |
| var rs = factory(); | |
| var startPromise = Promise.resolve(); | |
| var ws = new WritableStream({ | |
| start: function() { | |
| return startPromise; | |
| }, | |
| write: function() { | |
| assert_unreached('Unexpected write call'); | |
| }, | |
| close: function() { | |
| closeCalled = true; | |
| }, | |
| abort: function() { | |
| assert_unreached('Unexpected abort call'); | |
| } | |
| }); | |
| startPromise.then(test1.step_func(function() { | |
| assert_equals(ws.state, 'writable', 'writable stream should start in writable state'); | |
| return rs.pipeTo(ws).then(test1.step_func(function() { | |
| assert_true(closeCalled); | |
| assert_equals(ws.state, 'closed', 'writable stream should become closed'); | |
| test1.done('underlying source close should be called'); | |
| })); | |
| })).catch(test1.step_func(function(e) { assert_unreached(e); })); | |
| }); | |
| var test2 = async_test(label + ': piping to a WritableStream in the writable state with { preventClose: true } should do nothing'); | |
| test2.step(function() { | |
| var rs = factory(); | |
| var startPromise = Promise.resolve(); | |
| var ws = new WritableStream({ | |
| start: function() { | |
| return startPromise; | |
| }, | |
| write: function() { | |
| assert_unreached('Unexpected write call'); | |
| }, | |
| close: function() { | |
| assert_unreached('Unexpected close call'); | |
| }, | |
| abort: function() { | |
| assert_unreached('Unexpected abort call'); | |
| } | |
| }); | |
| startPromise.then(test2.step_func(function() { | |
| assert_equals(ws.state, 'writable', 'writable stream should start in writable state'); | |
| return rs.pipeTo(ws, { preventClose: true }).then(test2.step_func(function() { | |
| assert_equals(ws.state, 'writable', 'writable stream should still be writable'); | |
| test2.done('pipeTo promise should be fulfilled'); | |
| })); | |
| })).catch(test2.step_func(function(e) { assert_unreached(e); })); | |
| }); | |
| }; | |
| function templatedRSErrored(label, factory, error) { | |
| test(function() { | |
| }, 'Running templatedRSErrored with ' + label); | |
| var test1 = async_test(label + ': piping to a WritableStream in the writable state should abort the writable stream'); | |
| test1.step(function() { | |
| var rs = factory(); | |
| var startPromise = Promise.resolve(); | |
| var ws = new WritableStream({ | |
| start: function() { | |
| return startPromise; | |
| }, | |
| write: function() { | |
| assert_unreached('Unexpected write call'); | |
| }, | |
| close: function() { | |
| assert_reached('Unexpected close call'); | |
| }, | |
| abort: function(reason) { | |
| assert_equals(reason, error); | |
| } | |
| }); | |
| startPromise.then(test1.step_func(function() { | |
| assert_equals(ws.state, 'writable'); | |
| rs.pipeTo(ws).then( | |
| test1.step_func(function() { assert_unreached('pipeTo promise should not be fulfilled'); }), | |
| test1.step_func(function(e) { | |
| assert_equals(e, error, 'pipeTo promise should be rejected with the passed error'); | |
| assert_equals(ws.state, 'errored', 'writable stream should become errored'); | |
| test1.done(); | |
| }) | |
| ); | |
| })); | |
| }); | |
| }; | |
| function templatedRSErroredAsyncOnly(label, factory, error) { | |
| test(function() { | |
| }, 'Running templatedRSErroredAsyncOnly with ' + label); | |
| var test1 = async_test(label + ': piping with no options'); | |
| test1.step(function() { | |
| var closeCalled = false; | |
| var rs = factory(); | |
| var ws = new WritableStream({ | |
| abort: function(r) { | |
| assert_equals(r, error, 'reason passed to abort should equal the source error'); | |
| } | |
| }); | |
| rs.pipeTo(ws).catch(test1.step_func(function(e) { | |
| assert_equals(ws.state, 'errored', 'destination should be errored'); | |
| assert_equals(e, error, 'rejection reason of pipeToPromise should be the source error'); | |
| assert_true(closeCalled); | |
| test1.done(); | |
| })); | |
| ws.closed.catch(test1.step_func(function(e) { | |
| assert_equals(e, error, 'rejection reason of dest closed should be the source error'); | |
| closeCalled = true; | |
| })) | |
| }); | |
| var test2 = async_test(label + ': piping with { preventAbort: false }'); | |
| test2.step(function() { | |
| var abortCalled = false; | |
| var closeRejected = false; | |
| var rs = factory(); | |
| var ws = new WritableStream({ | |
| abort: function(r) { | |
| assert_equals(r, error, 'reason passed to abort should equal the source error'); | |
| abortCalled = true; | |
| } | |
| }); | |
| rs.pipeTo(ws, { preventAbort: false }).catch(test2.step_func(function(e) { | |
| assert_equals(ws.state, 'errored', 'destination should be errored'); | |
| assert_equals(e, error, 'rejection reason of pipeToPromise should be the source error'); | |
| assert_true(abortCalled); | |
| assert_true(closeRejected); | |
| test2.done(); | |
| })); | |
| ws.closed.catch(test2.step_func(function(e) { | |
| assert_equals(e, error, 'rejection reason of dest closed should be the source error'); | |
| closeRejected = true; | |
| })); | |
| }); | |
| var test3 = async_test(label + ': piping with { preventAbort: true }'); | |
| test3.step(function() { | |
| var rs = factory(); | |
| var ws = new WritableStream({ | |
| abort: function() { | |
| assert_unreached('underlying sink abort should not be called'); | |
| } | |
| }); | |
| rs.pipeTo(ws, { preventAbort: true }).catch(test3.step_func(function(e) { | |
| assert_equals(ws.state, 'writable', 'destination should remain writable'); | |
| assert_equals(e, error, 'rejection reason of pipeToPromise should be the source error'); | |
| test3.done(); | |
| })); | |
| }); | |
| }; | |
| function templatedRSTwoChunksClosed(label, factory, error) { | |
| test(function() { | |
| }, 'Running templatedRSTwoChunksClosed with ' + label); | |
| var test1 = async_test(label + ': piping with no options and no destination errors'); | |
| test1.step(function() { | |
| var rs = factory(); | |
| var chunksWritten = []; | |
| var ws = new WritableStream({ | |
| abort: function() { | |
| assert_unreached('unexpected abort call'); | |
| }, | |
| write: function(chunk) { | |
| chunksWritten.push(chunk); | |
| } | |
| }); | |
| rs.pipeTo(ws).then(test1.step_func(function() { | |
| assert_equals(ws.state, 'closed', 'destination should be closed'); | |
| assert_array_equals(chunksWritten, chunks); | |
| test1.done(); | |
| })); | |
| }); | |
| var test2 = async_test(label + ': piping with { preventClose: false } and no destination errors'); | |
| test2.step(function() { | |
| var rs = factory(); | |
| var chunksWritten = []; | |
| var ws = new WritableStream({ | |
| abort: function() { | |
| assert_unreached('unexpected abort call'); | |
| }, | |
| write: function(chunk) { | |
| chunksWritten.push(chunk); | |
| } | |
| }); | |
| rs.pipeTo(ws).then(test2.step_func(function() { | |
| assert_equals(ws.state, 'closed', 'destination should be closed'); | |
| assert_array_equals(chunksWritten, chunks); | |
| test2.done(); | |
| })); | |
| }); | |
| var test3 = async_test(label + ': piping with { preventClose: true } and no destination errors'); | |
| test3.step(function() { | |
| var rs = factory(); | |
| var chunksWritten = []; | |
| var ws = new WritableStream({ | |
| close: function() { | |
| assert_unreached('unexpected close call'); | |
| }, | |
| abort: function() { | |
| assert_unreached('unexpected abort call'); | |
| }, | |
| write: function(chunk) { | |
| chunksWritten.push(chunk); | |
| } | |
| }); | |
| rs.pipeTo(ws, { preventClose: true }).then(test3.step_func(function() { | |
| assert_equals(ws.state, 'writable', 'destination should be writable'); | |
| assert_array_equals(chunksWritten, chunks); | |
| test3.done(); | |
| })); | |
| }); | |
| var test4 = async_test(label + ': piping with { preventClose: false } and a destination with that errors synchronously'); | |
| test4.step(function() { | |
| var rs = factory(); | |
| var theError = new Error('!!!'); | |
| var ws = new WritableStream({ | |
| close: function() { | |
| assert_unreached('unexpected close call'); | |
| }, | |
| abort: function() { | |
| assert_unreached('unexpected abort call'); | |
| }, | |
| write: function() { | |
| throw theError; | |
| } | |
| }); | |
| rs.pipeTo(ws, { preventClose: false }).then( | |
| test4.step_func(function() { assert_unreached('pipeTo promise should not fulfill'); }), | |
| test4.step_func(function(e) { | |
| assert_equals(e, theError, 'pipeTo promise should reject with the write error'); | |
| test4.done(); | |
| }) | |
| ); | |
| }); | |
| var test5 = async_test(label + ': piping with { preventClose: true } and a destination with that errors synchronously'); | |
| test5.step(function() { | |
| var rs = factory(); | |
| var theError = new Error('!!!'); | |
| var ws = new WritableStream({ | |
| close: function() { | |
| assert_unreached('unexpected close call'); | |
| }, | |
| abort: function() { | |
| assert_unreached('unexpected abort call'); | |
| }, | |
| write: function() { | |
| throw theError; | |
| } | |
| }); | |
| rs.pipeTo(ws, { preventClose: true }).then( | |
| test5.step_func(function() { assert_unreached('pipeTo promise should not fulfill'); }), | |
| test5.step_func(function(e) { | |
| assert_equals(e, theError, 'pipeTo promise should reject with the write error'); | |
| test5.done(); | |
| }) | |
| ); | |
| }); | |
| var test6 = async_test(label + ': piping with { preventClose: true } and a destination that errors on the last chunk'); | |
| test6.step(function() { | |
| var rs = factory(); | |
| var theError = new Error('!!!'); | |
| var chunkCounter = 0; | |
| var ws = new WritableStream( | |
| { | |
| close: function() { | |
| assert_unreached('unexpected close call'); | |
| }, | |
| abort: function() { | |
| assert_unreached('unexpected abort call'); | |
| }, | |
| write: function() { | |
| if (++chunkCounter === 2) { | |
| return new Promise(test6.step_func(function(r, reject) { setTimeout(test6.step_func(function() { reject(theError); }), 200); })); | |
| } | |
| } | |
| }, | |
| { | |
| highWaterMark: Infinity, | |
| size: function() { return 1; } | |
| } | |
| ); | |
| rs.pipeTo(ws, { preventClose: true }).then( | |
| test6.step_func(function() { assert_unreached('pipeTo promise should not fulfill'); }), | |
| test6.step_func(function(e) { | |
| assert_equals(e, theError, 'pipeTo promise should reject with the write error'); | |
| test6.done(); | |
| }) | |
| ); | |
| }); | |
| }; | |
| templatedRSClosed('ReadableStream (closed via call in start)', function() { | |
| return new ReadableStream({ | |
| start: function(c) { | |
| c.close(); | |
| } | |
| }); | |
| }); | |
| templatedRSClosed('ReadableStream (closed via cancel)', function() { | |
| var stream = new ReadableStream(); | |
| stream.cancel(); | |
| return stream; | |
| }); | |
| var theError = new Error('boo!'); | |
| templatedRSErrored('ReadableStream (errored via call in start)', function() { | |
| return new ReadableStream({ | |
| start: function(c) { | |
| c.error(theError); | |
| } | |
| })}, | |
| theError | |
| ); | |
| templatedRSErrored('ReadableStream (errored via returning a rejected promise in start)', function() { | |
| return new ReadableStream({ | |
| start: function() { | |
| return Promise.reject(theError); | |
| } | |
| })}, | |
| theError | |
| ); | |
| templatedRSErroredAsyncOnly('ReadableStream (errored via returning a rejected promise in start) reader', function() { | |
| return new ReadableStream({ | |
| start: function() { return Promise.reject(theError); } | |
| })}, | |
| theError | |
| ); | |
| var chunks = ['a', 'b']; | |
| templatedRSTwoChunksClosed('ReadableStream (two chunks enqueued, then closed)', function() { | |
| return new ReadableStream({ | |
| start: function(c) { | |
| c.enqueue(chunks[0]); | |
| c.enqueue(chunks[1]); | |
| c.close(); | |
| } | |
| })}, | |
| chunks | |
| ); | |
| templatedRSTwoChunksClosed('ReadableStream (two chunks enqueued async, then closed)', function() { | |
| return new ReadableStream({ | |
| _cancelled: false, | |
| start: function(c) { | |
| setTimeout(() => { | |
| if (!this._cancelled) | |
| c.enqueue(chunks[0]); | |
| }, 100); | |
| setTimeout(() => { | |
| if (!this._cancelled) | |
| c.enqueue(chunks[1]); | |
| }, 200); | |
| setTimeout(() => { | |
| if (!this._cancelled) | |
| c.close(); | |
| }, 300); | |
| }, | |
| cancel: function(reason) { | |
| this._cancelled = true; | |
| } | |
| })}, | |
| chunks | |
| ); | |
| templatedRSTwoChunksClosed('ReadableStream (two chunks enqueued via pull, then closed)', function() { | |
| var pullCall = 0; | |
| return new ReadableStream({ | |
| pull:function(c) { | |
| if (pullCall >= chunks.length) { | |
| c.close(); | |
| } else { | |
| c.enqueue(chunks[pullCall++]); | |
| } | |
| } | |
| }); | |
| }, | |
| chunks | |
| ); | |
| </script> |