[Streams API] Implement pulling of a source by a ReadableStream
[WebKit-https.git] / LayoutTests / streams / reference-implementation / readable-stream.html
1 <!DOCTYPE html>
2 <script src='../../resources/testharness.js'></script>
3 <script src='../../resources/testharnessreport.js'></script>
4 <script src='resources/streams-utils.js'></script>
5 <script>
6 test(function() {
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.
10     var x;
11     new ReadableStream(x) // ReadableStream constructed with an undefined variable as parameter.
12 }, 'ReadableStream can be constructed with no errors');
13
14 test(function() {
15     var methods = ['cancel', 'constructor', 'getReader', 'pipeThrough', 'pipeTo', 'tee'];
16
17     var rs = new ReadableStream();
18     var proto = Object.getPrototypeOf(rs);
19
20     assert_array_equals(Object.getOwnPropertyNames(proto).sort(), methods, 'should have all the correct methods');
21
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');
28     }
29
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');
37
38 test(function() {
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');
43
44 test(function() {
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');
47
48 test(function() {
49     new ReadableStream({ pull: { } }); // Constructor should not throw when pull is not a function.
50 }, 'ReadableStream constructor can get initial garbage as pull argument');
51
52 test(function() {
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');
55
56 var test1 = async_test('ReadableStream start should be able to return a promise', { timeout: 50 });
57 test1.step(function()
58 {
59     var readCalled = false;
60     var rs = new ReadableStream({
61         start: function(c) {
62             return new Promise(test1.step_func(function(resolve, reject) {
63                 setTimeout(test1.step_func(function() {
64                     c.enqueue('a');
65                     c.close();
66                     resolve();
67                 }), standardTimeout);
68             }));
69         },
70     });
71
72     var reader = rs.getReader();
73
74     reader.read().then(test1.step_func(function(r) {
75         readCalled = true;
76         assert_object_equals(r, { value: 'a', done: false }, 'value read should be the one enqueued');
77     }));
78
79     reader.closed.then(test1.step_func(function() {
80         assert_true(readCalled);
81         test1.done('stream should close successfully');
82     }));
83 });
84
85 var test2 = async_test('ReadableStream start should be able to return a promise and reject it', { timeout: 100 });
86 test2.step(function()
87 {
88     var theError = new Error('rejected!');
89     var rs = new ReadableStream({
90         start: function() {
91             return new Promise(test2.step_func(function(resolve, reject) {
92                 setTimeout(test2.step_func(function() {
93                     reject(theError);
94                 }), standardTimeout);
95             }));
96         },
97     });
98
99     rs.getReader().closed.catch(test2.step_func(function(e) {
100         assert_equals(e, theError, 'promise should be rejected with the same error');
101         test2.done();
102     }));
103 });
104
105 var test3 = async_test('ReadableStream should be able to enqueue different objects.');
106 test3.step(function() {
107     var readCalls = 0;
108     var objects = [
109     { potato: 'Give me more!'},
110     'test',
111     1
112     ];
113
114     var rs = new ReadableStream({
115         start: function(c) {
116             for (var o of objects) {
117                 c.enqueue(o);
118             }
119             c.close();
120         }
121     });
122
123     var reader = rs.getReader();
124
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');
127     }));
128
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');
131     }));
132
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');
135     }));
136
137     reader.closed.then(test3.step_func(function() {
138         assert_equals(readCalls, 3);
139         test3.done('stream should close correctly correctly');
140     }));
141 });
142
143 test(function() {
144     var error = new Error('aaaugh!!');
145
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');
148
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({
153         pull: function() {
154             return Promise.reject(error);
155         }
156     });
157
158     var reader = rs.getReader();
159
160     var closed = false;
161     var read = false;
162
163     reader.closed.catch(test4.step_func(function(e) {
164         closed = true;
165         assert_false(read);
166         assert_equals(e, error, 'closed should reject with the thrown error');
167     }));
168
169     reader.read().catch(test4.step_func(function(e) {
170         read = true;
171         assert_true(closed);
172         assert_equals(e, error, 'read() should reject with the thrown error');
173         test4.done();
174     }));
175 });
176
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;
184 //         },
185 //         pull: function() {
186 //             pullCount++;
187 //         }
188 //     });
189
190 //     startPromise.then(test5.step_func(function() {
191 //         assert_equals(pullCount, 1, 'pull should be called once start finishes');
192 //     }));
193
194 //     setTimeout(test5.step_func(function() {
195 //         assert_equals(pullCount, 1, 'pull should be called exactly once');
196 //         test5.done();
197 //     }), standardTimeout);
198 // });
199
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;
207 //         },
208 //         pull: function() {
209 //             pullCount++;
210 //         }
211 //     });
212
213 //     startPromise.then(test6.step_func(function() {
214 //         assert_equals(pullCount, 1, 'pull should be called once start finishes');
215 //     }));
216
217 //     rs.getReader().read();
218
219 //     setTimeout(test6.step_func(function() {
220 //         assert_equals(pullCount, 1, 'pull should be called exactly once');
221 //         test6.done();
222 //     }), standardTimeout);
223 // });
224
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) {
231 //             c.enqueue('a');
232 //             return startPromise;
233 //         },
234 //         pull: function() {
235 //             pullCount++;
236 //         }
237 //     });
238
239 //     startPromise.then(test7.step_func(function() {
240 //         assert_equals(pullCount, 1, 'pull should be called once start finishes');
241 //     }));
242
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');
246 //     }));
247
248 //     assert_equals(pullCount, 0, 'calling read() should not cause pull to be called yet');
249
250 //     setTimeout(test7.step_func(function() {
251 //         assert_equals(pullCount, 1, 'pull should be called exactly once');
252 //         test7.done();
253 //     }), standardTimeout);
254 // });
255
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) {
262 //             c.enqueue('a');
263 //             return startPromise;
264 //         },
265 //         pull: function() {
266 //             pullCount++;
267 //         }
268 //     });
269
270 //     startPromise.then(test8.step_func(function() {
271 //         assert_equals(pullCount, 1, 'pull should be called once start finishes');
272
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');
276 //         }));
277 //     }));
278
279 //     assert_equals(pullCount, 0, 'calling read() should not cause pull to be called yet');
280
281 //     setTimeout(test8.step_func(function() {
282 //         assert_equals(pullCount, 2, 'pull should be called exactly twice')
283 //         test8.done();
284 //     }), standardTimeout);
285 // });
286
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;
290 //     var controller;
291 //     var startPromise = Promise.resolve();
292 //     var pullPromise = Promise.resolve();
293 //     var rs = new ReadableStream({
294 //         start: function(c) {
295 //             controller = c;
296 //             return startPromise;
297 //         },
298 //         pull: function() {
299 //             ++pullCount;
300 //             return pullPromise;
301 //         }
302 //     });
303
304 //     var reader = rs.getReader();
305
306 //     startPromise.then(test9.step_func(function() {
307 //         assert_equals(pullCount, 1, 'pull should have been called once after read');
308
309 //         controller.enqueue('a');
310
311 //         return pullPromise.then(test9.step_func(function() {
312 //             assert_equals(pullCount, 2, 'pull should have been called a second time after enqueue');
313
314 //             return reader.read().then(test9.step_func(function() {
315 //                 assert_equals(pullCount, 3, 'pull should have been called a third time after read');
316 //             }));
317 //         }));
318 //     })).catch(test9.step_func(function(e) {
319 //         assert_unreached(e);
320 //     }));
321
322 //     setTimeout(test9.step_func(function() {
323 //         assert_equals(pullCount, 3, 'pull should be called exactly thrice')
324 //         test9.done();
325 //     }), standardTimeout);
326 // });
327
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;
331 //     var controller;
332 //     var startPromise = Promise.resolve();
333 //     var pullPromise = Promise.resolve();
334 //     var rs = new ReadableStream({
335 //         start: function(c) {
336 //             controller = c;
337 //             return startPromise;
338 //         },
339 //         pull: function() {
340 //             ++pullCount;
341 //             return pullPromise;
342 //         }
343 //     });
344
345 //     var reader = rs.getReader();
346
347 //     startPromise.then(test10.step_func(function() {
348 //         assert_equals(pullCount, 1, 'pull should have been called once after read');
349
350 //         controller.enqueue('a');
351
352 //         return pullPromise.then(test10.step_func(function() {
353 //             assert_equals(pullCount, 2, 'pull should have been called a second time after enqueue');
354
355 //             controller.close();
356
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');
359 //             }));
360 //         }));
361 //     })).catch(test10.step_func(function(e) {
362 //         assert_unreached(e)
363 //     }));
364
365 //     setTimeout(test10.step_func(function() {
366 //         assert_equals(pullCount, 2, 'pull should be called exactly twice')
367 //         test10.done();
368 //     }), standardTimeout);
369 // });
370
371 var test11 = async_test('ReadableStream: should not call pull until the previous pull call\'s promise fulfills');
372 test11.step(function() {
373     var resolve;
374     var returnedPromise;
375     var timesCalled = 0;
376     var startPromise = Promise.resolve();
377     var rs = new ReadableStream({
378         start: function(c) {
379             c.enqueue('a');
380             return startPromise;
381         },
382         pull: function() {
383             ++timesCalled;
384             returnedPromise = new Promise(test11.step_func(function(r) { resolve = r; }));
385             return returnedPromise;
386         }
387     });
388     var reader = rs.getReader();
389
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');
394
395             setTimeout(test11.step_func(function() {
396                 assert_equals(timesCalled, 1, 'after 30 ms, pull should still only have been called once');
397
398                 resolve();
399
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');
402                     test11.done();
403                 }));
404             }), standardTimeout);
405         }))
406     })).catch(test11.step_func(function(e) {
407         assert_unreached(e)
408     }));
409 });
410
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) {
417 //             c.enqueue('a');
418 //             c.enqueue('b');
419 //             c.enqueue('c');
420 //             return startPromise;
421 //         },
422 //         pull: function() {
423 //             ++timesCalled;
424 //         },
425 //         strategy: {
426 //             size: function() {
427 //                 return 1;
428 //             },
429 //             shouldApplyBackpressure: function() {
430 //                 return false;
431 //             }
432 //         }
433 //     });
434 //     var reader = rs.getReader();
435
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');
439
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');
442
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');
445
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');
449 //                         test12.done();
450 //                     }), standardTimeout);
451 //                 }));
452 //             }));
453 //         }));
454 //     })).catch(test12.step_func(function(e) { assert_unreached(e); }));
455 // });
456
457 var test13 = async_test('ReadableStream: should not call pull after start if the stream is now closed');
458 test13.step(function() {
459     var timesCalled = 0;
460     var startPromise = Promise.resolve();
461     var rs = new ReadableStream({
462         start: function(c) {
463             c.enqueue('a');
464             c.close();
465             return startPromise;
466         },
467         pull: function() {
468             ++timesCalled;
469         }
470     });
471
472     startPromise.then(test13.step_func(function() {
473         assert_equals(timesCalled, 0, 'after start finishes, pull should not have been called');
474
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');
478
479             return reader.closed.then(test13.step_func(function() {
480                 assert_equals(timesCalled, 0, 'stream should have closed with still no calls to pull');
481                 test13.done();
482             }));
483         }));
484     })).catch(test13.step_func(function(e) { assert_unreached(e); }));
485 });
486
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() {
489     var timesCalled = 0;
490     var startPromise = Promise.resolve();
491     var rs = new ReadableStream({
492         start: function() {
493             return startPromise;
494         },
495         pull: function(c) {
496             c.enqueue(++timesCalled);
497         },
498         strategy: {
499             size: function() {
500                 return 1;
501             },
502             shouldApplyBackpressure: function(size) {
503                 return size > 3;
504             }
505         }
506     });
507
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');
515         test14.done();
516     }));
517 });
518
519 var test15 = async_test('ReadableStream pull should be able to close a stream.');
520 test15.step(function() {
521     var pullCalled = false;
522     var rs = new ReadableStream({
523         pull: function(c) {
524             pullCalled = true;
525             c.close();
526         }
527     });
528
529     var reader = rs.getReader();
530     reader.closed.then(test15.step_func(function() {
531         assert_true(pullCalled);
532         test15.done('stream was closed successfully');
533     }));
534 });
535
536 test(function() {
537   var rs = new ReadableStream({
538       start: function(c) {
539           assert_equals(c.enqueue('a'), true, 'the first enqueue should return true');
540           c.close();
541
542           assert_throws(new TypeError(''), function() { c.enqueue('b'); }, 'enqueue after close should throw a TypeError');
543       }
544   });
545 }, 'ReadableStream: enqueue should throw when the stream is readable but draining');
546
547 test(function() {
548     var rs = new ReadableStream({
549         start: function(c) {
550             c.close();
551
552             assert_throws(new TypeError(), function() { c.enqueue('a'); }, 'enqueue after close should throw a TypeError');
553         }
554     });
555 }, 'ReadableStream: enqueue should throw when the stream is closed');
556
557 test(function() {
558     var expectedError = new Error('i am sad');
559     var rs = new ReadableStream({
560         start: function(c) {
561             c.error(expectedError);
562
563             assert_throws(expectedError, function() { c.enqueue('a'); }, 'enqueue after error should throw that error');
564         }
565     });
566 }, 'ReadableStream: enqueue should throw the stored error when the stream is errored');
567
568 var test16 = async_test('ReadableStream: should call underlying source methods as methods');
569 test16.step(function() {
570     var startCalled = 0;
571     var pullCalled = 0;
572     var cancelCalled = 0;
573     var strategyCalled = 0;
574
575     function Source() {
576     }
577
578     Source.prototype = {
579         start: function(c) {
580             startCalled++;
581             assert_equals(this, theSource, 'start() should be called with the correct this');
582             c.enqueue('a');
583         },
584
585         pull: function() {
586             pullCalled++;
587             assert_equals(this, theSource, 'pull() should be called with the correct this');
588         },
589
590         cancel: function() {
591             cancelCalled++;
592             assert_equals(this, theSource, 'cancel() should be called with the correct this');
593         },
594
595         get strategy() {
596             // Called three times
597             strategyCalled++;
598             assert_equals(this, theSource, 'strategy getter should be called with the correct this');
599             return undefined;
600         }
601     };
602
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);
606
607     var reader = rs.getReader();
608     reader.read().then(test16.step_func(function() {
609         reader.releaseLock();
610         rs.cancel();
611         assert_equals(startCalled, 1);
612         assert_equals(pullCalled, 1);
613         assert_equals(cancelCalled, 1);
614         assert_equals(strategyCalled, 3);
615         test16.done();
616     })).catch(test16.step_func(function(e) { assert_unreached(e); } ));
617 });
618
619 test(function() {
620   new ReadableStream({
621       start: function(c) {
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');
627       }
628   });
629 }, 'ReadableStream strategies: the default strategy should return false for all but the first enqueue call');
630
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() {
633     var controller;
634     var rs = new ReadableStream({
635         start: function(c) {
636             controller = c;
637         }
638     });
639     var reader = rs.getReader();
640
641     assert_equals(controller.enqueue('a'), true, 'first enqueue should return true');
642
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');
646
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');
651
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');
656
657         test17.done();
658     })).catch(test17.step_func(function(e) { assert_unreached(e); } ));
659 });
660
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);
665
666     var rs = new ReadableStream({
667         start: function(c) {
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');
672
673             randomSource.ondata = test18.step_func(function(chunk) {
674                 if (!c.enqueue(chunk)) {
675                     randomSource.readStop();
676                 }
677             });
678
679             randomSource.onend = c.close.bind(c);
680             randomSource.onerror = c.error.bind(c);
681         },
682
683         pull: function(c) {
684             if (!pullChecked) {
685                 pullChecked = true;
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');
689             }
690
691             randomSource.readStart();
692         }
693     });
694
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');
699         }
700
701         test18.done();
702     }), test18.step_func(function(e) { assert_reached(e); }));
703 });
704
705 var test19 = async_test('ReadableStream integration test: adapting a sync pull source');
706 test19.step(function() {
707     var rs = sequentialReadableStream(10);
708
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');
712
713         test19.done();
714     }));
715 });
716 /*
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 });
720
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');
724
725         test20.done();
726     }));
727 });
728 */
729 </script>