2 <script src='../../resources/testharness.js'></script>
3 <script src='../../resources/testharnessreport.js'></script>
4 <script src='resources/streams-utils.js'></script>
7 new ReadableStream(); // ReadableStream constructed with no parameters.
8 new ReadableStream({ }); // ReadableStream constructed with an empty object as parameter.
9 new ReadableStream(undefined); // ReadableStream constructed with undefined as parameter.
11 new ReadableStream(x) // ReadableStream constructed with an undefined variable as parameter.
12 }, 'ReadableStream can be constructed with no errors');
15 var methods = ['cancel', 'constructor', 'getReader', 'pipeThrough', 'pipeTo', 'tee'];
17 var rs = new ReadableStream();
18 var proto = Object.getPrototypeOf(rs);
20 assert_array_equals(Object.getOwnPropertyNames(proto).sort(), methods, 'should have all the correct methods');
22 for (var m of methods) {
23 var propDesc = Object.getOwnPropertyDescriptor(proto, m);
24 assert_equals(propDesc.enumerable, false, 'method should be non-enumerable');
25 assert_equals(propDesc.configurable, true, 'method should be configurable');
26 assert_equals(propDesc.writable, true, 'method should be writable');
27 assert_equals(typeof rs[m], 'function', 'should have be a method');
30 assert_equals(rs.cancel.length, 1, 'cancel should have 1 parameter');
31 assert_equals(rs.constructor.length, 0, 'constructor should have no parameters');
32 assert_equals(rs.getReader.length, 0, 'getReader should have no parameters');
33 assert_equals(rs.pipeThrough.length, 2, 'pipeThrough should have 2 parameters');
34 assert_equals(rs.pipeTo.length, 1, 'pipeTo should have 1 parameter');
35 assert_equals(rs.tee.length, 0, 'tee should have no parameters');
36 }, 'ReadableStream instances should have the correct list of properties');
39 assert_throws(new TypeError(), function() {
40 new ReadableStream({ start: 'potato'});
41 }, 'constructor should throw when start is not a function');
42 }, 'ReadableStream constructor should throw for non-function start arguments');
45 new ReadableStream({ cancel: '2'}); // Constructor should not throw when cancel is not a function.
46 }, 'ReadableStream constructor can get initial garbage as cancel argument');
49 new ReadableStream({ pull: { } }); // Constructor should not throw when pull is not a function.
50 }, 'ReadableStream constructor can get initial garbage as pull argument');
53 new ReadableStream({ strategy: 2 }); // Constructor should not throw when strategy is not an object.
54 }, 'ReadableStream constructor can get initial garbage as strategy argument');
56 var test1 = async_test('ReadableStream start should be able to return a promise', { timeout: 50 });
59 var readCalled = false;
60 var rs = new ReadableStream({
62 return new Promise(test1.step_func(function(resolve, reject) {
63 setTimeout(test1.step_func(function() {
72 var reader = rs.getReader();
74 reader.read().then(test1.step_func(function(r) {
76 assert_object_equals(r, { value: 'a', done: false }, 'value read should be the one enqueued');
79 reader.closed.then(test1.step_func(function() {
80 assert_true(readCalled);
81 test1.done('stream should close successfully');
85 var test2 = async_test('ReadableStream start should be able to return a promise and reject it', { timeout: 100 });
88 var theError = new Error('rejected!');
89 var rs = new ReadableStream({
91 return new Promise(test2.step_func(function(resolve, reject) {
92 setTimeout(test2.step_func(function() {
99 rs.getReader().closed.catch(test2.step_func(function(e) {
100 assert_equals(e, theError, 'promise should be rejected with the same error');
105 var test3 = async_test('ReadableStream should be able to enqueue different objects.');
106 test3.step(function() {
109 { potato: 'Give me more!'},
114 var rs = new ReadableStream({
116 for (var o of objects) {
123 var reader = rs.getReader();
125 reader.read().then(test3.step_func(function(r) {
126 assert_object_equals(r, { value: objects[readCalls++], done: false }, 'value read should be the one enqueued');
129 reader.read().then(test3.step_func(function(r) {
130 assert_object_equals(r, { value: objects[readCalls++], done: false }, 'value read should be the one enqueued');
133 reader.read().then(test3.step_func(function(r) {
134 assert_object_equals(r, { value: objects[readCalls++], done: false }, 'value read should be the one enqueued');
137 reader.closed.then(test3.step_func(function() {
138 assert_equals(readCalls, 3);
139 test3.done('stream should close correctly correctly');
144 var error = new Error('aaaugh!!');
146 assert_throws(error, function() { new ReadableStream({ start() { throw error; } }) }, 'error should be re-thrown');
147 }, 'ReadableStream: if start throws an error, it should be re-thrown');
149 var test4 = async_test('ReadableStream: if pull rejects, it should error the stream', { timeout: 50 });
150 test4.step(function() {
151 var error = new Error('pull failure');
152 var rs = new ReadableStream({
154 return Promise.reject(error);
158 var reader = rs.getReader();
163 reader.closed.catch(test4.step_func(function(e) {
166 assert_equals(e, error, 'closed should reject with the thrown error');
169 reader.read().catch(test4.step_func(function(e) {
172 assert_equals(e, error, 'read() should reject with the thrown error');
177 // var test5 = async_test('ReadableStream: should only call pull once upon starting the stream');
178 // test5.step(function() {
179 // var pullCount = 0;
180 // var startPromise = Promise.resolve();
181 // var rs = new ReadableStream({
182 // start: function() {
183 // return startPromise;
185 // pull: function() {
190 // startPromise.then(test5.step_func(function() {
191 // assert_equals(pullCount, 1, 'pull should be called once start finishes');
194 // setTimeout(test5.step_func(function() {
195 // assert_equals(pullCount, 1, 'pull should be called exactly once');
197 // }), standardTimeout);
200 // var test6 = async_test('ReadableStream: should only call pull once for a forever-empty stream, even after reading');
201 // test6.step(function() {
202 // var pullCount = 0;
203 // var startPromise = Promise.resolve();
204 // var rs = new ReadableStream({
205 // start: function() {
206 // return startPromise;
208 // pull: function() {
213 // startPromise.then(test6.step_func(function() {
214 // assert_equals(pullCount, 1, 'pull should be called once start finishes');
217 // rs.getReader().read();
219 // setTimeout(test6.step_func(function() {
220 // assert_equals(pullCount, 1, 'pull should be called exactly once');
222 // }), standardTimeout);
225 // var test7 = async_test('ReadableStream: should only call pull once on a non-empty stream read from before start fulfills');
226 // test7.step(function() {
227 // var pullCount = 0;
228 // var startPromise = Promise.resolve();
229 // var rs = new ReadableStream({
230 // start: function(c) {
232 // return startPromise;
234 // pull: function() {
239 // startPromise.then(test7.step_func(function() {
240 // assert_equals(pullCount, 1, 'pull should be called once start finishes');
243 // rs.getReader().read().then(test7.step_func(function(r) {
244 // assert_object_equals(r, { value: 'a', done: false }, 'first read() should return first chunk');
245 // assert_equals(pullCount, 1, 'pull should not have been called again');
248 // assert_equals(pullCount, 0, 'calling read() should not cause pull to be called yet');
250 // setTimeout(test7.step_func(function() {
251 // assert_equals(pullCount, 1, 'pull should be called exactly once');
253 // }), standardTimeout);
256 // var test8 = async_test('ReadableStream: should only call pull twice on a non-empty stream read from after start fulfills');
257 // test8.step(function() {
258 // var pullCount = 0;
259 // var startPromise = Promise.resolve();
260 // var rs = new ReadableStream({
261 // start: function(c) {
263 // return startPromise;
265 // pull: function() {
270 // startPromise.then(test8.step_func(function() {
271 // assert_equals(pullCount, 1, 'pull should be called once start finishes');
273 // rs.getReader().read().then(test8.step_func(function(r) {
274 // assert_object_equals(r, { value: 'a', done: false }, 'first read() should return first chunk');
275 // assert_equals(pullCount, 2, 'pull should be called again once read fulfills');
279 // assert_equals(pullCount, 0, 'calling read() should not cause pull to be called yet');
281 // setTimeout(test8.step_func(function() {
282 // assert_equals(pullCount, 2, 'pull should be called exactly twice')
284 // }), standardTimeout);
287 // var test9 = async_test('ReadableStream: should call pull in reaction to read()ing the last chunk, if not draining');
288 // test9.step(function() {
289 // var pullCount = 0;
291 // var startPromise = Promise.resolve();
292 // var pullPromise = Promise.resolve();
293 // var rs = new ReadableStream({
294 // start: function(c) {
296 // return startPromise;
298 // pull: function() {
300 // return pullPromise;
304 // var reader = rs.getReader();
306 // startPromise.then(test9.step_func(function() {
307 // assert_equals(pullCount, 1, 'pull should have been called once after read');
309 // controller.enqueue('a');
311 // return pullPromise.then(test9.step_func(function() {
312 // assert_equals(pullCount, 2, 'pull should have been called a second time after enqueue');
314 // return reader.read().then(test9.step_func(function() {
315 // assert_equals(pullCount, 3, 'pull should have been called a third time after read');
318 // })).catch(test9.step_func(function(e) {
319 // assert_unreached(e);
322 // setTimeout(test9.step_func(function() {
323 // assert_equals(pullCount, 3, 'pull should be called exactly thrice')
325 // }), standardTimeout);
328 // var test10 = async_test('ReadableStream: should not call pull() in reaction to read()ing the last chunk, if draining');
329 // test10.step(function() {
330 // var pullCount = 0;
332 // var startPromise = Promise.resolve();
333 // var pullPromise = Promise.resolve();
334 // var rs = new ReadableStream({
335 // start: function(c) {
337 // return startPromise;
339 // pull: function() {
341 // return pullPromise;
345 // var reader = rs.getReader();
347 // startPromise.then(test10.step_func(function() {
348 // assert_equals(pullCount, 1, 'pull should have been called once after read');
350 // controller.enqueue('a');
352 // return pullPromise.then(test10.step_func(function() {
353 // assert_equals(pullCount, 2, 'pull should have been called a second time after enqueue');
355 // controller.close();
357 // return reader.read().then(test10.step_func(function() {
358 // assert_equals(pullCount, 2, 'pull should not have been called a third time after read');
361 // })).catch(test10.step_func(function(e) {
362 // assert_unreached(e)
365 // setTimeout(test10.step_func(function() {
366 // assert_equals(pullCount, 2, 'pull should be called exactly twice')
368 // }), standardTimeout);
371 var test11 = async_test('ReadableStream: should not call pull until the previous pull call\'s promise fulfills');
372 test11.step(function() {
376 var startPromise = Promise.resolve();
377 var rs = new ReadableStream({
384 returnedPromise = new Promise(test11.step_func(function(r) { resolve = r; }));
385 return returnedPromise;
388 var reader = rs.getReader();
390 startPromise.then(test11.step_func(function() {
391 reader.read().then(test11.step_func(function(result1) {
392 assert_equals(timesCalled, 1, 'pull should have been called once after start, but not yet have been called a second time');
393 assert_object_equals(result1, { value: 'a', done: false }, 'read() should fulfill with the enqueued value');
395 setTimeout(test11.step_func(function() {
396 assert_equals(timesCalled, 1, 'after 30 ms, pull should still only have been called once');
400 returnedPromise.then(test11.step_func(function() {
401 assert_equals(timesCalled, 2, 'after the promise returned by pull is fulfilled, pull should be called a second time');
404 }), standardTimeout);
406 })).catch(test11.step_func(function(e) {
411 // var test12 = async_test('ReadableStream: should pull after start, and after every read');
412 // test12.step(function() {
413 // var timesCalled = 0;
414 // var startPromise = Promise.resolve();
415 // var rs = new ReadableStream({
416 // start: function(c) {
420 // return startPromise;
422 // pull: function() {
426 // size: function() {
429 // shouldApplyBackpressure: function() {
434 // var reader = rs.getReader();
436 // startPromise.then(test12.step_func(function() {
437 // return reader.read().then(test12.step_func(function(result1) {
438 // assert_object_equals(result1, { value: 'a', done: false }, 'first chunk should be as expected');
440 // return reader.read().then(test12.step_func(function(result2) {
441 // assert_object_equals(result2, { value: 'b', done: false }, 'second chunk should be as expected');
443 // return reader.read().then(test12.step_func(function(result3) {
444 // assert_object_equals(result3, { value: 'c', done: false }, 'third chunk should be as expected');
446 // setTimeout(test12.step_func(function() {
447 // // Once for after start, and once for every read.
448 // assert_equals(timesCalled, 4, 'pull() should be called exactly four times');
450 // }), standardTimeout);
454 // })).catch(test12.step_func(function(e) { assert_unreached(e); }));
457 var test13 = async_test('ReadableStream: should not call pull after start if the stream is now closed');
458 test13.step(function() {
460 var startPromise = Promise.resolve();
461 var rs = new ReadableStream({
472 startPromise.then(test13.step_func(function() {
473 assert_equals(timesCalled, 0, 'after start finishes, pull should not have been called');
475 var reader = rs.getReader();
476 return reader.read().then(test13.step_func(function() {
477 assert_equals(timesCalled, 0, 'reading should not have triggered a pull call');
479 return reader.closed.then(test13.step_func(function() {
480 assert_equals(timesCalled, 0, 'stream should have closed with still no calls to pull');
484 })).catch(test13.step_func(function(e) { assert_unreached(e); }));
487 var test14 = async_test('ReadableStream: should call pull after enqueueing from inside pull (with no read requests), if strategy allows');
488 test14.step(function() {
490 var startPromise = Promise.resolve();
491 var rs = new ReadableStream({
496 c.enqueue(++timesCalled);
502 shouldApplyBackpressure: function(size) {
508 startPromise.then(test14.step_func(function() {
509 // after start: size = 0, pull()
510 // after enqueue(1): size = 1, pull()
511 // after enqueue(2): size = 2, pull()
512 // after enqueue(3): size = 3, pull()
513 // after enqueue(4): size = 4, do not pull
514 assert_equals(timesCalled, 4, 'pull() should have been called four times');
519 var test15 = async_test('ReadableStream pull should be able to close a stream.', { timeout: 50 });
520 test15.step(function() {
521 var pullCalled = false;
522 var rs = new ReadableStream({
529 var reader = rs.getReader();
530 reader.closed.then(test15.step_func(function() {
531 assert_true(pullCalled);
532 test15.done('stream was closed successfully');
537 var rs = new ReadableStream({
539 assert_equals(c.enqueue('a'), true, 'the first enqueue should return true');
542 assert_throws(new TypeError(''), function() { c.enqueue('b'); }, 'enqueue after close should throw a TypeError');
545 }, 'ReadableStream: enqueue should throw when the stream is readable but draining');
548 var rs = new ReadableStream({
552 assert_throws(new TypeError(), function() { c.enqueue('a'); }, 'enqueue after close should throw a TypeError');
555 }, 'ReadableStream: enqueue should throw when the stream is closed');
558 var expectedError = new Error('i am sad');
559 var rs = new ReadableStream({
561 c.error(expectedError);
563 assert_throws(expectedError, function() { c.enqueue('a'); }, 'enqueue after error should throw that error');
566 }, 'ReadableStream: enqueue should throw the stored error when the stream is errored');
568 var test16 = async_test('ReadableStream: should call underlying source methods as methods');
569 test16.step(function() {
572 var cancelCalled = 0;
573 var strategyCalled = 0;
581 assert_equals(this, theSource, 'start() should be called with the correct this');
587 assert_equals(this, theSource, 'pull() should be called with the correct this');
592 assert_equals(this, theSource, 'cancel() should be called with the correct this');
596 // Called three times
598 assert_equals(this, theSource, 'strategy getter should be called with the correct this');
603 var theSource = new Source();
604 theSource.debugName = 'the source object passed to the constructor'; // makes test failures easier to diagnose
605 var rs = new ReadableStream(theSource);
607 var reader = rs.getReader();
608 reader.read().then(test16.step_func(function() {
609 reader.releaseLock();
611 assert_equals(startCalled, 1);
612 assert_equals(pullCalled, 1);
613 assert_equals(cancelCalled, 1);
614 assert_equals(strategyCalled, 3);
616 })).catch(test16.step_func(function(e) { assert_unreached(e); } ));
622 assert_equals(c.enqueue('a'), true, 'first enqueue should return true');
623 assert_equals(c.enqueue('b'), false, 'second enqueue should return false');
624 assert_equals(c.enqueue('c'), false, 'third enqueue should return false');
625 assert_equals(c.enqueue('d'), false, 'fourth enqueue should return false');
626 assert_equals(c.enqueue('e'), false, 'fifth enqueue should return false');
629 }, 'ReadableStream strategies: the default strategy should return false for all but the first enqueue call');
631 var test17 = async_test('ReadableStream strategies: the default strategy should continue returning true from enqueue if the chunks are read immediately');
632 test17.step(function() {
634 var rs = new ReadableStream({
639 var reader = rs.getReader();
641 assert_equals(controller.enqueue('a'), true, 'first enqueue should return true');
643 reader.read().then(test17.step_func(function(result1) {
644 assert_object_equals(result1, { value: 'a', done: false }, 'first chunk read should be correct');
645 assert_equals(controller.enqueue('b'), true, 'second enqueue should return true');
647 return reader.read();
648 })).then(test17.step_func(function(result2) {
649 assert_object_equals(result2, { value: 'b', done: false }, 'second chunk read should be correct');
650 assert_equals(controller.enqueue('c'), true, 'third enqueue should return true');
652 return reader.read();
653 })).then(test17.step_func(function(result3) {
654 assert_object_equals(result3, { value: 'c', done: false }, 'third chunk read should be correct');
655 assert_equals(controller.enqueue('d'), true, 'fourth enqueue should return true');
658 })).catch(test17.step_func(function(e) { assert_unreached(e); } ));
661 var test18 = async_test('ReadableStream integration test: adapting a random push source', { timeout: 50 });
662 test18.step(function() {
663 var pullChecked = false;
664 var randomSource = new RandomPushSource(8);
666 var rs = new ReadableStream({
668 assert_equals(typeof c, 'object', 'c should be an object in start');
669 assert_equals(typeof c.enqueue, 'function', 'enqueue should be a function in start');
670 assert_equals(typeof c.close, 'function', 'close should be a function in start');
671 assert_equals(typeof c.error, 'function', 'error should be a function in start');
673 randomSource.ondata = test18.step_func(function(chunk) {
674 if (!c.enqueue(chunk)) {
675 randomSource.readStop();
679 randomSource.onend = c.close.bind(c);
680 randomSource.onerror = c.error.bind(c);
686 assert_equals(typeof c, 'object', 'c should be an object in pull');
687 assert_equals(typeof c.enqueue, 'function', 'enqueue should be a function in pull');
688 assert_equals(typeof c.close, 'function', 'close should be a function in pull');
691 randomSource.readStart();
695 readableStreamToArray(rs).then(test18.step_func(function(chunks) {
696 assert_equals(chunks.length, 8, '8 chunks should be read');
697 for (var i = 0; i < chunks.length; i++) {
698 assert_equals(chunks[i].length, 128, 'chunk should have 128 bytes');
702 }), test18.step_func(function(e) { assert_reached(e); }));
705 var test19 = async_test('ReadableStream integration test: adapting a sync pull source', { timeout: 50 });
706 test19.step(function() {
707 var rs = sequentialReadableStream(10);
709 readableStreamToArray(rs).then(test19.step_func(function(chunks) {
710 assert_equals(rs.source.closed, true, 'source should be closed after all chunks are read');
711 assert_array_equals(chunks, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'the expected 10 chunks should be read');
717 var test20 = async_test('ReadableStream integration test: adapting an async pull source', { timeout: 50 });
718 test20.step(function() {
719 var rs = sequentialReadableStream(10, { async: true });
721 readableStreamToArray(rs).then(test20.step_func(function(chunks) {
722 assert_equals(rs.source.closed, true, 'source should be closed after all chunks are read');
723 assert_array_equals(chunks, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'the expected 10 chunks should be read');