1// META: global=window,worker 2// META: script=../resources/rs-utils.js 3// META: script=../resources/test-utils.js 4// META: script=../resources/recording-streams.js 5// META: script=../resources/rs-test-templates.js 6'use strict'; 7 8test(() => { 9 10 const rs = new ReadableStream({ type: 'bytes' }); 11 const result = rs.tee(); 12 13 assert_true(Array.isArray(result), 'return value should be an array'); 14 assert_equals(result.length, 2, 'array should have length 2'); 15 assert_equals(result[0].constructor, ReadableStream, '0th element should be a ReadableStream'); 16 assert_equals(result[1].constructor, ReadableStream, '1st element should be a ReadableStream'); 17 18}, 'ReadableStream teeing with byte source: rs.tee() returns an array of two ReadableStreams'); 19 20promise_test(async t => { 21 22 const rs = new ReadableStream({ 23 type: 'bytes', 24 start(c) { 25 c.enqueue(new Uint8Array([0x01])); 26 c.enqueue(new Uint8Array([0x02])); 27 c.close(); 28 } 29 }); 30 31 const [branch1, branch2] = rs.tee(); 32 const reader1 = branch1.getReader({ mode: 'byob' }); 33 const reader2 = branch2.getReader({ mode: 'byob' }); 34 35 reader2.closed.then(t.unreached_func('branch2 should not be closed')); 36 37 { 38 const result = await reader1.read(new Uint8Array(1)); 39 assert_equals(result.done, false, 'done'); 40 assert_typed_array_equals(result.value, new Uint8Array([0x01]), 'value'); 41 } 42 43 { 44 const result = await reader1.read(new Uint8Array(1)); 45 assert_equals(result.done, false, 'done'); 46 assert_typed_array_equals(result.value, new Uint8Array([0x02]), 'value'); 47 } 48 49 { 50 const result = await reader1.read(new Uint8Array(1)); 51 assert_equals(result.done, true, 'done'); 52 assert_typed_array_equals(result.value, new Uint8Array([0]).subarray(0, 0), 'value'); 53 } 54 55 { 56 const result = await reader2.read(new Uint8Array(1)); 57 assert_equals(result.done, false, 'done'); 58 assert_typed_array_equals(result.value, new Uint8Array([0x01]), 'value'); 59 } 60 61 await reader1.closed; 62 63}, 'ReadableStream teeing with byte source: should be able to read one branch to the end without affecting the other'); 64 65promise_test(async () => { 66 67 let pullCount = 0; 68 const enqueuedChunk = new Uint8Array([0x01]); 69 const rs = new ReadableStream({ 70 type: 'bytes', 71 pull(c) { 72 ++pullCount; 73 if (pullCount === 1) { 74 c.enqueue(enqueuedChunk); 75 } 76 } 77 }); 78 79 const [branch1, branch2] = rs.tee(); 80 const reader1 = branch1.getReader(); 81 const reader2 = branch2.getReader(); 82 83 const [result1, result2] = await Promise.all([reader1.read(), reader2.read()]); 84 assert_equals(result1.done, false, 'reader1 done'); 85 assert_equals(result2.done, false, 'reader2 done'); 86 87 const view1 = result1.value; 88 const view2 = result2.value; 89 assert_typed_array_equals(view1, new Uint8Array([0x01]), 'reader1 value'); 90 assert_typed_array_equals(view2, new Uint8Array([0x01]), 'reader2 value'); 91 92 assert_not_equals(view1.buffer, view2.buffer, 'chunks should have different buffers'); 93 assert_not_equals(enqueuedChunk.buffer, view1.buffer, 'enqueued chunk and branch1\'s chunk should have different buffers'); 94 assert_not_equals(enqueuedChunk.buffer, view2.buffer, 'enqueued chunk and branch2\'s chunk should have different buffers'); 95 96}, 'ReadableStream teeing with byte source: chunks should be cloned for each branch'); 97 98promise_test(async () => { 99 100 let pullCount = 0; 101 const rs = new ReadableStream({ 102 type: 'bytes', 103 pull(c) { 104 ++pullCount; 105 if (pullCount === 1) { 106 c.byobRequest.view[0] = 0x01; 107 c.byobRequest.respond(1); 108 } 109 } 110 }); 111 112 const [branch1, branch2] = rs.tee(); 113 const reader1 = branch1.getReader({ mode: 'byob' }); 114 const reader2 = branch2.getReader(); 115 const buffer = new Uint8Array([42, 42, 42]).buffer; 116 117 { 118 const result = await reader1.read(new Uint8Array(buffer, 0, 1)); 119 assert_equals(result.done, false, 'done'); 120 assert_typed_array_equals(result.value, new Uint8Array([0x01, 42, 42]).subarray(0, 1), 'value'); 121 } 122 123 { 124 const result = await reader2.read(); 125 assert_equals(result.done, false, 'done'); 126 assert_typed_array_equals(result.value, new Uint8Array([0x01]), 'value'); 127 } 128 129}, 'ReadableStream teeing with byte source: chunks for BYOB requests from branch 1 should be cloned to branch 2'); 130 131promise_test(async t => { 132 133 const theError = { name: 'boo!' }; 134 const rs = new ReadableStream({ 135 type: 'bytes', 136 start(c) { 137 c.enqueue(new Uint8Array([0x01])); 138 c.enqueue(new Uint8Array([0x02])); 139 }, 140 pull() { 141 throw theError; 142 } 143 }); 144 145 const [branch1, branch2] = rs.tee(); 146 const reader1 = branch1.getReader({ mode: 'byob' }); 147 const reader2 = branch2.getReader({ mode: 'byob' }); 148 149 { 150 const result = await reader1.read(new Uint8Array(1)); 151 assert_equals(result.done, false, 'first read from branch1 should not be done'); 152 assert_typed_array_equals(result.value, new Uint8Array([0x01]), 'first read from branch1'); 153 } 154 155 { 156 const result = await reader1.read(new Uint8Array(1)); 157 assert_equals(result.done, false, 'second read from branch1 should not be done'); 158 assert_typed_array_equals(result.value, new Uint8Array([0x02]), 'second read from branch1'); 159 } 160 161 await promise_rejects_exactly(t, theError, reader1.read(new Uint8Array(1))); 162 await promise_rejects_exactly(t, theError, reader2.read(new Uint8Array(1))); 163 164 await Promise.all([ 165 promise_rejects_exactly(t, theError, reader1.closed), 166 promise_rejects_exactly(t, theError, reader2.closed) 167 ]); 168 169}, 'ReadableStream teeing with byte source: errors in the source should propagate to both branches'); 170 171promise_test(async () => { 172 173 const rs = new ReadableStream({ 174 type: 'bytes', 175 start(c) { 176 c.enqueue(new Uint8Array([0x01])); 177 c.enqueue(new Uint8Array([0x02])); 178 c.close(); 179 } 180 }); 181 182 const [branch1, branch2] = rs.tee(); 183 branch1.cancel(); 184 185 const [chunks1, chunks2] = await Promise.all([readableStreamToArray(branch1), readableStreamToArray(branch2)]); 186 assert_array_equals(chunks1, [], 'branch1 should have no chunks'); 187 assert_equals(chunks2.length, 2, 'branch2 should have two chunks'); 188 assert_typed_array_equals(chunks2[0], new Uint8Array([0x01]), 'first chunk from branch2'); 189 assert_typed_array_equals(chunks2[1], new Uint8Array([0x02]), 'second chunk from branch2'); 190 191}, 'ReadableStream teeing with byte source: canceling branch1 should not impact branch2'); 192 193promise_test(async () => { 194 195 const rs = new ReadableStream({ 196 type: 'bytes', 197 start(c) { 198 c.enqueue(new Uint8Array([0x01])); 199 c.enqueue(new Uint8Array([0x02])); 200 c.close(); 201 } 202 }); 203 204 const [branch1, branch2] = rs.tee(); 205 branch2.cancel(); 206 207 const [chunks1, chunks2] = await Promise.all([readableStreamToArray(branch1), readableStreamToArray(branch2)]); 208 assert_equals(chunks1.length, 2, 'branch1 should have two chunks'); 209 assert_typed_array_equals(chunks1[0], new Uint8Array([0x01]), 'first chunk from branch1'); 210 assert_typed_array_equals(chunks1[1], new Uint8Array([0x02]), 'second chunk from branch1'); 211 assert_array_equals(chunks2, [], 'branch2 should have no chunks'); 212 213}, 'ReadableStream teeing with byte source: canceling branch2 should not impact branch1'); 214 215templatedRSTeeCancel('ReadableStream teeing with byte source', (extras) => { 216 return new ReadableStream({ type: 'bytes', ...extras }); 217}); 218 219promise_test(async () => { 220 221 let controller; 222 const rs = new ReadableStream({ 223 type: 'bytes', 224 start(c) { 225 controller = c; 226 } 227 }); 228 229 const [branch1, branch2] = rs.tee(); 230 const reader1 = branch1.getReader({ mode: 'byob' }); 231 const reader2 = branch2.getReader({ mode: 'byob' }); 232 233 const promise = Promise.all([reader1.closed, reader2.closed]); 234 235 controller.close(); 236 237 // The branches are created with HWM 0, so we need to read from at least one of them 238 // to observe the stream becoming closed. 239 const read1 = await reader1.read(new Uint8Array(1)); 240 assert_equals(read1.done, true, 'first read from branch1 should be done'); 241 242 await promise; 243 244}, 'ReadableStream teeing with byte source: closing the original should close the branches'); 245 246promise_test(async t => { 247 248 let controller; 249 const rs = new ReadableStream({ 250 type: 'bytes', 251 start(c) { 252 controller = c; 253 } 254 }); 255 256 const [branch1, branch2] = rs.tee(); 257 const reader1 = branch1.getReader({ mode: 'byob' }); 258 const reader2 = branch2.getReader({ mode: 'byob' }); 259 260 const theError = { name: 'boo!' }; 261 const promise = Promise.all([ 262 promise_rejects_exactly(t, theError, reader1.closed), 263 promise_rejects_exactly(t, theError, reader2.closed) 264 ]); 265 266 controller.error(theError); 267 await promise; 268 269}, 'ReadableStream teeing with byte source: erroring the original should immediately error the branches'); 270 271promise_test(async t => { 272 273 let controller; 274 const rs = new ReadableStream({ 275 type: 'bytes', 276 start(c) { 277 controller = c; 278 } 279 }); 280 281 const [branch1, branch2] = rs.tee(); 282 const reader1 = branch1.getReader(); 283 const reader2 = branch2.getReader(); 284 285 const theError = { name: 'boo!' }; 286 const promise = Promise.all([ 287 promise_rejects_exactly(t, theError, reader1.read()), 288 promise_rejects_exactly(t, theError, reader2.read()) 289 ]); 290 291 controller.error(theError); 292 await promise; 293 294}, 'ReadableStream teeing with byte source: erroring the original should error pending reads from default reader'); 295 296promise_test(async t => { 297 298 let controller; 299 const rs = new ReadableStream({ 300 type: 'bytes', 301 start(c) { 302 controller = c; 303 } 304 }); 305 306 const [branch1, branch2] = rs.tee(); 307 const reader1 = branch1.getReader({ mode: 'byob' }); 308 const reader2 = branch2.getReader({ mode: 'byob' }); 309 310 const theError = { name: 'boo!' }; 311 const promise = Promise.all([ 312 promise_rejects_exactly(t, theError, reader1.read(new Uint8Array(1))), 313 promise_rejects_exactly(t, theError, reader2.read(new Uint8Array(1))) 314 ]); 315 316 controller.error(theError); 317 await promise; 318 319}, 'ReadableStream teeing with byte source: erroring the original should error pending reads from BYOB reader'); 320 321promise_test(async () => { 322 323 let controller; 324 const rs = new ReadableStream({ 325 type: 'bytes', 326 start(c) { 327 controller = c; 328 } 329 }); 330 331 const [branch1, branch2] = rs.tee(); 332 const reader1 = branch1.getReader({ mode: 'byob' }); 333 const reader2 = branch2.getReader({ mode: 'byob' }); 334 const cancelPromise = reader2.cancel(); 335 336 controller.enqueue(new Uint8Array([0x01])); 337 338 const read1 = await reader1.read(new Uint8Array(1)); 339 assert_equals(read1.done, false, 'first read() from branch1 should not be done'); 340 assert_typed_array_equals(read1.value, new Uint8Array([0x01]), 'first read() from branch1'); 341 342 controller.close(); 343 344 const read2 = await reader1.read(new Uint8Array(1)); 345 assert_equals(read2.done, true, 'second read() from branch1 should be done'); 346 347 await Promise.all([ 348 reader1.closed, 349 cancelPromise 350 ]); 351 352}, 'ReadableStream teeing with byte source: canceling branch1 should finish when branch2 reads until end of stream'); 353 354promise_test(async t => { 355 356 let controller; 357 const theError = { name: 'boo!' }; 358 const rs = new ReadableStream({ 359 type: 'bytes', 360 start(c) { 361 controller = c; 362 } 363 }); 364 365 const [branch1, branch2] = rs.tee(); 366 const reader1 = branch1.getReader({ mode: 'byob' }); 367 const reader2 = branch2.getReader({ mode: 'byob' }); 368 const cancelPromise = reader2.cancel(); 369 370 controller.error(theError); 371 372 await Promise.all([ 373 promise_rejects_exactly(t, theError, reader1.read(new Uint8Array(1))), 374 cancelPromise 375 ]); 376 377}, 'ReadableStream teeing with byte source: canceling branch1 should finish when original stream errors'); 378 379promise_test(async () => { 380 381 const rs = recordingReadableStream({ type: 'bytes' }); 382 383 // Create two branches, each with a HWM of 0. This should result in no chunks being pulled. 384 rs.tee(); 385 386 await flushAsyncEvents(); 387 assert_array_equals(rs.events, [], 'pull should not be called'); 388 389}, 'ReadableStream teeing with byte source: should not pull any chunks if no branches are reading'); 390 391promise_test(async () => { 392 393 const rs = recordingReadableStream({ 394 type: 'bytes', 395 pull(controller) { 396 controller.enqueue(new Uint8Array([0x01])); 397 } 398 }); 399 400 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 401 await Promise.all([ 402 reader1.read(new Uint8Array(1)), 403 reader2.read(new Uint8Array(1)) 404 ]); 405 assert_array_equals(rs.events, ['pull'], 'pull should be called once'); 406 407}, 'ReadableStream teeing with byte source: should only pull enough to fill the emptiest queue'); 408 409promise_test(async t => { 410 411 const rs = recordingReadableStream({ type: 'bytes' }); 412 const theError = { name: 'boo!' }; 413 414 rs.controller.error(theError); 415 416 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 417 418 await flushAsyncEvents(); 419 assert_array_equals(rs.events, [], 'pull should not be called'); 420 421 await Promise.all([ 422 promise_rejects_exactly(t, theError, reader1.closed), 423 promise_rejects_exactly(t, theError, reader2.closed) 424 ]); 425 426}, 'ReadableStream teeing with byte source: should not pull when original is already errored'); 427 428for (const branch of [1, 2]) { 429 promise_test(async t => { 430 431 const rs = recordingReadableStream({ type: 'bytes' }); 432 const theError = { name: 'boo!' }; 433 434 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 435 436 await flushAsyncEvents(); 437 assert_array_equals(rs.events, [], 'pull should not be called'); 438 439 const reader = (branch === 1) ? reader1 : reader2; 440 const read1 = reader.read(new Uint8Array(1)); 441 442 await flushAsyncEvents(); 443 assert_array_equals(rs.events, ['pull'], 'pull should be called once'); 444 445 rs.controller.error(theError); 446 447 await Promise.all([ 448 promise_rejects_exactly(t, theError, read1), 449 promise_rejects_exactly(t, theError, reader1.closed), 450 promise_rejects_exactly(t, theError, reader2.closed) 451 ]); 452 453 await flushAsyncEvents(); 454 assert_array_equals(rs.events, ['pull'], 'pull should be called once'); 455 456 }, `ReadableStream teeing with byte source: stops pulling when original stream errors while branch ${branch} is reading`); 457} 458 459promise_test(async t => { 460 461 const rs = recordingReadableStream({ type: 'bytes' }); 462 const theError = { name: 'boo!' }; 463 464 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 465 466 await flushAsyncEvents(); 467 assert_array_equals(rs.events, [], 'pull should not be called'); 468 469 const read1 = reader1.read(new Uint8Array(1)); 470 const read2 = reader2.read(new Uint8Array(1)); 471 472 await flushAsyncEvents(); 473 assert_array_equals(rs.events, ['pull'], 'pull should be called once'); 474 475 rs.controller.error(theError); 476 477 await Promise.all([ 478 promise_rejects_exactly(t, theError, read1), 479 promise_rejects_exactly(t, theError, read2), 480 promise_rejects_exactly(t, theError, reader1.closed), 481 promise_rejects_exactly(t, theError, reader2.closed) 482 ]); 483 484 await flushAsyncEvents(); 485 assert_array_equals(rs.events, ['pull'], 'pull should be called once'); 486 487}, 'ReadableStream teeing with byte source: stops pulling when original stream errors while both branches are reading'); 488 489promise_test(async () => { 490 491 const rs = recordingReadableStream({ type: 'bytes' }); 492 493 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 494 495 const read1 = reader1.read(new Uint8Array([0x11])); 496 const read2 = reader2.read(new Uint8Array([0x22])); 497 498 const cancel1 = reader1.cancel(); 499 await flushAsyncEvents(); 500 const cancel2 = reader2.cancel(); 501 502 const result1 = await read1; 503 assert_object_equals(result1, { value: undefined, done: true }); 504 const result2 = await read2; 505 assert_object_equals(result2, { value: undefined, done: true }); 506 507 await Promise.all([cancel1, cancel2]); 508 509}, 'ReadableStream teeing with byte source: canceling both branches in sequence with delay'); 510 511promise_test(async t => { 512 513 const theError = { name: 'boo!' }; 514 const rs = new ReadableStream({ 515 type: 'bytes', 516 cancel() { 517 throw theError; 518 } 519 }); 520 521 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 522 523 const read1 = reader1.read(new Uint8Array([0x11])); 524 const read2 = reader2.read(new Uint8Array([0x22])); 525 526 const cancel1 = reader1.cancel(); 527 await flushAsyncEvents(); 528 const cancel2 = reader2.cancel(); 529 530 const result1 = await read1; 531 assert_object_equals(result1, { value: undefined, done: true }); 532 const result2 = await read2; 533 assert_object_equals(result2, { value: undefined, done: true }); 534 535 await Promise.all([ 536 promise_rejects_exactly(t, theError, cancel1), 537 promise_rejects_exactly(t, theError, cancel2) 538 ]); 539 540}, 'ReadableStream teeing with byte source: failing to cancel when canceling both branches in sequence with delay'); 541 542promise_test(async () => { 543 544 let cancelResolve; 545 const cancelCalled = new Promise((resolve) => { 546 cancelResolve = resolve; 547 }); 548 const rs = recordingReadableStream({ 549 type: 'bytes', 550 cancel() { 551 cancelResolve(); 552 } 553 }); 554 555 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 556 557 const read1 = reader1.read(new Uint8Array([0x11])); 558 await flushAsyncEvents(); 559 const read2 = reader2.read(new Uint8Array([0x22])); 560 await flushAsyncEvents(); 561 562 // We are reading into branch1's buffer. 563 const byobRequest1 = rs.controller.byobRequest; 564 assert_not_equals(byobRequest1, null); 565 assert_typed_array_equals(byobRequest1.view, new Uint8Array([0x11]), 'byobRequest1.view'); 566 567 // Cancelling branch1 should not affect the BYOB request. 568 const cancel1 = reader1.cancel(); 569 const result1 = await read1; 570 assert_equals(result1.done, true); 571 assert_equals(result1.value, undefined); 572 await flushAsyncEvents(); 573 const byobRequest2 = rs.controller.byobRequest; 574 assert_typed_array_equals(byobRequest2.view, new Uint8Array([0x11]), 'byobRequest2.view'); 575 576 // Cancelling branch1 should invalidate the BYOB request. 577 const cancel2 = reader2.cancel(); 578 await cancelCalled; 579 const byobRequest3 = rs.controller.byobRequest; 580 assert_equals(byobRequest3, null); 581 const result2 = await read2; 582 assert_equals(result2.done, true); 583 assert_equals(result2.value, undefined); 584 585 await Promise.all([cancel1, cancel2]); 586 587}, 'ReadableStream teeing with byte source: read from branch1 and branch2, cancel branch1, cancel branch2'); 588 589promise_test(async () => { 590 591 let cancelResolve; 592 const cancelCalled = new Promise((resolve) => { 593 cancelResolve = resolve; 594 }); 595 const rs = recordingReadableStream({ 596 type: 'bytes', 597 cancel() { 598 cancelResolve(); 599 } 600 }); 601 602 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 603 604 const read1 = reader1.read(new Uint8Array([0x11])); 605 await flushAsyncEvents(); 606 const read2 = reader2.read(new Uint8Array([0x22])); 607 await flushAsyncEvents(); 608 609 // We are reading into branch1's buffer. 610 const byobRequest1 = rs.controller.byobRequest; 611 assert_not_equals(byobRequest1, null); 612 assert_typed_array_equals(byobRequest1.view, new Uint8Array([0x11]), 'byobRequest1.view'); 613 614 // Cancelling branch2 should not affect the BYOB request. 615 const cancel2 = reader2.cancel(); 616 const result2 = await read2; 617 assert_equals(result2.done, true); 618 assert_equals(result2.value, undefined); 619 await flushAsyncEvents(); 620 const byobRequest2 = rs.controller.byobRequest; 621 assert_typed_array_equals(byobRequest2.view, new Uint8Array([0x11]), 'byobRequest2.view'); 622 623 // Cancelling branch1 should invalidate the BYOB request. 624 const cancel1 = reader1.cancel(); 625 await cancelCalled; 626 const byobRequest3 = rs.controller.byobRequest; 627 assert_equals(byobRequest3, null); 628 const result1 = await read1; 629 assert_equals(result1.done, true); 630 assert_equals(result1.value, undefined); 631 632 await Promise.all([cancel1, cancel2]); 633 634}, 'ReadableStream teeing with byte source: read from branch1 and branch2, cancel branch2, cancel branch1'); 635 636promise_test(async () => { 637 638 const rs = recordingReadableStream({ type: 'bytes' }); 639 640 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 641 642 const read1 = reader1.read(new Uint8Array([0x11])); 643 await flushAsyncEvents(); 644 const read2 = reader2.read(new Uint8Array([0x22])); 645 await flushAsyncEvents(); 646 647 // We are reading into branch1's buffer. 648 assert_typed_array_equals(rs.controller.byobRequest.view, new Uint8Array([0x11]), 'first byobRequest.view'); 649 650 // Cancelling branch2 should not affect the BYOB request. 651 reader2.cancel(); 652 const result2 = await read2; 653 assert_equals(result2.done, true); 654 assert_equals(result2.value, undefined); 655 await flushAsyncEvents(); 656 assert_typed_array_equals(rs.controller.byobRequest.view, new Uint8Array([0x11]), 'second byobRequest.view'); 657 658 // Respond to the BYOB request. 659 rs.controller.byobRequest.view[0] = 0x33; 660 rs.controller.byobRequest.respond(1); 661 662 // branch1 should receive the read chunk. 663 const result1 = await read1; 664 assert_equals(result1.done, false); 665 assert_typed_array_equals(result1.value, new Uint8Array([0x33]), 'first read() from branch1'); 666 667}, 'ReadableStream teeing with byte source: read from branch1 and branch2, cancel branch2, enqueue to branch1'); 668 669promise_test(async () => { 670 671 const rs = recordingReadableStream({ type: 'bytes' }); 672 673 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 674 675 const read1 = reader1.read(new Uint8Array([0x11])); 676 await flushAsyncEvents(); 677 const read2 = reader2.read(new Uint8Array([0x22])); 678 await flushAsyncEvents(); 679 680 // We are reading into branch1's buffer. 681 assert_typed_array_equals(rs.controller.byobRequest.view, new Uint8Array([0x11]), 'first byobRequest.view'); 682 683 // Cancelling branch1 should not affect the BYOB request. 684 reader1.cancel(); 685 const result1 = await read1; 686 assert_equals(result1.done, true); 687 assert_equals(result1.value, undefined); 688 await flushAsyncEvents(); 689 assert_typed_array_equals(rs.controller.byobRequest.view, new Uint8Array([0x11]), 'second byobRequest.view'); 690 691 // Respond to the BYOB request. 692 rs.controller.byobRequest.view[0] = 0x33; 693 rs.controller.byobRequest.respond(1); 694 695 // branch2 should receive the read chunk. 696 const result2 = await read2; 697 assert_equals(result2.done, false); 698 assert_typed_array_equals(result2.value, new Uint8Array([0x33]), 'first read() from branch2'); 699 700}, 'ReadableStream teeing with byte source: read from branch1 and branch2, cancel branch1, respond to branch2'); 701 702promise_test(async () => { 703 704 let pullCount = 0; 705 const byobRequestDefined = []; 706 const rs = new ReadableStream({ 707 type: 'bytes', 708 pull(c) { 709 ++pullCount; 710 byobRequestDefined.push(c.byobRequest !== null); 711 c.enqueue(new Uint8Array([pullCount])); 712 } 713 }); 714 715 const [branch1, _] = rs.tee(); 716 const reader1 = branch1.getReader({ mode: 'byob' }); 717 718 const result1 = await reader1.read(new Uint8Array([0x11])); 719 assert_equals(result1.done, false, 'first read should not be done'); 720 assert_typed_array_equals(result1.value, new Uint8Array([0x1]), 'first read'); 721 assert_equals(pullCount, 1, 'pull() should be called once'); 722 assert_equals(byobRequestDefined[0], true, 'should have created a BYOB request for first read'); 723 724 reader1.releaseLock(); 725 const reader2 = branch1.getReader(); 726 727 const result2 = await reader2.read(); 728 assert_equals(result2.done, false, 'second read should not be done'); 729 assert_typed_array_equals(result2.value, new Uint8Array([0x2]), 'second read'); 730 assert_equals(pullCount, 2, 'pull() should be called twice'); 731 assert_equals(byobRequestDefined[1], false, 'should not have created a BYOB request for second read'); 732 733}, 'ReadableStream teeing with byte source: pull with BYOB reader, then pull with default reader'); 734 735promise_test(async () => { 736 737 let pullCount = 0; 738 const byobRequestDefined = []; 739 const rs = new ReadableStream({ 740 type: 'bytes', 741 pull(c) { 742 ++pullCount; 743 byobRequestDefined.push(c.byobRequest !== null); 744 c.enqueue(new Uint8Array([pullCount])); 745 } 746 }); 747 748 const [branch1, _] = rs.tee(); 749 const reader1 = branch1.getReader(); 750 751 const result1 = await reader1.read(); 752 assert_equals(result1.done, false, 'first read should not be done'); 753 assert_typed_array_equals(result1.value, new Uint8Array([0x1]), 'first read'); 754 assert_equals(pullCount, 1, 'pull() should be called once'); 755 assert_equals(byobRequestDefined[0], false, 'should not have created a BYOB request for first read'); 756 757 reader1.releaseLock(); 758 const reader2 = branch1.getReader({ mode: 'byob' }); 759 760 const result2 = await reader2.read(new Uint8Array([0x22])); 761 assert_equals(result2.done, false, 'second read should not be done'); 762 assert_typed_array_equals(result2.value, new Uint8Array([0x2]), 'second read'); 763 assert_equals(pullCount, 2, 'pull() should be called twice'); 764 assert_equals(byobRequestDefined[1], true, 'should have created a BYOB request for second read'); 765 766}, 'ReadableStream teeing with byte source: pull with default reader, then pull with BYOB reader'); 767 768promise_test(async () => { 769 770 const rs = recordingReadableStream({ 771 type: 'bytes' 772 }); 773 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 774 775 // Wait for each branch's start() promise to resolve. 776 await flushAsyncEvents(); 777 778 const read2 = reader2.read(new Uint8Array([0x22])); 779 const read1 = reader1.read(new Uint8Array([0x11])); 780 await flushAsyncEvents(); 781 782 // branch2 should provide the BYOB request. 783 const byobRequest = rs.controller.byobRequest; 784 assert_typed_array_equals(byobRequest.view, new Uint8Array([0x22]), 'first BYOB request'); 785 byobRequest.view[0] = 0x01; 786 byobRequest.respond(1); 787 788 const result1 = await read1; 789 assert_equals(result1.done, false, 'first read should not be done'); 790 assert_typed_array_equals(result1.value, new Uint8Array([0x1]), 'first read'); 791 792 const result2 = await read2; 793 assert_equals(result2.done, false, 'second read should not be done'); 794 assert_typed_array_equals(result2.value, new Uint8Array([0x1]), 'second read'); 795 796}, 'ReadableStream teeing with byte source: read from branch2, then read from branch1'); 797 798promise_test(async () => { 799 800 const rs = recordingReadableStream({ type: 'bytes' }); 801 const [branch1, branch2] = rs.tee(); 802 const reader1 = branch1.getReader(); 803 const reader2 = branch2.getReader({ mode: 'byob' }); 804 await flushAsyncEvents(); 805 806 const read1 = reader1.read(); 807 const read2 = reader2.read(new Uint8Array([0x22])); 808 await flushAsyncEvents(); 809 810 // There should be no BYOB request. 811 assert_equals(rs.controller.byobRequest, null, 'first BYOB request'); 812 813 // Close the stream. 814 rs.controller.close(); 815 816 const result1 = await read1; 817 assert_equals(result1.done, true, 'read from branch1 should be done'); 818 assert_equals(result1.value, undefined, 'read from branch1'); 819 820 // branch2 should get its buffer back. 821 const result2 = await read2; 822 assert_equals(result2.done, true, 'read from branch2 should be done'); 823 assert_typed_array_equals(result2.value, new Uint8Array([0x22]).subarray(0, 0), 'read from branch2'); 824 825}, 'ReadableStream teeing with byte source: read from branch1 with default reader, then close while branch2 has pending BYOB read'); 826 827promise_test(async () => { 828 829 const rs = recordingReadableStream({ type: 'bytes' }); 830 const [branch1, branch2] = rs.tee(); 831 const reader1 = branch1.getReader({ mode: 'byob' }); 832 const reader2 = branch2.getReader(); 833 await flushAsyncEvents(); 834 835 const read2 = reader2.read(); 836 const read1 = reader1.read(new Uint8Array([0x11])); 837 await flushAsyncEvents(); 838 839 // There should be no BYOB request. 840 assert_equals(rs.controller.byobRequest, null, 'first BYOB request'); 841 842 // Close the stream. 843 rs.controller.close(); 844 845 const result2 = await read2; 846 assert_equals(result2.done, true, 'read from branch2 should be done'); 847 assert_equals(result2.value, undefined, 'read from branch2'); 848 849 // branch1 should get its buffer back. 850 const result1 = await read1; 851 assert_equals(result1.done, true, 'read from branch1 should be done'); 852 assert_typed_array_equals(result1.value, new Uint8Array([0x11]).subarray(0, 0), 'read from branch1'); 853 854}, 'ReadableStream teeing with byte source: read from branch2 with default reader, then close while branch1 has pending BYOB read'); 855 856promise_test(async () => { 857 858 const rs = recordingReadableStream({ type: 'bytes' }); 859 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 860 await flushAsyncEvents(); 861 862 const read1 = reader1.read(new Uint8Array([0x11])); 863 const read2 = reader2.read(new Uint8Array([0x22])); 864 await flushAsyncEvents(); 865 866 // branch1 should provide the BYOB request. 867 const byobRequest = rs.controller.byobRequest; 868 assert_typed_array_equals(byobRequest.view, new Uint8Array([0x11]), 'first BYOB request'); 869 870 // Close the stream. 871 rs.controller.close(); 872 byobRequest.respond(0); 873 874 // Both branches should get their buffers back. 875 const result1 = await read1; 876 assert_equals(result1.done, true, 'first read should be done'); 877 assert_typed_array_equals(result1.value, new Uint8Array([0x11]).subarray(0, 0), 'first read'); 878 879 const result2 = await read2; 880 assert_equals(result2.done, true, 'second read should be done'); 881 assert_typed_array_equals(result2.value, new Uint8Array([0x22]).subarray(0, 0), 'second read'); 882 883}, 'ReadableStream teeing with byte source: close when both branches have pending BYOB reads'); 884 885promise_test(async () => { 886 887 const rs = recordingReadableStream({ type: 'bytes' }); 888 889 const [reader1, reader2] = rs.tee().map(branch => branch.getReader()); 890 const branch1Reads = [reader1.read(), reader1.read()]; 891 const branch2Reads = [reader2.read(), reader2.read()]; 892 893 await flushAsyncEvents(); 894 rs.controller.enqueue(new Uint8Array([0x11])); 895 rs.controller.close(); 896 897 const result1 = await branch1Reads[0]; 898 assert_equals(result1.done, false, 'first read() from branch1 should be not done'); 899 assert_typed_array_equals(result1.value, new Uint8Array([0x11]), 'first chunk from branch1 should be correct'); 900 const result2 = await branch2Reads[0]; 901 assert_equals(result2.done, false, 'first read() from branch2 should be not done'); 902 assert_typed_array_equals(result2.value, new Uint8Array([0x11]), 'first chunk from branch2 should be correct'); 903 904 assert_object_equals(await branch1Reads[1], { value: undefined, done: true }, 'second read() from branch1 should be done'); 905 assert_object_equals(await branch2Reads[1], { value: undefined, done: true }, 'second read() from branch2 should be done'); 906 907}, 'ReadableStream teeing with byte source: enqueue() and close() while both branches are pulling'); 908 909promise_test(async () => { 910 911 const rs = recordingReadableStream({ type: 'bytes' }); 912 913 const [reader1, reader2] = rs.tee().map(branch => branch.getReader({ mode: 'byob' })); 914 const branch1Reads = [reader1.read(new Uint8Array(1)), reader1.read(new Uint8Array(1))]; 915 const branch2Reads = [reader2.read(new Uint8Array(1)), reader2.read(new Uint8Array(1))]; 916 917 await flushAsyncEvents(); 918 rs.controller.byobRequest.view[0] = 0x11; 919 rs.controller.byobRequest.respond(1); 920 rs.controller.close(); 921 922 const result1 = await branch1Reads[0]; 923 assert_equals(result1.done, false, 'first read() from branch1 should be not done'); 924 assert_typed_array_equals(result1.value, new Uint8Array([0x11]), 'first chunk from branch1 should be correct'); 925 const result2 = await branch2Reads[0]; 926 assert_equals(result2.done, false, 'first read() from branch2 should be not done'); 927 assert_typed_array_equals(result2.value, new Uint8Array([0x11]), 'first chunk from branch2 should be correct'); 928 929 const result3 = await branch1Reads[1]; 930 assert_equals(result3.done, true, 'second read() from branch1 should be done'); 931 assert_typed_array_equals(result3.value, new Uint8Array([0]).subarray(0, 0), 'second chunk from branch1 should be correct'); 932 const result4 = await branch2Reads[1]; 933 assert_equals(result4.done, true, 'second read() from branch2 should be done'); 934 assert_typed_array_equals(result4.value, new Uint8Array([0]).subarray(0, 0), 'second chunk from branch2 should be correct'); 935 936}, 'ReadableStream teeing with byte source: respond() and close() while both branches are pulling'); 937