• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// META: global=window,worker
2// META: script=../resources/test-utils.js
3// META: script=../resources/recording-streams.js
4'use strict';
5
6const error1 = new Error('error1!');
7error1.name = 'error1';
8
9const error2 = new Error('error2!');
10error2.name = 'error2';
11
12promise_test(t => {
13
14  const rs = recordingReadableStream();
15
16  const ws = recordingWritableStream({
17    start() {
18      return Promise.reject(error1);
19    }
20  });
21
22  return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error')
23    .then(() => {
24      assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
25      assert_array_equals(ws.events, []);
26    });
27
28}, 'Errors must be propagated backward: starts errored; preventCancel omitted; fulfilled cancel promise');
29
30promise_test(t => {
31
32  const rs = recordingReadableStream();
33
34  const ws = recordingWritableStream({
35    write() {
36      return Promise.reject(error1);
37    }
38  });
39
40  const writer = ws.getWriter();
41
42  return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
43    .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
44    .then(() => {
45      writer.releaseLock();
46
47      return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the write error')
48        .then(() => {
49          assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
50          assert_array_equals(ws.events, ['write', 'Hello']);
51        });
52    });
53
54}, 'Errors must be propagated backward: becomes errored before piping due to write; preventCancel omitted; ' +
55   'fulfilled cancel promise');
56
57promise_test(t => {
58
59  const rs = recordingReadableStream({
60    cancel() {
61      throw error2;
62    }
63  });
64
65  const ws = recordingWritableStream({
66    write() {
67      return Promise.reject(error1);
68    }
69  });
70
71  const writer = ws.getWriter();
72
73  return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
74    .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
75    .then(() => {
76      writer.releaseLock();
77
78      return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error')
79        .then(() => {
80          assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
81          assert_array_equals(ws.events, ['write', 'Hello']);
82        });
83    });
84
85}, 'Errors must be propagated backward: becomes errored before piping due to write; preventCancel omitted; rejected ' +
86   'cancel promise');
87
88for (const falsy of [undefined, null, false, +0, -0, NaN, '']) {
89  const stringVersion = Object.is(falsy, -0) ? '-0' : String(falsy);
90
91  promise_test(t => {
92
93    const rs = recordingReadableStream();
94
95    const ws = recordingWritableStream({
96      write() {
97        return Promise.reject(error1);
98      }
99    });
100
101    const writer = ws.getWriter();
102
103    return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
104      .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
105      .then(() => {
106        writer.releaseLock();
107
108        return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: falsy }),
109                               'pipeTo must reject with the write error')
110          .then(() => {
111            assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
112            assert_array_equals(ws.events, ['write', 'Hello']);
113          });
114      });
115
116  }, `Errors must be propagated backward: becomes errored before piping due to write; preventCancel = ` +
117     `${stringVersion} (falsy); fulfilled cancel promise`);
118}
119
120for (const truthy of [true, 'a', 1, Symbol(), { }]) {
121  promise_test(t => {
122
123    const rs = recordingReadableStream();
124
125    const ws = recordingWritableStream({
126      write() {
127        return Promise.reject(error1);
128      }
129    });
130
131    const writer = ws.getWriter();
132
133    return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
134      .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
135      .then(() => {
136        writer.releaseLock();
137
138        return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: truthy }),
139                               'pipeTo must reject with the write error')
140          .then(() => {
141            assert_array_equals(rs.eventsWithoutPulls, []);
142            assert_array_equals(ws.events, ['write', 'Hello']);
143          });
144      });
145
146  }, `Errors must be propagated backward: becomes errored before piping due to write; preventCancel = ` +
147     `${String(truthy)} (truthy)`);
148}
149
150promise_test(t => {
151
152  const rs = recordingReadableStream();
153
154  const ws = recordingWritableStream({
155    write() {
156      return Promise.reject(error1);
157    }
158  });
159
160  const writer = ws.getWriter();
161
162  return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
163    .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
164    .then(() => {
165      writer.releaseLock();
166
167      return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true, preventAbort: true }),
168                             'pipeTo must reject with the write error')
169        .then(() => {
170          assert_array_equals(rs.eventsWithoutPulls, []);
171          assert_array_equals(ws.events, ['write', 'Hello']);
172        });
173    });
174
175}, 'Errors must be propagated backward: becomes errored before piping due to write, preventCancel = true; ' +
176   'preventAbort = true');
177
178promise_test(t => {
179
180  const rs = recordingReadableStream();
181
182  const ws = recordingWritableStream({
183    write() {
184      return Promise.reject(error1);
185    }
186  });
187
188  const writer = ws.getWriter();
189
190  return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
191    .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
192    .then(() => {
193      writer.releaseLock();
194
195      return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true, preventAbort: true, preventClose: true }),
196                             'pipeTo must reject with the write error')
197        .then(() => {
198          assert_array_equals(rs.eventsWithoutPulls, []);
199          assert_array_equals(ws.events, ['write', 'Hello']);
200        });
201    });
202
203}, 'Errors must be propagated backward: becomes errored before piping due to write; preventCancel = true, ' +
204   'preventAbort = true, preventClose = true');
205
206promise_test(t => {
207
208  const rs = recordingReadableStream({
209    start(controller) {
210      controller.enqueue('Hello');
211    }
212  });
213
214  const ws = recordingWritableStream({
215    write() {
216      throw error1;
217    }
218  });
219
220  return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => {
221    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
222    assert_array_equals(ws.events, ['write', 'Hello']);
223  });
224
225}, 'Errors must be propagated backward: becomes errored during piping due to write; preventCancel omitted; fulfilled ' +
226   'cancel promise');
227
228promise_test(t => {
229
230  const rs = recordingReadableStream({
231    start(controller) {
232      controller.enqueue('Hello');
233    },
234    cancel() {
235      throw error2;
236    }
237  });
238
239  const ws = recordingWritableStream({
240    write() {
241      throw error1;
242    }
243  });
244
245  return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error').then(() => {
246    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
247    assert_array_equals(ws.events, ['write', 'Hello']);
248  });
249
250}, 'Errors must be propagated backward: becomes errored during piping due to write; preventCancel omitted; rejected ' +
251   'cancel promise');
252
253promise_test(t => {
254
255  const rs = recordingReadableStream({
256    start(controller) {
257      controller.enqueue('Hello');
258    }
259  });
260
261  const ws = recordingWritableStream({
262    write() {
263      throw error1;
264    }
265  });
266
267  return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 'pipeTo must reject with the same error')
268  .then(() => {
269    assert_array_equals(rs.eventsWithoutPulls, []);
270    assert_array_equals(ws.events, ['write', 'Hello']);
271  });
272
273}, 'Errors must be propagated backward: becomes errored during piping due to write; preventCancel = true');
274
275promise_test(t => {
276
277  const rs = recordingReadableStream({
278    start(controller) {
279      controller.enqueue('a');
280      controller.enqueue('b');
281      controller.enqueue('c');
282    }
283  });
284
285  const ws = recordingWritableStream({
286    write() {
287      if (ws.events.length > 2) {
288        return delay(0).then(() => {
289          throw error1;
290        });
291      }
292      return undefined;
293    }
294  });
295
296  return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => {
297    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
298    assert_array_equals(ws.events, ['write', 'a', 'write', 'b']);
299  });
300
301}, 'Errors must be propagated backward: becomes errored during piping due to write, but async; preventCancel = ' +
302   'false; fulfilled cancel promise');
303
304promise_test(t => {
305
306  const rs = recordingReadableStream({
307    start(controller) {
308      controller.enqueue('a');
309      controller.enqueue('b');
310      controller.enqueue('c');
311    },
312    cancel() {
313      throw error2;
314    }
315  });
316
317  const ws = recordingWritableStream({
318    write() {
319      if (ws.events.length > 2) {
320        return delay(0).then(() => {
321          throw error1;
322        });
323      }
324      return undefined;
325    }
326  });
327
328  return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error').then(() => {
329    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
330    assert_array_equals(ws.events, ['write', 'a', 'write', 'b']);
331  });
332
333}, 'Errors must be propagated backward: becomes errored during piping due to write, but async; preventCancel = ' +
334   'false; rejected cancel promise');
335
336promise_test(t => {
337
338  const rs = recordingReadableStream({
339    start(controller) {
340      controller.enqueue('a');
341      controller.enqueue('b');
342      controller.enqueue('c');
343    }
344  });
345
346  const ws = recordingWritableStream({
347    write() {
348      if (ws.events.length > 2) {
349        return delay(0).then(() => {
350          throw error1;
351        });
352      }
353      return undefined;
354    }
355  });
356
357  return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 'pipeTo must reject with the same error')
358  .then(() => {
359    assert_array_equals(rs.eventsWithoutPulls, []);
360    assert_array_equals(ws.events, ['write', 'a', 'write', 'b']);
361  });
362
363}, 'Errors must be propagated backward: becomes errored during piping due to write, but async; preventCancel = true');
364
365promise_test(t => {
366
367  const rs = recordingReadableStream();
368
369  const ws = recordingWritableStream();
370
371  const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
372
373  t.step_timeout(() => ws.controller.error(error1), 10);
374
375  return pipePromise.then(() => {
376    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
377    assert_array_equals(ws.events, []);
378  });
379
380}, 'Errors must be propagated backward: becomes errored after piping; preventCancel omitted; fulfilled cancel promise');
381
382promise_test(t => {
383
384  const rs = recordingReadableStream({
385    cancel() {
386      throw error2;
387    }
388  });
389
390  const ws = recordingWritableStream();
391
392  const pipePromise = promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error');
393
394  t.step_timeout(() => ws.controller.error(error1), 10);
395
396  return pipePromise.then(() => {
397    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
398    assert_array_equals(ws.events, []);
399  });
400
401}, 'Errors must be propagated backward: becomes errored after piping; preventCancel omitted; rejected cancel promise');
402
403promise_test(t => {
404
405  const rs = recordingReadableStream();
406
407  const ws = recordingWritableStream();
408
409  const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }),
410                                              'pipeTo must reject with the same error');
411
412  t.step_timeout(() => ws.controller.error(error1), 10);
413
414  return pipePromise.then(() => {
415    assert_array_equals(rs.eventsWithoutPulls, []);
416    assert_array_equals(ws.events, []);
417  });
418
419}, 'Errors must be propagated backward: becomes errored after piping; preventCancel = true');
420
421promise_test(t => {
422
423  const rs = recordingReadableStream({
424    start(controller) {
425      controller.enqueue('a');
426      controller.enqueue('b');
427      controller.enqueue('c');
428      controller.close();
429    }
430  });
431
432  const ws = recordingWritableStream({
433    write(chunk) {
434      if (chunk === 'c') {
435        return Promise.reject(error1);
436      }
437      return undefined;
438    }
439  });
440
441  return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => {
442    assert_array_equals(rs.eventsWithoutPulls, []);
443    assert_array_equals(ws.events, ['write', 'a', 'write', 'b', 'write', 'c']);
444  });
445
446}, 'Errors must be propagated backward: becomes errored after piping due to last write; source is closed; ' +
447   'preventCancel omitted (but cancel is never called)');
448
449promise_test(t => {
450
451  const rs = recordingReadableStream({
452    start(controller) {
453      controller.enqueue('a');
454      controller.enqueue('b');
455      controller.enqueue('c');
456      controller.close();
457    }
458  });
459
460  const ws = recordingWritableStream({
461    write(chunk) {
462      if (chunk === 'c') {
463        return Promise.reject(error1);
464      }
465      return undefined;
466    }
467  });
468
469  return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 'pipeTo must reject with the same error')
470    .then(() => {
471      assert_array_equals(rs.eventsWithoutPulls, []);
472      assert_array_equals(ws.events, ['write', 'a', 'write', 'b', 'write', 'c']);
473    });
474
475}, 'Errors must be propagated backward: becomes errored after piping due to last write; source is closed; ' +
476   'preventCancel = true');
477
478promise_test(t => {
479
480  const rs = recordingReadableStream();
481
482  const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
483
484  const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
485
486  t.step_timeout(() => ws.controller.error(error1), 10);
487
488  return pipePromise.then(() => {
489    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
490    assert_array_equals(ws.events, []);
491  });
492
493}, 'Errors must be propagated backward: becomes errored after piping; dest never desires chunks; preventCancel = ' +
494   'false; fulfilled cancel promise');
495
496promise_test(t => {
497
498  const rs = recordingReadableStream({
499    cancel() {
500      throw error2;
501    }
502  });
503
504  const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
505
506  const pipePromise = promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error');
507
508  t.step_timeout(() => ws.controller.error(error1), 10);
509
510  return pipePromise.then(() => {
511    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
512    assert_array_equals(ws.events, []);
513  });
514
515}, 'Errors must be propagated backward: becomes errored after piping; dest never desires chunks; preventCancel = ' +
516   'false; rejected cancel promise');
517
518promise_test(t => {
519
520  const rs = recordingReadableStream();
521
522  const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
523
524  const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }),
525                                              'pipeTo must reject with the same error');
526
527  t.step_timeout(() => ws.controller.error(error1), 10);
528
529  return pipePromise.then(() => {
530    assert_array_equals(rs.eventsWithoutPulls, []);
531    assert_array_equals(ws.events, []);
532  });
533
534}, 'Errors must be propagated backward: becomes errored after piping; dest never desires chunks; preventCancel = ' +
535   'true');
536
537promise_test(() => {
538
539  const rs = recordingReadableStream();
540
541  const ws = recordingWritableStream();
542
543  ws.abort(error1);
544
545  return rs.pipeTo(ws).then(
546    () => assert_unreached('the promise must not fulfill'),
547    err => {
548      assert_equals(err, error1, 'the promise must reject with error1');
549
550      assert_array_equals(rs.eventsWithoutPulls, ['cancel', err]);
551      assert_array_equals(ws.events, ['abort', error1]);
552    }
553  );
554
555}, 'Errors must be propagated backward: becomes errored before piping via abort; preventCancel omitted; fulfilled ' +
556   'cancel promise');
557
558promise_test(t => {
559
560  const rs = recordingReadableStream({
561    cancel() {
562      throw error2;
563    }
564  });
565
566  const ws = recordingWritableStream();
567
568  ws.abort(error1);
569
570  return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error')
571    .then(() => {
572      return ws.getWriter().closed.then(
573        () => assert_unreached('the promise must not fulfill'),
574        err => {
575          assert_equals(err, error1, 'the promise must reject with error1');
576
577          assert_array_equals(rs.eventsWithoutPulls, ['cancel', err]);
578          assert_array_equals(ws.events, ['abort', error1]);
579        }
580      );
581    });
582
583}, 'Errors must be propagated backward: becomes errored before piping via abort; preventCancel omitted; rejected ' +
584   'cancel promise');
585
586promise_test(t => {
587
588  const rs = recordingReadableStream();
589
590  const ws = recordingWritableStream();
591
592  ws.abort(error1);
593
594  return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true })).then(() => {
595    assert_array_equals(rs.eventsWithoutPulls, []);
596    assert_array_equals(ws.events, ['abort', error1]);
597  });
598
599}, 'Errors must be propagated backward: becomes errored before piping via abort; preventCancel = true');
600
601promise_test(t => {
602
603  const rs = recordingReadableStream();
604
605  let resolveWriteCalled;
606  const writeCalledPromise = new Promise(resolve => {
607    resolveWriteCalled = resolve;
608  });
609
610  const ws = recordingWritableStream({
611    write() {
612      resolveWriteCalled();
613      return flushAsyncEvents();
614    }
615  });
616
617  const pipePromise = rs.pipeTo(ws);
618
619  rs.controller.enqueue('a');
620
621  return writeCalledPromise.then(() => {
622    ws.controller.error(error1);
623
624    return promise_rejects_exactly(t, error1, pipePromise);
625  }).then(() => {
626    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
627    assert_array_equals(ws.events, ['write', 'a']);
628  });
629
630}, 'Errors must be propagated backward: erroring via the controller errors once pending write completes');
631