[Streams API] Fix readable-stream-tee.html
[WebKit-https.git] / LayoutTests / streams / reference-implementation / readable-stream-tee.html
1 <!DOCTYPE html>
2 <script src='../../resources/testharness.js'></script>
3 <script src='../../resources/testharnessreport.js'></script>
4 <script src='resources/streams-utils.js'></script>
5 <script>
6 test(function() {
7     var rs = new ReadableStream();
8
9     var result = rs.tee();
10
11     assert_true(Array.isArray(result), 'return value should be an array');
12     assert_equals(result.length, 2, 'array should have length 2');
13     assert_equals(result[0].constructor, ReadableStream, '0th element should be a ReadableStream');
14     assert_equals(result[1].constructor, ReadableStream, '1st element should be a ReadableStream');
15 }, 'ReadableStream teeing: rs.tee() returns an array of two ReadableStreams');
16
17 var test1 = async_test('ReadableStream teeing: should be able to read one branch to the end without affecting the other');
18 test1.step(function() {
19     var closedCalled = false;
20     var readCalls = 0;
21     const rs = new ReadableStream({
22         start: function(c) {
23             c.enqueue('a');
24             c.enqueue('b');
25             c.close();
26         }
27     });
28
29     var branch = rs.tee();
30     var branch1 = branch[0];
31     var branch2 = branch[1];
32     var reader1 = branch1.getReader();
33     var reader2 = branch2.getReader();
34
35     reader1.closed.then(test1.step_func(function() {
36         closedCalled = true;
37     })).catch(test1.step_func(function(e) { assert_unreached(e); }));
38     reader2.closed.then(test1.step_func(function() { assert_unreached('branch2 should not be closed'); }));
39
40     reader1.read().then(test1.step_func(function(r) {
41         assert_object_equals(r, { value: 'a', done: false }, 'first chunk from branch1 should be correct');
42         ++readCalls;
43     }));
44     reader1.read().then(test1.step_func(function(r) {
45         assert_object_equals(r, { value: 'b', done: false }, 'second chunk from branch1 should be correct');
46         ++readCalls;
47     }));
48     reader1.read().then(test1.step_func(function(r) {
49         assert_object_equals(r, { value: undefined, done: true }, 'third read() from branch1 should be done');
50         assert_true(closedCalled);
51         assert_equals(++readCalls, 4);
52         test1.done();
53     }));
54
55     reader2.read().then(test1.step_func(function(r) {
56         assert_object_equals(r, { value: 'a', done: false }, 'first chunk from branch2 should be correct');
57         ++readCalls;
58     }));
59 });
60
61 var test2 = async_test('ReadableStream teeing: values should be equal across each branch');
62 test2.step(function() {
63     var theObject = { the: 'test object' };
64     var rs = new ReadableStream({
65         start: function(c) {
66             c.enqueue(theObject);
67         }
68     });
69
70     var branch = rs.tee();
71     var branch1 = branch[0];
72     var branch2 = branch[1];
73     var reader1 = branch1.getReader();
74     var reader2 = branch2.getReader();
75
76     Promise.all([reader1.read(), reader2.read()]).then(test2.step_func(function(values) {
77         assert_object_equals(values[0], values[1], 'the values should be equal');
78         test2.done();
79     }));
80 });
81
82 var test3 = async_test('ReadableStream teeing: errors in the source should propagate to both branches');
83 test3.step(function() {
84     var closedRejects = 0;
85     var readCalls = 0;
86     var readRejects = 0;
87     var theError = new Error('boo!');
88     var rs = new ReadableStream({
89         start: function(c) {
90             c.enqueue('a');
91             c.enqueue('b');
92         },
93         pull: function() {
94             throw theError;
95         }
96     });
97
98     var branch = rs.tee();
99     var branch1 = branch[0];
100     var branch2 = branch[1];
101     var reader1 = branch1.getReader();
102     var reader2 = branch2.getReader();
103
104     reader1.label = 'reader1';
105     reader2.label = 'reader2';
106
107     reader1.closed.catch(test3.step_func(function(e) {
108         ++closedRejects;
109         assert_equals(e, theError, 'branch1 closed promise should reject with the error');
110     }));
111     reader2.closed.catch(test3.step_func(function(e) {
112         ++closedRejects;
113         assert_equals(e, theError, 'branch2 closed promise should reject with the error');
114     }));
115
116     reader1.read().then(test3.step_func(function(r) {
117         ++readCalls;
118         assert_object_equals(r, { value: 'a', done: false }, 'should be able to read the first chunk in branch1');
119     }));
120
121     reader1.read().then(test3.step_func(function(r) {
122         ++readCalls;
123         assert_object_equals(r, { value: 'b', done: false }, 'should be able to read the second chunk in branch1');
124
125         return reader2.read().then(
126             test3.step_func(function() { assert_unreached('once the root stream has errored, you should not be able to read from branch2'); }),
127             test3.step_func(function(e) {
128                 ++readRejects;
129                 assert_equals(e, theError, 'branch2 read() promise should reject with the error');
130             }));
131     })).then(test3.step_func(function() {
132         return reader1.read().then(
133             test3.step_func(function() { assert_unreached('once the root stream has errored, you should not be able to read from branch1 either'); }),
134             test3.step_func(function(e) {
135                 assert_equals(closedRejects, 2);
136                 assert_equals(readCalls, 2);
137                 assert_equals(++readRejects, 2);
138                 assert_equals(e, theError, 'branch1 read() promise should reject with the error');
139                 test3.done();
140             })
141         );
142     })).catch(test3.step_func(function(e) { assert_unreached(e); }));
143 });
144
145 var test4 = async_test('ReadableStream teeing: canceling branch1 should not impact branch2');
146 test4.step(function() {
147     var branch1Read = false;
148     var rs = new ReadableStream({
149         start: function(c) {
150             c.enqueue('a');
151             c.enqueue('b');
152             c.close();
153         }
154     });
155
156     var branch = rs.tee();
157     var branch1 = branch[0];
158     var branch2 = branch[1];
159     branch1.cancel();
160
161     readableStreamToArray(branch1).then(test4.step_func(function(chunks) {
162         assert_array_equals(chunks, [], 'branch1 should have no chunks');
163         branch1Read = true;
164     }));
165
166     readableStreamToArray(branch2).then(test4.step_func(function(chunks) {
167         assert_array_equals(chunks, ['a', 'b'], 'branch2 should have two chunks');
168         assert_true(branch1Read);
169         test4.done();
170     }));
171 });
172
173 var test5 = async_test('ReadableStream teeing: canceling branch2 should not impact branch1');
174 test5.step(function() {
175     var branch2Read = false;
176     var rs = new ReadableStream({
177         start: function(c) {
178             c.enqueue('a');
179             c.enqueue('b');
180             c.close();
181         }
182     });
183
184     var branch = rs.tee();
185     var branch1 = branch[0];
186     var branch2 = branch[1];
187     branch2.cancel();
188
189     readableStreamToArray(branch1).then(test5.step_func(function(chunks) {
190         assert_array_equals(chunks, ['a', 'b'], 'branch1 should have two chunks');
191         assert_true(branch2Read);
192         test5.done();
193     }));
194     readableStreamToArray(branch2).then(test5.step_func(function(chunks) {
195         assert_array_equals(chunks, [], 'branch2 should have no chunks');
196         branch2Read = true;
197     }));
198 });
199
200 var test6 = async_test('ReadableStream teeing: canceling both branches should aggregate the cancel reasons into an array');
201 test6.step(function() {
202     var reason1 = new Error('We\'re wanted men.');
203     var reason2 = new Error('I have the death sentence on twelve systems.');
204
205     var rs = new ReadableStream({
206         cancel: test6.step_func(function(reason) {
207             assert_array_equals(reason, [reason1, reason2], 'the cancel reason should be an array containing those from the branches');
208             test6.done();
209         })
210     });
211
212     var branch = rs.tee();
213     var branch1 = branch[0];
214     var branch2 = branch[1];
215     branch1.cancel(reason1);
216     branch2.cancel(reason2);
217 });
218
219 var test7 = async_test('ReadableStream teeing: failing to cancel the original stream should cause cancel() to reject on branches');
220 test7.step(function() {
221     var cancelRejected = false;
222     var theError = new Error('I\'ll be careful.');
223     var rs = new ReadableStream({
224         cancel: function() {
225             throw theError;
226         }
227     });
228
229     var branch = rs.tee();
230     var branch1 = branch[0];
231     var branch2 = branch[1];
232     branch1.cancel().catch(test7.step_func(function(e) {
233         assert_equals(e, theError, 'branch1.cancel() should reject with the error');
234         cancelRejected = true;
235         test7.done();
236     }));
237     branch2.cancel().catch(test7.step_func(function(e) {
238         assert_equals(e, theError, 'branch2.cancel() should reject with the error');
239         assert_true(cancelRejected);
240         test7.done();
241     }));
242 });
243
244 var test8 = async_test('ReadableStream teeing: closing the original should immediately close the branches');
245 test8.step(function() {
246     var reader1Closed = false;
247     var controller;
248     var rs = new ReadableStream({
249         start: function(c) {
250             controller = c;
251         }
252     });
253
254     var branch = rs.tee();
255     var branch1 = branch[0];
256     var branch2 = branch[1];
257     var reader1 = branch1.getReader();
258     var reader2 = branch2.getReader();
259
260     reader1.closed.then(test8.step_func(function() {
261         reader1Closed = true; //branch1 should be closed
262     })).catch(test8.step_func(function(e) { assert_unreached(e); }));
263     reader2.closed.then(test8.step_func(function() {
264         assert_true(reader1Closed);
265         test8.done('branch2 should be closed');
266     })).catch(test8.step_func(function(e) { assert_unreached(e); }));
267
268     controller.close();
269 });
270
271 var test9 = async_test('ReadableStream teeing: erroring the original should immediately error the branches');
272 test9.step(function() {
273     var reader1Rejected = false;
274     var controller;
275     var rs = new ReadableStream({
276         start(c) {
277             controller = c;
278         }
279     });
280
281     var branch = rs.tee();
282     var branch1 = branch[0];
283     var branch2 = branch[1];
284     var reader1 = branch1.getReader();
285     var reader2 = branch2.getReader();
286
287     var theError = new Error('boo!');
288
289     reader1.closed.then(
290         test9.step_func(function() { assert_unreached('branch1 should not be closed'); }),
291         test9.step_func(function(e) {
292             assert_equals(e, theError, 'branch1 should be errored with the error');
293             reader1Rejected = true;
294         })
295     );
296     reader2.closed.then(
297         test9.step_func(function() { assert_unreached('branch2 should not be closed'); }),
298         test9.step_func(function(e) {
299             assert_equals(e, theError, 'branch2 should be errored with the error');
300             assert_true(reader1Rejected);
301             test9.done();
302         })
303     );
304
305     controller.error(theError);
306 });
307 </script>