5bca6ae25c7c466543ec24edd9d85cbace9f3fe3
[WebKit-https.git] / LayoutTests / streams / reference-implementation / readable-stream-templated.html
1 <!DOCTYPE html>
2 <script>
3 // Silence unhandled rejection messages.
4 window.addEventListener("unhandledrejection", function(event) {
5     event.stopImmediatePropagation();
6     event.preventDefault();
7 });
8 </script>
9 <script src='../../resources/testharness.js'></script>
10 <script src='../../resources/testharnessreport.js'></script>
11 <script src='resources/streams-utils.js'></script>
12 <script>
13 // This is updated till https://github.com/whatwg/streams/commit/4ba861e6f60c248060811830e11271c84b439cc3
14
15 function templatedRSClosed(label, factory) {
16     test(function() {
17     }, 'Running templatedRSClosed with ' + label);
18
19     var test1 = async_test('piping to a WritableStream in the writable state should close the writable stream');
20     test1.step(function() {
21         var closeCalled = false;
22
23         var rs = factory();
24
25         var startPromise = Promise.resolve();
26         var ws = new WritableStream({
27             start: function() {
28                 return startPromise;
29             },
30             write: function() {
31                 assert_unreached('Unexpected write call');
32             },
33             close: function() {
34                 closeCalled = true;
35             },
36             abort: function() {
37                 assert_unreached('Unexpected abort call');
38             }
39         });
40
41         startPromise.then(test1.step_func(function() {
42             assert_equals(ws.state, 'writable', 'writable stream should start in writable state');
43
44             return rs.pipeTo(ws).then(test1.step_func(function() {
45                 assert_true(closeCalled);
46                 assert_equals(ws.state, 'closed', 'writable stream should become closed');
47                 test1.done('underlying source close should be called');
48             }));
49         })).catch(test1.step_func(function(e) { assert_unreached(e); }));
50     });
51
52     var test2 = async_test('piping to a WritableStream in the writable state with { preventClose: true } should do nothing');
53     test2.step(function() {
54         var rs = factory();
55
56         var startPromise = Promise.resolve();
57         var ws = new WritableStream({
58             start: function() {
59                 return startPromise;
60             },
61             write: function() {
62                 assert_unreached('Unexpected write call');
63             },
64             close: function() {
65                 assert_unreached('Unexpected close call');
66             },
67             abort: function() {
68                 assert_unreached('Unexpected abort call');
69             }
70         });
71
72         startPromise.then(test2.step_func(function() {
73             assert_equals(ws.state, 'writable', 'writable stream should start in writable state');
74
75             return rs.pipeTo(ws, { preventClose: true }).then(test2.step_func(function() {
76                 assert_equals(ws.state, 'writable', 'writable stream should still be writable');
77                 test2.done('pipeTo promise should be fulfilled');
78             }));
79         })).catch(test2.step_func(function(e) { assert_unreached(e); }));
80     });
81 };
82
83 function templatedRSErrored(label, factory, error) {
84     test(function() {
85     }, 'Running templatedRSErrored with ' + label);
86
87     var test1 = async_test('piping to a WritableStream in the writable state should abort the writable stream');
88     test1.step(function() {
89         var rs = factory();
90
91         var startPromise = Promise.resolve();
92         var ws = new WritableStream({
93             start: function() {
94                 return startPromise;
95             },
96             write: function() {
97                 assert_unreached('Unexpected write call');
98             },
99             close: function() {
100                 assert_reached('Unexpected close call');
101             },
102             abort: function(reason) {
103                 assert_equals(reason, error);
104             }
105         });
106
107         startPromise.then(test1.step_func(function() {
108             assert_equals(ws.state, 'writable');
109
110             rs.pipeTo(ws).then(
111                 test1.step_func(function() { assert_unreached('pipeTo promise should not be fulfilled'); }),
112                 test1.step_func(function(e) {
113                     assert_equals(e, error, 'pipeTo promise should be rejected with the passed error');
114                     assert_equals(ws.state, 'errored', 'writable stream should become errored');
115                     test1.done();
116                 })
117             );
118         }));
119     });
120 };
121
122 function templatedRSErroredAsyncOnly(label, factory, error) {
123     test(function() {
124     }, 'Running templatedRSErroredAsyncOnly with ' + label);
125
126     var test1 = async_test('piping with no options');
127     test1.step(function() {
128         var closeCalled = false;
129
130         var rs = factory();
131
132         var ws = new WritableStream({
133             abort: function(r) {
134                 assert_equals(r, error, 'reason passed to abort should equal the source error');
135             }
136         });
137
138         rs.pipeTo(ws).catch(test1.step_func(function(e) {
139             assert_equals(ws.state, 'errored', 'destination should be errored');
140             assert_equals(e, error, 'rejection reason of pipeToPromise should be the source error');
141             assert_true(closeCalled);
142             test1.done();
143         }));
144
145         ws.closed.catch(test1.step_func(function(e) {
146             assert_equals(e, error, 'rejection reason of dest closed should be the source error');
147             closeCalled = true;
148         }))
149     });
150
151     var test2 = async_test('piping with { preventAbort: false }');
152     test2.step(function() {
153         var abortCalled = false;
154         var closeRejected = false;
155
156         var rs = factory();
157
158         var ws = new WritableStream({
159             abort: function(r) {
160                 assert_equals(r, error, 'reason passed to abort should equal the source error');
161                 abortCalled = true;
162             }
163         });
164
165         rs.pipeTo(ws, { preventAbort: false }).catch(test2.step_func(function(e) {
166             assert_equals(ws.state, 'errored', 'destination should be errored');
167             assert_equals(e, error, 'rejection reason of pipeToPromise should be the source error');
168             assert_true(abortCalled);
169             assert_true(closeRejected);
170             test2.done();
171         }));
172
173         ws.closed.catch(test2.step_func(function(e) {
174             assert_equals(e, error, 'rejection reason of dest closed should be the source error');
175             closeRejected = true;
176         }));
177     });
178
179     var test3 = async_test('piping with { preventAbort: true }');
180     test3.step(function() {
181         var rs = factory();
182
183         var ws = new WritableStream({
184             abort: function() {
185                 assert_unreached('underlying sink abort should not be called');
186             }
187         });
188
189         rs.pipeTo(ws, { preventAbort: true }).catch(test3.step_func(function(e) {
190             assert_equals(ws.state, 'writable', 'destination should remain writable');
191             assert_equals(e, error, 'rejection reason of pipeToPromise should be the source error');
192             test3.done();
193         }));
194    });
195 };
196
197 function templatedRSTwoChunksClosed(label, factory, error) {
198     test(function() {
199     }, 'Running templatedRSTwoChunksClosed with ' + label);
200
201     var test1 = async_test('piping with no options and no destination errors');
202     test1.step(function() {
203         var rs = factory();
204
205         var chunksWritten = [];
206         var ws = new WritableStream({
207             abort: function() {
208                 assert_unreached('unexpected abort call');
209             },
210             write: function(chunk) {
211                 chunksWritten.push(chunk);
212             }
213         });
214
215         rs.pipeTo(ws).then(test1.step_func(function() {
216             assert_equals(ws.state, 'closed', 'destination should be closed');
217             assert_array_equals(chunksWritten, chunks);
218             test1.done();
219         }));
220     });
221
222     var test2 = async_test('piping with { preventClose: false } and no destination errors');
223     test2.step(function() {
224         var rs = factory();
225
226         var chunksWritten = [];
227         var ws = new WritableStream({
228             abort: function() {
229                 assert_unreached('unexpected abort call');
230             },
231             write: function(chunk) {
232                 chunksWritten.push(chunk);
233             }
234         });
235
236         rs.pipeTo(ws).then(test2.step_func(function() {
237             assert_equals(ws.state, 'closed', 'destination should be closed');
238             assert_array_equals(chunksWritten, chunks);
239             test2.done();
240         }));
241     });
242
243     var test3 = async_test('piping with { preventClose: true } and no destination errors');
244     test3.step(function() {
245         var rs = factory();
246
247         var chunksWritten = [];
248         var ws = new WritableStream({
249             close: function() {
250                 assert_unreached('unexpected close call');
251             },
252             abort: function() {
253                 assert_unreached('unexpected abort call');
254             },
255             write: function(chunk) {
256                 chunksWritten.push(chunk);
257             }
258         });
259
260         rs.pipeTo(ws, { preventClose: true }).then(test3.step_func(function() {
261             assert_equals(ws.state, 'writable', 'destination should be writable');
262             assert_array_equals(chunksWritten, chunks);
263             test3.done();
264         }));
265     });
266
267     var test4 = async_test('piping with { preventClose: false } and a destination with that errors synchronously');
268     test4.step(function() {
269         var rs = factory();
270
271         var theError = new Error('!!!');
272         var ws = new WritableStream({
273             close: function() {
274                 assert_unreached('unexpected close call');
275             },
276             abort: function() {
277                 assert_unreached('unexpected abort call');
278             },
279             write: function() {
280                 throw theError;
281             }
282         });
283
284         rs.pipeTo(ws, { preventClose: false }).then(
285             test4.step_func(function() { assert_unreached('pipeTo promise should not fulfill'); }),
286             test4.step_func(function(e) {
287                 assert_equals(e, theError, 'pipeTo promise should reject with the write error');
288                 test4.done();
289             })
290         );
291     });
292
293     var test5 = async_test('piping with { preventClose: true } and a destination with that errors synchronously');
294     test5.step(function() {
295         var rs = factory();
296
297         var theError = new Error('!!!');
298         var ws = new WritableStream({
299             close: function() {
300                 assert_unreached('unexpected close call');
301             },
302             abort: function() {
303                 assert_unreached('unexpected abort call');
304             },
305             write: function() {
306                 throw theError;
307             }
308         });
309
310         rs.pipeTo(ws, { preventClose: true }).then(
311             test5.step_func(function() { assert_unreached('pipeTo promise should not fulfill'); }),
312             test5.step_func(function(e) {
313                 assert_equals(e, theError, 'pipeTo promise should reject with the write error');
314                 test5.done();
315             })
316         );
317     });
318
319     var test6 = async_test('piping with { preventClose: true } and a destination that errors on the last chunk');
320     test6.step(function() {
321         var rs = factory();
322
323         var theError = new Error('!!!');
324         var chunkCounter = 0;
325         var ws = new WritableStream(
326             {
327                 close: function() {
328                     assert_unreached('unexpected close call');
329                 },
330                 abort: function() {
331                     assert_unreached('unexpected abort call');
332                 },
333                 write: function() {
334                     if (++chunkCounter === 2) {
335                         return new Promise(test6.step_func(function(r, reject) { setTimeout(test6.step_func(function() { reject(theError); }), 200); }));
336                     }
337                 }
338             },
339             {
340                 highWaterMark: Infinity,
341                 size: function() { return 1; }
342             }
343         );
344
345         rs.pipeTo(ws, { preventClose: true }).then(
346             test6.step_func(function() { assert_unreached('pipeTo promise should not fulfill'); }),
347             test6.step_func(function(e) {
348                 assert_equals(e, theError, 'pipeTo promise should reject with the write error');
349                 test6.done();
350             })
351         );
352     });
353 };
354
355 templatedRSClosed('ReadableStream (closed via call in start)', function() {
356     return new ReadableStream({
357         start: function(c) {
358             c.close();
359         }
360     });
361 });
362
363 templatedRSClosed('ReadableStream (closed via cancel)', function() {
364     var stream = new ReadableStream();
365     stream.cancel();
366     return stream;
367 });
368 var theError = new Error('boo!');
369
370 templatedRSErrored('ReadableStream (errored via call in start)', function() {
371     return new ReadableStream({
372         start: function(c) {
373             c.error(theError);
374         }
375     })},
376     theError
377 );
378
379 templatedRSErrored('ReadableStream (errored via returning a rejected promise in start)', function() {
380     return new ReadableStream({
381         start: function() {
382             return Promise.reject(theError);
383         }
384     })},
385     theError
386 );
387
388 templatedRSErroredAsyncOnly('ReadableStream (errored via returning a rejected promise in start) reader', function() {
389     return new ReadableStream({
390         start: function() { return Promise.reject(theError); }
391     })},
392     theError
393 );
394
395 var chunks = ['a', 'b'];
396
397 templatedRSTwoChunksClosed('ReadableStream (two chunks enqueued, then closed)', function() {
398     return new ReadableStream({
399         start: function(c) {
400             c.enqueue(chunks[0]);
401             c.enqueue(chunks[1]);
402             c.close();
403         }
404     })},
405     chunks
406 );
407
408 templatedRSTwoChunksClosed('ReadableStream (two chunks enqueued async, then closed)', function() {
409     return new ReadableStream({
410         _cancelled: false,
411         start: function(c) {
412             setTimeout(() => {
413                 if (!this._cancelled)
414                     c.enqueue(chunks[0]);
415             }, 100);
416             setTimeout(() => {
417                 if (!this._cancelled)
418                     c.enqueue(chunks[1]);
419             }, 200);
420             setTimeout(() => {
421                 if (!this._cancelled)
422                     c.close();
423             }, 300);
424         },
425         cancel: function(reason) {
426              this._cancelled = true;
427         }
428     })},
429     chunks
430 );
431
432 templatedRSTwoChunksClosed('ReadableStream (two chunks enqueued via pull, then closed)', function() {
433     var pullCall = 0;
434
435     return new ReadableStream({
436         pull:function(c) {
437             if (pullCall >= chunks.length) {
438                 c.close();
439             } else {
440                 c.enqueue(chunks[pullCall++]);
441             }
442         }
443     });
444 },
445 chunks
446 );
447 </script>