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