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