Sync web-platform-tests up to revision a5b95cb31914507088a4eda16f7674bbc6f3313f
[WebKit-https.git] / LayoutTests / imported / w3c / web-platform-tests / streams / readable-streams / general.js
1 'use strict';
2
3 if (self.importScripts) {
4   self.importScripts('../resources/test-utils.js');
5   self.importScripts('../resources/rs-utils.js');
6   self.importScripts('/resources/testharness.js');
7 }
8
9 test(() => {
10
11   new ReadableStream(); // ReadableStream constructed with no parameters
12   new ReadableStream({ }); // ReadableStream constructed with an empty object as parameter
13   new ReadableStream({ type: undefined }); // ReadableStream constructed with undefined type
14   new ReadableStream(undefined); // ReadableStream constructed with undefined as parameter
15
16   let x;
17   new ReadableStream(x); // ReadableStream constructed with an undefined variable as parameter
18
19 }, 'ReadableStream can be constructed with no errors');
20
21 test(() => {
22
23   assert_throws(new TypeError(), () => new ReadableStream(null), 'constructor should throw when the source is null');
24
25 }, 'ReadableStream can\'t be constructed with garbage');
26
27 test(() => {
28
29   assert_throws(new RangeError(), () => new ReadableStream({ type: null }),
30     'constructor should throw when the type is null');
31   assert_throws(new RangeError(), () => new ReadableStream({ type: '' }),
32     'constructor should throw when the type is empty string');
33   assert_throws(new RangeError(), () => new ReadableStream({ type: 'asdf' }),
34     'constructor should throw when the type is asdf');
35
36 }, 'ReadableStream can\'t be constructed with an invalid type');
37
38 test(() => {
39
40   const methods = ['cancel', 'constructor', 'getReader', 'pipeThrough', 'pipeTo', 'tee'];
41   const properties = methods.concat(['locked']).sort();
42
43   const rs = new ReadableStream();
44   const proto = Object.getPrototypeOf(rs);
45
46   assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties, 'should have all the correct methods');
47
48   for (const m of methods) {
49     const propDesc = Object.getOwnPropertyDescriptor(proto, m);
50     assert_false(propDesc.enumerable, 'method should be non-enumerable');
51     assert_true(propDesc.configurable, 'method should be configurable');
52     assert_true(propDesc.writable, 'method should be writable');
53     assert_equals(typeof rs[m], 'function', 'method should be a function');
54   }
55
56   const lockedPropDesc = Object.getOwnPropertyDescriptor(proto, 'locked');
57   assert_false(lockedPropDesc.enumerable, 'locked should be non-enumerable');
58   assert_equals(lockedPropDesc.writable, undefined, 'locked should not be a data property');
59   assert_equals(typeof lockedPropDesc.get, 'function', 'locked should have a getter');
60   assert_equals(lockedPropDesc.set, undefined, 'locked should not have a setter');
61   assert_true(lockedPropDesc.configurable, 'locked should be configurable');
62
63   assert_equals(rs.cancel.length, 1, 'cancel should have 1 parameter');
64   assert_equals(rs.constructor.length, 0, 'constructor should have no parameters');
65   assert_equals(rs.getReader.length, 0, 'getReader should have no parameters');
66   assert_equals(rs.pipeThrough.length, 2, 'pipeThrough should have 2 parameters');
67   assert_equals(rs.pipeTo.length, 1, 'pipeTo should have 1 parameter');
68   assert_equals(rs.tee.length, 0, 'tee should have no parameters');
69
70 }, 'ReadableStream instances should have the correct list of properties');
71
72 test(() => {
73
74   assert_throws(new TypeError(), () => {
75     new ReadableStream({ start: 'potato' });
76   }, 'constructor should throw when start is not a function');
77
78 }, 'ReadableStream constructor should throw for non-function start arguments');
79
80 test(() => {
81
82   new ReadableStream({ cancel: '2' });
83
84 }, 'ReadableStream constructor can get initial garbage as cancel argument');
85
86 test(() => {
87
88   new ReadableStream({ pull: { } });
89
90 }, 'ReadableStream constructor can get initial garbage as pull argument');
91
92 test(() => {
93
94   let startCalled = false;
95
96   const source = {
97     start(controller) {
98       assert_equals(this, source, 'source is this during start');
99
100       const methods = ['close', 'enqueue', 'error', 'constructor'];
101       const properties = ['desiredSize'].concat(methods).sort();
102       const proto = Object.getPrototypeOf(controller);
103
104       assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties,
105         'the controller should have the right properties');
106
107       for (const m of methods) {
108         const propDesc = Object.getOwnPropertyDescriptor(proto, m);
109         assert_equals(typeof controller[m], 'function', `should have a ${m} method`);
110         assert_false(propDesc.enumerable, m + ' should be non-enumerable');
111         assert_true(propDesc.configurable, m + ' should be configurable');
112         assert_true(propDesc.writable, m + ' should be writable');
113       }
114
115       const desiredSizePropDesc = Object.getOwnPropertyDescriptor(proto, 'desiredSize');
116       assert_false(desiredSizePropDesc.enumerable, 'desiredSize should be non-enumerable');
117       assert_equals(desiredSizePropDesc.writable, undefined, 'desiredSize should not be a data property');
118       assert_equals(typeof desiredSizePropDesc.get, 'function', 'desiredSize should have a getter');
119       assert_equals(desiredSizePropDesc.set, undefined, 'desiredSize should not have a setter');
120       assert_true(desiredSizePropDesc.configurable, 'desiredSize should be configurable');
121
122       assert_equals(controller.close.length, 0, 'close should have no parameters');
123       assert_equals(controller.constructor.length, 4, 'constructor should have 4 parameter');
124       assert_equals(controller.enqueue.length, 1, 'enqueue should have 1 parameter');
125       assert_equals(controller.error.length, 1, 'error should have 1 parameter');
126
127       startCalled = true;
128     }
129   };
130
131   new ReadableStream(source);
132   assert_true(startCalled);
133
134 }, 'ReadableStream start should be called with the proper parameters');
135
136 test(() => {
137
138   let startCalled = false;
139   const source = {
140     start(controller) {
141       const properties = ['close', 'constructor', 'desiredSize', 'enqueue', 'error'];
142       assert_array_equals(Object.getOwnPropertyNames(Object.getPrototypeOf(controller)).sort(), properties,
143         'prototype should have the right properties');
144
145       controller.test = '';
146       assert_array_equals(Object.getOwnPropertyNames(Object.getPrototypeOf(controller)).sort(), properties,
147         'prototype should still have the right properties');
148       assert_not_equals(Object.getOwnPropertyNames(controller).indexOf('test'), -1,
149         '"test" should be a property of the controller');
150
151       startCalled = true;
152     }
153   };
154
155   new ReadableStream(source);
156   assert_true(startCalled);
157
158 }, 'ReadableStream start controller parameter should be extensible');
159
160 promise_test(() => {
161
162   function SimpleStreamSource() {}
163   let resolve;
164   const promise = new Promise(r => resolve = r);
165   SimpleStreamSource.prototype = {
166     start: resolve
167   };
168
169   new ReadableStream(new SimpleStreamSource());
170   return promise;
171
172 }, 'ReadableStream should be able to call start method within prototype chain of its source');
173
174 promise_test(() => {
175
176   const rs = new ReadableStream({
177     start(c) {
178       return delay(5).then(() => {
179         c.enqueue('a');
180         c.close();
181       });
182     }
183   });
184
185   const reader = rs.getReader();
186   return reader.read().then(r => {
187     assert_object_equals(r, { value: 'a', done: false }, 'value read should be the one enqueued');
188     return reader.closed;
189   });
190
191 }, 'ReadableStream start should be able to return a promise');
192
193 promise_test(() => {
194
195   const theError = new Error('rejected!');
196   const rs = new ReadableStream({
197     start() {
198       return delay(1).then(() => {
199         throw theError;
200       });
201     }
202   });
203
204   return rs.getReader().closed.then(() => {
205     assert_unreached('closed promise should be rejected');
206   }, e => {
207     assert_equals(e, theError, 'promise should be rejected with the same error');
208   });
209
210 }, 'ReadableStream start should be able to return a promise and reject it');
211
212 promise_test(() => {
213
214   const objects = [
215     { potato: 'Give me more!' },
216     'test',
217     1
218   ];
219
220   const rs = new ReadableStream({
221     start(c) {
222       for (const o of objects) {
223         c.enqueue(o);
224       }
225       c.close();
226     }
227   });
228
229   const reader = rs.getReader();
230
231   return Promise.all([reader.read(), reader.read(), reader.read(), reader.closed]).then(r => {
232     assert_object_equals(r[0], { value: objects[0], done: false }, 'value read should be the one enqueued');
233     assert_object_equals(r[1], { value: objects[1], done: false }, 'value read should be the one enqueued');
234     assert_object_equals(r[2], { value: objects[2], done: false }, 'value read should be the one enqueued');
235   });
236
237 }, 'ReadableStream should be able to enqueue different objects.');
238
239 promise_test(() => {
240
241   const error = new Error('pull failure');
242   const rs = new ReadableStream({
243     pull() {
244       return Promise.reject(error);
245     }
246   });
247
248   const reader = rs.getReader();
249
250   let closed = false;
251   let read = false;
252
253   return Promise.all([
254     reader.closed.then(() => {
255       assert_unreached('closed should be rejected');
256     }, e => {
257       closed = true;
258       assert_true(read);
259       assert_equals(e, error, 'closed should be rejected with the thrown error');
260     }),
261     reader.read().then(() => {
262       assert_unreached('read() should be rejected');
263     }, e => {
264       read = true;
265       assert_false(closed);
266       assert_equals(e, error, 'read() should be rejected with the thrown error');
267     })
268   ]);
269
270 }, 'ReadableStream: if pull rejects, it should error the stream');
271
272 promise_test(() => {
273
274   let pullCount = 0;
275   const startPromise = Promise.resolve();
276
277   new ReadableStream({
278     start() {
279       return startPromise;
280     },
281     pull() {
282       pullCount++;
283     }
284   });
285
286   return startPromise.then(() => {
287     assert_equals(pullCount, 1, 'pull should be called once start finishes');
288     return delay(10);
289   }).then(() => {
290     assert_equals(pullCount, 1, 'pull should be called exactly once');
291   });
292
293 }, 'ReadableStream: should only call pull once upon starting the stream');
294
295 promise_test(() => {
296
297   let pullCount = 0;
298   const startPromise = Promise.resolve();
299
300   const rs = new ReadableStream({
301     start() {
302       return startPromise;
303     },
304     pull(c) {
305       // Don't enqueue immediately after start. We want the stream to be empty when we call .read() on it.
306       if (pullCount > 0) {
307         c.enqueue(pullCount);
308       }
309       ++pullCount;
310     }
311   });
312
313   return startPromise.then(() => {
314     assert_equals(pullCount, 1, 'pull should be called once start finishes');
315   }).then(() => {
316     const reader = rs.getReader();
317     const read = reader.read();
318     assert_equals(pullCount, 2, 'pull should be called when read is called');
319     return read;
320   }).then(result => {
321     assert_equals(pullCount, 3, 'pull should be called again in reaction to calling read');
322     assert_object_equals(result, { value: 1, done: false }, 'the result read should be the one enqueued');
323   });
324
325 }, 'ReadableStream: should call pull when trying to read from a started, empty stream');
326
327 promise_test(() => {
328
329   let pullCount = 0;
330   const startPromise = Promise.resolve();
331
332   const rs = new ReadableStream({
333     start(c) {
334       c.enqueue('a');
335       return startPromise;
336     },
337     pull() {
338       pullCount++;
339     }
340   });
341
342   const read = rs.getReader().read();
343   assert_equals(pullCount, 0, 'calling read() should not cause pull to be called yet');
344
345   return startPromise.then(() => {
346     assert_equals(pullCount, 1, 'pull should be called once start finishes');
347     return read;
348   }).then(r => {
349     assert_object_equals(r, { value: 'a', done: false }, 'first read() should return first chunk');
350     assert_equals(pullCount, 1, 'pull should not have been called again');
351     return delay(10);
352   }).then(() => {
353     assert_equals(pullCount, 1, 'pull should be called exactly once');
354   });
355
356 }, 'ReadableStream: should only call pull once on a non-empty stream read from before start fulfills');
357
358 promise_test(() => {
359
360   let pullCount = 0;
361   const startPromise = Promise.resolve();
362
363   const rs = new ReadableStream({
364     start(c) {
365       c.enqueue('a');
366       return startPromise;
367     },
368     pull() {
369       pullCount++;
370     }
371   });
372
373   return startPromise.then(() => {
374     assert_equals(pullCount, 0, 'pull should not be called once start finishes, since the queue is full');
375
376     const read = rs.getReader().read();
377     assert_equals(pullCount, 1, 'calling read() should cause pull to be called immediately');
378     return read;
379   }).then(r => {
380     assert_object_equals(r, { value: 'a', done: false }, 'first read() should return first chunk');
381     return delay(10);
382   }).then(() => {
383     assert_equals(pullCount, 1, 'pull should be called exactly once');
384   });
385
386 }, 'ReadableStream: should only call pull once on a non-empty stream read from after start fulfills');
387
388 promise_test(() => {
389
390   let pullCount = 0;
391   let controller;
392   const startPromise = Promise.resolve();
393
394   const rs = new ReadableStream({
395     start(c) {
396       controller = c;
397       return startPromise;
398     },
399     pull() {
400       ++pullCount;
401     }
402   });
403
404   const reader = rs.getReader();
405   return startPromise.then(() => {
406     assert_equals(pullCount, 1, 'pull should have been called once by the time the stream starts');
407
408     controller.enqueue('a');
409     assert_equals(pullCount, 1, 'pull should not have been called again after enqueue');
410
411     return reader.read();
412   }).then(() => {
413     assert_equals(pullCount, 2, 'pull should have been called again after read');
414
415     return delay(10);
416   }).then(() => {
417     assert_equals(pullCount, 2, 'pull should be called exactly twice');
418   });
419 }, 'ReadableStream: should call pull in reaction to read()ing the last chunk, if not draining');
420
421 promise_test(() => {
422
423   let pullCount = 0;
424   let controller;
425   const startPromise = Promise.resolve();
426
427   const rs = new ReadableStream({
428     start(c) {
429       controller = c;
430       return startPromise;
431     },
432     pull() {
433       ++pullCount;
434     }
435   });
436
437   const reader = rs.getReader();
438
439   return startPromise.then(() => {
440     assert_equals(pullCount, 1, 'pull should have been called once by the time the stream starts');
441
442     controller.enqueue('a');
443     assert_equals(pullCount, 1, 'pull should not have been called again after enqueue');
444
445     controller.close();
446
447     return reader.read();
448   }).then(() => {
449     assert_equals(pullCount, 1, 'pull should not have been called a second time after read');
450
451     return delay(10);
452   }).then(() => {
453     assert_equals(pullCount, 1, 'pull should be called exactly once');
454   });
455
456 }, 'ReadableStream: should not call pull() in reaction to read()ing the last chunk, if draining');
457
458 promise_test(() => {
459
460   let resolve;
461   let returnedPromise;
462   let timesCalled = 0;
463   const startPromise = Promise.resolve();
464
465   const rs = new ReadableStream({
466     start() {
467       return startPromise;
468     },
469     pull(c) {
470       c.enqueue(++timesCalled);
471       returnedPromise = new Promise(r => resolve = r);
472       return returnedPromise;
473     }
474   });
475   const reader = rs.getReader();
476
477   return startPromise.then(() => {
478     return reader.read();
479   }).then(result1 => {
480     assert_equals(timesCalled, 1,
481       'pull should have been called once after start, but not yet have been called a second time');
482     assert_object_equals(result1, { value: 1, done: false }, 'read() should fulfill with the enqueued value');
483
484     return delay(10);
485   }).then(() => {
486     assert_equals(timesCalled, 1, 'after 10 ms, pull should still only have been called once');
487
488     resolve();
489     return returnedPromise;
490   }).then(() => {
491     assert_equals(timesCalled, 2,
492       'after the promise returned by pull is fulfilled, pull should be called a second time');
493   });
494
495 }, 'ReadableStream: should not call pull until the previous pull call\'s promise fulfills');
496
497 promise_test(() => {
498
499   let timesCalled = 0;
500   const startPromise = Promise.resolve();
501
502   const rs = new ReadableStream(
503     {
504       start(c) {
505         c.enqueue('a');
506         c.enqueue('b');
507         c.enqueue('c');
508         return startPromise;
509       },
510       pull() {
511         ++timesCalled;
512       }
513     },
514     {
515       size() {
516         return 1;
517       },
518       highWaterMark: Infinity
519     }
520   );
521   const reader = rs.getReader();
522
523   return startPromise.then(() => {
524     return reader.read();
525   }).then(result1 => {
526     assert_object_equals(result1, { value: 'a', done: false }, 'first chunk should be as expected');
527
528     return reader.read();
529   }).then(result2 => {
530     assert_object_equals(result2, { value: 'b', done: false }, 'second chunk should be as expected');
531
532     return reader.read();
533   }).then(result3 => {
534     assert_object_equals(result3, { value: 'c', done: false }, 'third chunk should be as expected');
535
536     return delay(10);
537   }).then(() => {
538     // Once for after start, and once for every read.
539     assert_equals(timesCalled, 4, 'pull() should be called exactly four times');
540   });
541
542 }, 'ReadableStream: should pull after start, and after every read');
543
544 promise_test(() => {
545
546   let timesCalled = 0;
547   const startPromise = Promise.resolve();
548
549   const rs = new ReadableStream({
550     start(c) {
551       c.enqueue('a');
552       c.close();
553       return startPromise;
554     },
555     pull() {
556       ++timesCalled;
557     }
558   });
559
560   const reader = rs.getReader();
561   return startPromise.then(() => {
562     assert_equals(timesCalled, 0, 'after start finishes, pull should not have been called');
563
564     return reader.read();
565   }).then(() => {
566     assert_equals(timesCalled, 0, 'reading should not have triggered a pull call');
567
568     return reader.closed;
569   }).then(() => {
570     assert_equals(timesCalled, 0, 'stream should have closed with still no calls to pull');
571   });
572
573 }, 'ReadableStream: should not call pull after start if the stream is now closed');
574
575 promise_test(() => {
576
577   let timesCalled = 0;
578   let resolve;
579   const ready = new Promise(r => resolve = r);
580
581   new ReadableStream(
582     {
583       start() {},
584       pull(c) {
585         c.enqueue(++timesCalled);
586
587         if (timesCalled === 4) {
588           resolve();
589         }
590       }
591     },
592     {
593       size() {
594         return 1;
595       },
596       highWaterMark: 4
597     }
598   );
599
600   return ready.then(() => {
601     // after start: size = 0, pull()
602     // after enqueue(1): size = 1, pull()
603     // after enqueue(2): size = 2, pull()
604     // after enqueue(3): size = 3, pull()
605     // after enqueue(4): size = 4, do not pull
606     assert_equals(timesCalled, 4, 'pull() should have been called four times');
607   });
608
609 }, 'ReadableStream: should call pull after enqueueing from inside pull (with no read requests), if strategy allows');
610
611 promise_test(() => {
612
613   let pullCalled = false;
614
615   const rs = new ReadableStream({
616     pull(c) {
617       pullCalled = true;
618       c.close();
619     }
620   });
621
622   const reader = rs.getReader();
623   return reader.closed.then(() => {
624     assert_true(pullCalled);
625   });
626
627 }, 'ReadableStream pull should be able to close a stream.');
628
629 promise_test(t => {
630
631   const controllerError = { name: 'controller error' };
632
633   const rs = new ReadableStream({
634     pull(c) {
635       c.error(controllerError);
636     }
637   });
638
639   return promise_rejects(t, controllerError, rs.getReader().closed);
640
641 }, 'ReadableStream pull should be able to error a stream.');
642
643 promise_test(t => {
644
645   const controllerError = { name: 'controller error' };
646   const thrownError = { name: 'thrown error' };
647
648   const rs = new ReadableStream({
649     pull(c) {
650       c.error(controllerError);
651       throw thrownError;
652     }
653   });
654
655   return promise_rejects(t, controllerError, rs.getReader().closed);
656
657 }, 'ReadableStream pull should be able to error a stream and throw.');
658
659 test(() => {
660
661   let startCalled = false;
662
663   new ReadableStream({
664     start(c) {
665       assert_equals(c.enqueue('a'), undefined, 'the first enqueue should return undefined');
666       c.close();
667
668       assert_throws(new TypeError(), () => c.enqueue('b'), 'enqueue after close should throw a TypeError');
669       startCalled = true;
670     }
671   });
672
673   assert_true(startCalled);
674
675 }, 'ReadableStream: enqueue should throw when the stream is readable but draining');
676
677 test(() => {
678
679   let startCalled = false;
680
681   new ReadableStream({
682     start(c) {
683       c.close();
684
685       assert_throws(new TypeError(), () => c.enqueue('a'), 'enqueue after close should throw a TypeError');
686       startCalled = true;
687     }
688   });
689
690   assert_true(startCalled);
691
692 }, 'ReadableStream: enqueue should throw when the stream is closed');
693
694 promise_test(() => {
695
696   let startCalled = 0;
697   let pullCalled = 0;
698   let cancelCalled = 0;
699
700   /* eslint-disable no-use-before-define */
701   class Source {
702     start(c) {
703       startCalled++;
704       assert_equals(this, theSource, 'start() should be called with the correct this');
705       c.enqueue('a');
706     }
707
708     pull() {
709       pullCalled++;
710       assert_equals(this, theSource, 'pull() should be called with the correct this');
711     }
712
713     cancel() {
714       cancelCalled++;
715       assert_equals(this, theSource, 'cancel() should be called with the correct this');
716     }
717   }
718   /* eslint-enable no-use-before-define */
719
720   const theSource = new Source();
721   theSource.debugName = 'the source object passed to the constructor'; // makes test failures easier to diagnose
722
723   const rs = new ReadableStream(theSource);
724   const reader = rs.getReader();
725
726   return reader.read().then(() => {
727     reader.releaseLock();
728     rs.cancel();
729     assert_equals(startCalled, 1);
730     assert_equals(pullCalled, 1);
731     assert_equals(cancelCalled, 1);
732     return rs.getReader().closed;
733   });
734
735 }, 'ReadableStream: should call underlying source methods as methods');
736
737 test(() => {
738   new ReadableStream({
739     start(c) {
740       assert_equals(c.desiredSize, 10, 'desiredSize must start at highWaterMark');
741       c.close();
742       assert_equals(c.desiredSize, 0, 'after closing, desiredSize must be 0');
743     }
744   }, {
745     highWaterMark: 10
746   });
747 }, 'ReadableStream: desiredSize when closed');
748
749 test(() => {
750   new ReadableStream({
751     start(c) {
752       assert_equals(c.desiredSize, 10, 'desiredSize must start at highWaterMark');
753       c.error();
754       assert_equals(c.desiredSize, null, 'after erroring, desiredSize must be null');
755     }
756   }, {
757     highWaterMark: 10
758   });
759 }, 'ReadableStream: desiredSize when errored');
760
761 test(() => {
762
763   let startCalled = false;
764   new ReadableStream({
765     start(c) {
766       assert_equals(c.desiredSize, 1);
767       c.enqueue('a');
768       assert_equals(c.desiredSize, 0);
769       c.enqueue('b');
770       assert_equals(c.desiredSize, -1);
771       c.enqueue('c');
772       assert_equals(c.desiredSize, -2);
773       c.enqueue('d');
774       assert_equals(c.desiredSize, -3);
775       c.enqueue('e');
776       startCalled = true;
777     }
778   });
779
780   assert_true(startCalled);
781
782 }, 'ReadableStream strategies: the default strategy should give desiredSize of 1 to start, decreasing by 1 per enqueue');
783
784 promise_test(() => {
785
786   let controller;
787   const rs = new ReadableStream({
788     start(c) {
789       controller = c;
790     }
791   });
792   const reader = rs.getReader();
793
794   assert_equals(controller.desiredSize, 1, 'desiredSize should start at 1');
795   controller.enqueue('a');
796   assert_equals(controller.desiredSize, 0, 'desiredSize should decrease to 0 after first enqueue');
797
798   return reader.read().then(result1 => {
799     assert_object_equals(result1, { value: 'a', done: false }, 'first chunk read should be correct');
800
801     assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 after the first read');
802     controller.enqueue('b');
803     assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 after the second enqueue');
804
805     return reader.read();
806   }).then(result2 => {
807     assert_object_equals(result2, { value: 'b', done: false }, 'second chunk read should be correct');
808
809     assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 after the second read');
810     controller.enqueue('c');
811     assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 after the third enqueue');
812
813     return reader.read();
814   }).then(result3 => {
815     assert_object_equals(result3, { value: 'c', done: false }, 'third chunk read should be correct');
816
817     assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 after the third read');
818     controller.enqueue('d');
819     assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 after the fourth enqueue');
820   });
821
822 }, 'ReadableStream strategies: the default strategy should continue giving desiredSize of 1 if the chunks are read immediately');
823
824 promise_test(t => {
825
826   const randomSource = new RandomPushSource(8);
827
828   const rs = new ReadableStream({
829     start(c) {
830       assert_equals(typeof c, 'object', 'c should be an object in start');
831       assert_equals(typeof c.enqueue, 'function', 'enqueue should be a function in start');
832       assert_equals(typeof c.close, 'function', 'close should be a function in start');
833       assert_equals(typeof c.error, 'function', 'error should be a function in start');
834
835       randomSource.ondata = t.step_func(chunk => {
836         if (!c.enqueue(chunk) <= 0) {
837           randomSource.readStop();
838         }
839       });
840
841       randomSource.onend = c.close.bind(c);
842       randomSource.onerror = c.error.bind(c);
843     },
844
845     pull(c) {
846       assert_equals(typeof c, 'object', 'c should be an object in pull');
847       assert_equals(typeof c.enqueue, 'function', 'enqueue should be a function in pull');
848       assert_equals(typeof c.close, 'function', 'close should be a function in pull');
849
850       randomSource.readStart();
851     }
852   });
853
854   return readableStreamToArray(rs).then(chunks => {
855     assert_equals(chunks.length, 8, '8 chunks should be read');
856     for (const chunk of chunks) {
857       assert_equals(chunk.length, 128, 'chunk should have 128 bytes');
858     }
859   });
860
861 }, 'ReadableStream integration test: adapting a random push source');
862
863 promise_test(() => {
864
865   const rs = sequentialReadableStream(10);
866
867   return readableStreamToArray(rs).then(chunks => {
868     assert_true(rs.source.closed, 'source should be closed after all chunks are read');
869     assert_array_equals(chunks, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'the expected 10 chunks should be read');
870   });
871
872 }, 'ReadableStream integration test: adapting a sync pull source');
873
874 promise_test(() => {
875
876   const rs = sequentialReadableStream(10, { async: true });
877
878   return readableStreamToArray(rs).then(chunks => {
879     assert_true(rs.source.closed, 'source should be closed after all chunks are read');
880     assert_array_equals(chunks, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'the expected 10 chunks should be read');
881   });
882
883 }, 'ReadableStream integration test: adapting an async pull source');
884
885 done();