• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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