blob: dd3b78b7563b0dce813c1422391168102e8c6dc0 [file] [log] [blame]
<!DOCTYPE html>
<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
var test1 = async_test('Piping from a ReadableStream from which lots of data are readable synchronously');
test1.step(function() {
var events = 0;
var rs = new ReadableStream({
start: function(c) {
for (var i = 0; i < 1000; ++i) {
c.enqueue(i);
}
c.close();
}
});
var ws = new WritableStream({}, new CountQueuingStrategy({ highWaterMark: 1000 }));
assert_equals(ws.state, 'writable', 'writable stream state should start out writable');
var pipeFinished = false;
rs.pipeTo(ws).then(
test1.step_func(function() {
pipeFinished = true;
rs.getReader().closed.then(test1.step_func(function() {
assert_equals(++events, 2);
}));
assert_equals(ws.state, 'closed', 'writable stream state should be closed after pipe finishes');
assert_equals(++events, 1);
})
).catch(test1.step_func(function(e) { assert_unreached(e); }));
setTimeout(test1.step_func(function() {
assert_true(pipeFinished, 'pipe should have finished before a setTimeout(,0) since it should only be microtasks');
assert_equals(++events, 3);
test1.done();
}), 0);
});
var test2 = async_test('Piping from a ReadableStream in readable state to a WritableStream in closing state');
test2.step(function() {
var events = 0;
var cancelReason;
var rs = new ReadableStream({
start: function(c) {
c.enqueue('Hello');
},
cancel: function(reason) {
assert_throws(new TypeError(), function() { throw reason; }, 'underlying source cancel should have been called with a TypeError');
assert_equals(++events, 1);
cancelReason = reason;
}
});
var ws = new WritableStream({
write: function() {
assert_unreached('Unexpected write call');
},
abort: function() {
assert_unreached('Unexpected abort call');
}
});
ws.close();
assert_equals(ws.state, 'closing', 'writable stream should be closing immediately after closing it');
rs.pipeTo(ws).then(
test2.step_func(function() { assert_unreached('promise returned by pipeTo should not fulfill'); }),
test2.step_func(function(r) {
assert_equals(r, cancelReason, 'the pipeTo promise should reject with the same error as the underlying source cancel was called with');
assert_equals(++events, 2);
rs.getReader().closed.then(test2.step_func(function() {
assert_equals(++events, 3);
test2.done();
}));
})
).catch(test2.step_func(function(e) { assert_unreached(e); }));
});
var test3 = async_test('Piping from a ReadableStream in readable state to a WritableStream in errored state');
test3.step(function() {
var pullCount = 0;
var cancelCalled = false;
var passedError = new Error('horrible things');
var rs = new ReadableStream({
start: function(c) {
c.enqueue('Hello');
},
pull: function() {
++pullCount;
},
cancel: function(reason) {
assert_false(cancelCalled, 'cancel must not be called more than once');
cancelCalled = true;
assert_equals(reason, passedError);
}
});
var writeCalled = false;
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function() {
return startPromise;
},
write: function(chunk) {
assert_false(writeCalled, 'write must not be called more than once');
writeCalled = true;
assert_equals(chunk, 'Hello');
return Promise.reject(passedError);
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function() {
assert_unreached('Unexpected abort call');
}
});
startPromise.then(test3.step_func(function() {
ws.write('Hello');
assert_true(writeCalled, 'write must be called');
ws.ready.then(test3.step_func(function() {
assert_equals(ws.state, 'errored', 'as a result of rejected promise, ws must be in errored state');
rs.pipeTo(ws).catch(test3.step_func(function(e) {
assert_equals(e, passedError, 'pipeTo promise should be rejected with the error');
assert_true(cancelCalled, 'cancel should have been called');
test3.done();
}));
}));
}));
});
var test4 = async_test('Piping from a ReadableStream in the readable state which becomes closed after pipeTo call to a WritableStream in the writable state');
test4.step(function() {
var closeReadableStream;
var pullCount = 0;
var events = 0;
var rs = new ReadableStream({
start: function(c) {
c.enqueue('Hello');
closeReadableStream = c.close.bind(c);
},
pull: function() {
++pullCount;
},
cancel: function() {
assert_unreached('Unexpected cancel call');
}
});
var writeCalled = false;
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function() {
return startPromise;
},
write: function(chunk) {
if (!writeCalled) {
assert_equals(chunk, 'Hello', 'chunk written to writable stream should be the one enqueued into the readable stream');
writeCalled = true;
assert_equals(++events, 2);
} else {
assert_unreached('Unexpected extra write call');
}
},
close: function() {
assert_equals(pullCount, 1, 'underlying source pull should have been called once');
assert_equals(++events, 3);
},
abort: function() {
assert_unreached('Unexpected abort call');
}
});
startPromise.then(test4.step_func(function() {
rs.pipeTo(ws).then(test4.step_func(function() {
assert_equals(ws.state, 'closed', 'writable stream should be closed after pipeTo completes');
assert_equals(++events, 4);
test4.done();
}));
assert_equals(ws.state, 'writable', 'writable stream should still be writable immediately after pipeTo');
assert_equals(++events, 1);
closeReadableStream();
}));
});
var test5 = async_test('Piping from a ReadableStream in the readable state which becomes errored after pipeTo call to a WritableStream in the writable state');
test5.step(function() {
var errorReadableStream;
var pullCount = 0;
var events = 0;
var rs = new ReadableStream({
start: function(c) {
c.enqueue('Hello');
errorReadableStream = c.error.bind(c);
},
pull: function() {
++pullCount;
},
cancel: function() {
assert_unreached('Unexpected cancel call');
}
});
var passedError = new Error('horrible things');
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function() {
return startPromise;
},
write: function(chunk) {
assert_unreached('Unexpected extra write call');
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function(reason) {
assert_equals(reason, passedError, 'underlying sink abort should receive the error from the readable stream');
assert_equals(pullCount, 1, 'underlying source pull should have been called once');
assert_equals(++events, 2);
}
});
startPromise.then(test5.step_func(function() {
rs.pipeTo(ws).catch(test5.step_func(function(e) {
assert_equals(e, passedError, 'pipeTo should be rejected with the passed error');
assert_equals(ws.state, 'errored', 'writable stream should be errored after pipeTo completes');
assert_equals(++events, 3);
test5.done();
}));
assert_equals(ws.state, 'writable', 'writable stream should still be writable immediately after pipeTo');
assert_equals(++events, 1);
errorReadableStream(passedError);
}));
});
var test6 = async_test('Piping from an empty ReadableStream which becomes non-empty after pipeTo call to a WritableStream in the writable state');
test6.step(function() {
var controller;
var pullCount = 0;
var rs = new ReadableStream({
start: function(c) {
controller = c;
},
pull: function() {
++pullCount;
},
cancel: function() {
assert_unreached('Unexpected cancel call');
}
});
var ws = new WritableStream({
write: function(chunk) {
assert_equals(chunk, 'Hello', 'underlying sink write should be called with the single chunk');
assert_equals(pullCount, 1, 'pull should have been called once');
test6.done();
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function(reason) {
assert_unreached('Unexpected abort call');
}
});
rs.pipeTo(ws).then(test6.step_func(function() { assert_unreached('pipeTo promise should not fulfill'); }));
assert_equals(ws.state, 'writable', 'writable stream should start in writable state');
controller.enqueue('Hello');
});
var test7 = async_test('Piping from an empty ReadableStream which becomes errored after pipeTo call to a WritableStream in the writable state');
test7.step(function() {
var errorReadableStream;
var abortCalled = false;
var rs = new ReadableStream({
start: function(c) {
errorReadableStream = c.error.bind(c);
},
pull: function() {
assert_unreached('Unexpected pull call');
},
cancel: function() {
assert_unreached('Unexpected cancel call');
}
});
var passedError = new Error('horrible things');
var ws = new WritableStream({
write: function() {
assert_unreached('Unexpected write call');
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function(reason) {
assert_equals(reason, passedError, 'underlying sink abort should receive the error from the readable stream');
abortCalled = true;
}
});
rs.pipeTo(ws).catch(test7.step_func(function(e) {
assert_equals(e, passedError, 'pipeTo should reject with the passed error');
assert_true(abortCalled);
test7.done();
}));
assert_equals(ws.state, 'writable', 'writable stream should start out writable');
errorReadableStream(passedError);
});
var test8 = async_test('Piping from an empty ReadableStream to a WritableStream in the writable state which becomes errored after a pipeTo call');
test8.step(function() {
var cancelCalled = false;
var theError = new Error('cancel with me!');
var pullCount = 0;
var rs = new ReadableStream({
pull: function() {
++pullCount;
},
cancel: function(reason) {
assert_equals(reason, theError, 'underlying source cancellation reason should be the writable stream error');
assert_equals(pullCount, 2, 'pull should have been called twice by cancel-time');
cancelCalled = true;
}
});
var errorWritableStream;
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function(error) {
errorWritableStream = error;
return startPromise;
},
write: function(chunk) {
assert_unreached('Unexpected write call');
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function() {
assert_unreached('Unexpected abort call');
}
});
startPromise.then(test8.step_func(function() {
assert_equals(ws.state, 'writable', 'ws should start writable');
rs.pipeTo(ws).catch(test8.step_func(function(e) {
assert_equals(e, theError, 'pipeTo should reject with the passed error');
assert_true(cancelCalled);
test8.done();
}));
assert_equals(ws.state, 'writable', 'ws should be writable after pipe');
errorWritableStream(theError);
assert_equals(ws.state, 'errored', 'ws should be errored after erroring it');
}));
});
var test9 = async_test('Piping from a non-empty ReadableStream to a WritableStream in the waiting state which becomes writable after a pipeTo call');
test9.step(function() {
var pullCount = 0;
var readyFulfilled = false;
var rs = new ReadableStream({
start: function(c) {
c.enqueue('World');
},
pull: function() {
++pullCount;
},
cancel: function() {
assert_unreached('Unexpected cancel call');
}
});
var resolveWritePromise;
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function() {
return startPromise;
},
write: function(chunk) {
if (!resolveWritePromise) {
assert_equals(chunk, 'Hello', 'the first chunk to arrive in write should be the first chunk written');
return new Promise(test9.step_func(function(resolve) { resolveWritePromise = resolve; }));
} else {
assert_equals(chunk, 'World', 'the second chunk to arrive in write should be from the readable stream');
assert_equals(pullCount, 1, 'the readable stream\'s pull should have been called once');
assert_true(readyFulfilled);
test9.done();
}
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function() {
assert_unreached('Unexpected abort call');
}
});
ws.write('Hello');
startPromise.then(test9.step_func(function() {
assert_equals(ws.state, 'waiting', 'writable stream state should start waiting');
rs.pipeTo(ws);
assert_equals(ws.state, 'waiting', 'writable stream state should still be waiting immediately after piping');
resolveWritePromise();
ws.ready.then(test9.step_func(function() {
assert_equals(ws.state, 'writable', 'writable stream should eventually become writable (when ready fulfills)');
readyFulfilled = true;
})).catch(test9.step_func(function(e) { assert_unreached(e); }));
}));
});
var test10 = async_test('Piping from a non-empty ReadableStream to a WritableStream in waiting state which becomes errored after a pipeTo call');
test10.step(function() {
var writeCalled = false;
var pullCount = 0;
var rs = new ReadableStream({
start: function(c) {
c.enqueue('World');
},
pull: function() {
++pullCount;
},
cancel: function() {
assert_true(writeCalled);
assert_equals(pullCount, 1);
test10.done();
}
});
var errorWritableStream;
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function(error) {
errorWritableStream = error;
return startPromise;
},
write: function(chunk) {
assert_false(writeCalled);
assert_equals(chunk, 'Hello');
writeCalled = true;
return new Promise(test10.step_func(function() { }));
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function() {
assert_unreached('Unexpected abort call');
}
});
ws.write('Hello');
startPromise.then(test10.step_func(function() {
assert_equals(ws.state, 'waiting');
rs.pipeTo(ws);
assert_equals(ws.state, 'waiting');
errorWritableStream();
assert_equals(ws.state, 'errored');
}));
});
var test11 = async_test('Piping from a non-empty ReadableStream which becomes errored after pipeTo call to a WritableStream in the waiting state');
test11.step(function() {
var errorReadableStream;
var pullCount = 0;
var rs = new ReadableStream({
start: function(c) {
c.enqueue('World');
errorReadableStream = c.error.bind(c);
},
pull: function() {
++pullCount;
},
cancel: function() {
assert_unreached('Unexpected cancel call');
}
});
var writeCalled = false;
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function() {
return startPromise;
},
write: function(chunk) {
assert_false(writeCalled);
writeCalled = true;
assert_equals(chunk, 'Hello');
return new Promise(test11.step_func(function() { }));
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function() {
test11.done('underlying source abort was called');
}
});
ws.write('Hello');
startPromise.then(test11.step_func(function() {
assert_equals(ws.state, 'waiting');
assert_equals(pullCount, 0);
rs.pipeTo(ws);
assert_equals(ws.state, 'waiting');
errorReadableStream();
}));
});
var test12 = async_test('Piping from a non-empty ReadableStream to a WritableStream in the waiting state where both become ready after a pipeTo');
test12.step(function() {
var controller;
var pullCount = 0;
var rs = new ReadableStream({
start: function(c) {
controller = c;
},
pull: function() {
++pullCount;
},
cancel: function() {
assert_unreached('Unexpected cancel call');
}
});
var writeCount = 0;
var resolveWritePromise;
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function() {
return startPromise;
},
write: function(chunk) {
++writeCount;
if (writeCount === 1) {
assert_equals(chunk, 'Hello', 'first chunk written should equal the one passed to ws.write');
return new Promise(test12.step_func(function(resolve) { resolveWritePromise = resolve; }));
}
if (writeCount === 2) {
assert_equals(chunk, 'Goodbye', 'second chunk written should be from the source readable stream');
test12.done();
}
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function(reason) {
assert_unreached('Unexpected abort call');
}
});
ws.write('Hello');
startPromise.then(test12.step_func(function() {
assert_equals(writeCount, 1, 'exactly one write should have happened');
assert_equals(ws.state, 'waiting', 'writable stream should be waiting');
assert_equals(pullCount, 1, 'pull should have been called only once');
rs.pipeTo(ws);
controller.enqueue('Goodbye');
// Check that nothing happens before calling resolveWritePromise(), and then call resolveWritePromise()
// to check that pipeTo is woken up.
assert_equals(pullCount, 1, 'after the pipeTo and enqueue, pull still should have been called only once');
resolveWritePromise();
}));
});
var test13 = async_test('Piping from an empty ReadableStream to a WritableStream in the waiting state which becomes writable after a pipeTo call');
test13.step(function() {
var pullCount = 0;
var rs = new ReadableStream({
pull: function() {
++pullCount;
},
cancel: function() {
assert_unreached('Unexpected cancel call');
}
});
var resolveWritePromise;
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function() {
return startPromise;
},
write: function(chunk) {
assert_true(!resolveWritePromise);
assert_equals(chunk, 'Hello');
return new Promise(test13.step_func(function(resolve) { resolveWritePromise = resolve; }));
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function() {
assert_unreached('Unexpected abort call');
}
});
ws.write('Hello');
startPromise.then(test13.step_func(function() {
assert_equals(ws.state, 'waiting');
rs.pipeTo(ws);
assert_equals(ws.state, 'waiting');
assert_equals(pullCount, 1);
resolveWritePromise();
setTimeout(test13.step_func(function() {
assert_equals(pullCount, 2);
test13.done();
}), 500);
}));
});
var test14 = async_test('Piping from an empty ReadableStream which becomes closed after a pipeTo call to a WritableStream in the waiting state whose writes never complete');
test14.step(function() {
var closeReadableStream;
var pullCount = 0;
var rs = new ReadableStream({
start: function(c) {
closeReadableStream = c.close.bind(c);
},
pull: function() {
++pullCount;
},
cancel: function() {
assert_unreached('Unexpected cancel call');
}
});
var writeCalled = false;
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function() {
return startPromise;
},
write: function(chunk) {
if (!writeCalled) {
assert_equals(chunk, 'Hello', 'the chunk should be written to the writable stream');
writeCalled = true;
closeReadableStream();
} else {
assert_unreached('Unexpected extra write call');
}
return new Promise(test14.step_func(function() { }));
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function() {
assert_unreached('Unexpected abort call');
}
});
ws.write('Hello');
startPromise.then(test14.step_func(function() {
assert_equals(ws.state, 'waiting', 'the writable stream should be in the waiting state after starting');
rs.pipeTo(ws);
setTimeout(test14.step_func(function() {
assert_equals(ws.state, 'waiting', 'the writable stream should still be waiting since the write never completed');
assert_equals(pullCount, 1, 'pull should have been called only once');
assert_true(writeCalled);
test14.done();
}), 500);
}));
});
var test15 = async_test('Piping from an empty ReadableStream which becomes errored after a pipeTo call to a WritableStream in the waiting state');
test15.step(function() {
var errorReadableStream;
var pullCount = 0;
var rs = new ReadableStream({
start: function(c) {
errorReadableStream = c.error.bind(c);
},
pull: function() {
++pullCount;
},
cancel: function() {
assert_unreached('Unexpected cancel call');
}
});
var writeCalled = false;
var passedError = new Error('horrible things');
var startPromise = Promise.resolve();
var ws = new WritableStream({
start: function() {
return startPromise;
},
write: function(chunk) {
if (!writeCalled) {
assert_equals(chunk, 'Hello');
writeCalled = true;
} else {
assert_unreached('Unexpected extra write call');
}
return new Promise(test15.step_func(function() { }));
},
close: function() {
assert_unreached('Unexpected close call');
},
abort: function(reason) {
assert_equals(reason, passedError);
assert_true(writeCalled);
assert_equals(pullCount, 1);
test15.done();
}
});
ws.write('Hello');
startPromise.then(test15.step_func(function() {
assert_equals(ws.state, 'waiting');
rs.pipeTo(ws);
errorReadableStream(passedError);
}));
});
var test16 = async_test('Piping to a duck-typed asynchronous "writable stream" works');
test16.step(function() {
var rs = sequentialReadableStream(5, { async: true });
var chunksWritten = [];
var dest = {
state: 'writable',
write: function(chunk) {
chunksWritten.push(chunk);
return Promise.resolve();
},
get ready() {
return Promise.resolve();
},
close: function() {
assert_array_equals(chunksWritten, [1, 2, 3, 4, 5]);
test16.done();
return Promise.resolve();
},
abort: function() {
assert_unreached('Should not call abort');
},
closed: new Promise(test16.step_func(function() { }))
};
rs.pipeTo(dest);
});
var test17 = async_test('Piping to a stream that has been aborted passes through the error as the cancellation reason');
test17.step(function() {
var recordedReason;
var rs = new ReadableStream({
cancel: function(reason) {
recordedReason = reason;
}
});
var ws = new WritableStream();
var passedReason = new Error('I don\'t like you.');
ws.abort(passedReason);
rs.pipeTo(ws).catch(test17.step_func(function(e) {
assert_equals(e, passedReason, 'pipeTo rejection reason should be the cancellation reason');
assert_equals(recordedReason, passedReason, 'the recorded cancellation reason must be the passed abort reason');
test17.done();
}));
});
var test18 = async_test('Piping to a stream and then aborting it passes through the error as the cancellation reason');
test18.step(function() {
var recordedReason;
var rs = new ReadableStream({
cancel: function(reason) {
recordedReason = reason;
}
});
var ws = new WritableStream();
var passedReason = new Error('I don\'t like you.');
var pipeToPromise = rs.pipeTo(ws);
ws.abort(passedReason);
pipeToPromise.catch(test18.step_func(function(e) {
assert_equals(e, passedReason, 'pipeTo rejection reason should be the abortion reason');
assert_equals(recordedReason, passedReason, 'the recorded cancellation reason must be the passed abort reason');
test18.done();
}));
});
var test19 = async_test('Piping to a stream that has been closed propagates a TypeError cancellation reason backward');
test19.step(function() {
var recordedReason;
var rs = new ReadableStream({
cancel: function(reason) {
recordedReason = reason;
}
});
var ws = new WritableStream();
ws.close();
rs.pipeTo(ws).catch(test19.step_func(function(e) {
assert_throws(new TypeError(), function() { throw e; }, 'the rejection reason for the pipeTo promise should be a TypeError');
assert_throws(new TypeError(), function() { throw recordedReason; }, 'the recorded cancellation reason should be a TypeError');
test19.done();
}));
});
var test20 = async_test('Piping to a stream and then closing it propagates a TypeError cancellation reason backward');
test20.step(function() {
var recordedReason;
var rs = new ReadableStream({
cancel: function(reason) {
recordedReason = reason;
}
});
var ws = new WritableStream();
var pipeToPromise = rs.pipeTo(ws);
ws.close();
pipeToPromise.catch(test20.step_func(function(e) {
assert_throws(new TypeError(), function() { throw e; }, 'the rejection reason for the pipeTo promise should be a TypeError');
assert_throws(new TypeError(), function() { throw recordedReason; }, 'the recorded cancellation reason should be a TypeError');
test20.done();
}));
});
var test21 = async_test('Piping to a stream that errors on write should pass through the error as the cancellation reason');
test21.step(function() {
var recordedReason;
var rs = new ReadableStream({
start: function(c) {
c.enqueue('a');
c.enqueue('b');
c.enqueue('c');
},
cancel: function(reason) {
assert_equals(reason, passedError, 'the recorded cancellation reason must be the passed error');
test21.done();
}
});
var written = 0;
var passedError = new Error('I don\'t like you.');
var ws = new WritableStream({
write: function(chunk) {
return new Promise(test21.step_func(function(resolve, reject) {
if (++written > 1) {
reject(passedError);
} else {
resolve();
}
}));
}
});
rs.pipeTo(ws);
});
var test22 = async_test('Piping to a stream that errors on write should not pass through the error if the stream is already closed');
test22.step(function() {
var cancelCalled = false;
var rs = new ReadableStream({
start: function(c) {
c.enqueue('a');
c.enqueue('b');
c.enqueue('c');
c.close();
},
cancel: function() {
cancelCalled = true;
}
});
var written = 0;
var passedError = new Error('I don\'t like you.');
var ws = new WritableStream({
write: function(chunk) {
return new Promise(test22.step_func(function(resolve, reject) {
if (++written > 1) {
reject(passedError);
} else {
resolve();
}
}));
}
});
rs.pipeTo(ws).then(
test22.step_func(function() { assert_unreached('pipeTo should not fulfill'); }),
test22.step_func(function(r) {
assert_equals(r, passedError, 'pipeTo should reject with the same error as the write');
assert_equals(cancelCalled, false, 'cancel should not have been called');
test22.done();
})
);
});
var test23 = async_test('Piping to a stream that errors soon after writing should pass through the error as the cancellation reason');
test23.step(function() {
var recordedReason;
var rs = new ReadableStream({
start: function(c) {
c.enqueue('a');
c.enqueue('b');
c.enqueue('c');
},
cancel: function(reason) {
assert_equals(reason, passedError, 'the recorded cancellation reason must be the passed error');
test23.done();
}
});
var written = 0;
var passedError = new Error('I don\'t like you.');
var ws = new WritableStream({
write: function(chunk) {
return new Promise(test23.step_func(function(resolve, reject) {
if (++written > 1) {
setTimeout(test23.step_func(function() { reject(passedError); }), 200);
} else {
resolve();
}
}));
}
});
rs.pipeTo(ws);
});
// This test is alone under streams/pipe-to.html for timing reasons.
// var test24 = async_test('Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the source');
</script>