streams/reference-implementation/readable-stream-reader.html and streams/reference...
[WebKit-https.git] / LayoutTests / streams / reference-implementation / readable-stream-reader.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 var ReadableStreamReader;
7
8 test(function() {
9     // It's not exposed globally, but we test a few of its properties here.
10     ReadableStreamReader = (new ReadableStream()).getReader().constructor;
11 }, 'Can get the ReadableStreamReader constructor indirectly');
12
13 test(function() {
14     assert_throws(new TypeError(), function() {
15         new ReadableStreamReader('potato');
16     });
17     assert_throws(new TypeError(), function() {
18         new ReadableStreamReader({});
19     });
20     assert_throws(new TypeError(), function() {
21         new ReadableStreamReader();
22     });
23 }, 'ReadableStreamReader constructor should get a ReadableStream object as argument');
24
25 test(function() {
26     var methods = ['cancel', 'constructor', 'read', 'releaseLock'];
27     var properties = methods.concat(['closed']).sort();
28
29     var rsReader = new ReadableStreamReader(new ReadableStream());
30     var proto = Object.getPrototypeOf(rsReader);
31
32     assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties);
33
34     for (var m of methods) {
35         var propDesc = Object.getOwnPropertyDescriptor(proto, m);
36         assert_equals(propDesc.enumerable, false, 'method should be non-enumerable');
37         assert_equals(propDesc.configurable, true, 'method should be configurable');
38         assert_equals(propDesc.writable, true, 'method should be writable');
39         assert_equals(typeof rsReader[m], 'function', 'should have be a method');
40     }
41
42     var closedPropDesc = Object.getOwnPropertyDescriptor(proto, 'closed');
43     assert_equals(closedPropDesc.enumerable, false, 'closed should be non-enumerable');
44     assert_equals(closedPropDesc.configurable, true, 'closed should be configurable');
45     assert_not_equals(closedPropDesc.get, undefined, 'closed should have a getter');
46     assert_equals(closedPropDesc.set, undefined, 'closed should not have a setter');
47
48     assert_equals(rsReader.cancel.length, 1, 'cancel has 1 parameter');
49     assert_not_equals(rsReader.closed, undefined, 'has a non-undefined closed property');
50     assert_equals(typeof rsReader.closed.then, 'function', 'closed property is thenable');
51     assert_equals(typeof rsReader.constructor, 'function', 'has a constructor method');
52     assert_equals(rsReader.constructor.length, 1, 'constructor has 1 parameter');
53     assert_equals(typeof rsReader.read, 'function', 'has a getReader method');
54     assert_equals(rsReader.read.length, 0, 'read has no parameters');
55     assert_equals(typeof rsReader.releaseLock, 'function', 'has a releaseLock method');
56     assert_equals(rsReader.releaseLock.length, 0, 'releaseLock has no parameters');
57 }, 'ReadableStreamReader instances should have the correct list of properties');
58
59 test(function() {
60     var rsReader = new ReadableStreamReader(new ReadableStream());
61
62     assert_equals(rsReader.closed, rsReader.closed, 'closed should return the same promise');
63 }, 'ReadableStreamReader closed should always return the same promise object');
64
65 test(function() {
66     var rs = new ReadableStream();
67     new ReadableStreamReader(rs); // Constructing directly should be fine.
68     assert_throws(new TypeError(), function() { new ReadableStreamReader(rs); }, 'constructing directly should fail');
69 }, 'Constructing a ReadableStreamReader directly should fail if the stream is already locked (via direct construction)');
70
71 test(function() {
72     var rs = new ReadableStream();
73     new ReadableStreamReader(rs); // Constructing directly should be fine.
74     assert_throws(new TypeError(), function() { rs.getReader(); }, 'getReader() should fail');
75 }, 'Getting a ReadableStreamReader via getReader should fail if the stream is already locked (via direct construction)');
76
77 test(function() {
78     var rs = new ReadableStream();
79     rs.getReader(); // getReader() should be fine.
80     assert_throws(new TypeError(), function() { new ReadableStreamReader(rs); }, 'constructing directly should fail');
81 }, 'Constructing a ReadableStreamReader directly should fail if the stream is already locked (via getReader)');
82
83 test(function() {
84     var rs = new ReadableStream();
85     rs.getReader(); // getReader() should be fine.
86     assert_throws(new TypeError(), function() { rs.getReader(); }, 'getReader() should fail');
87 }, 'Getting a ReadableStreamReader via getReader should fail if the stream is already locked (via direct getReader)');
88
89 test(function() {
90     var rs = new ReadableStream({
91         start: function(c) {
92             c.close();
93         }
94     });
95
96     new ReadableStreamReader(rs); // Constructing directly should not throw.
97 }, 'Constructing a ReadableStreamReader directly should be OK if the stream is closed');
98
99 test(function() {
100     var theError = new Error('don\'t say i didn\'t warn ya');
101     var rs = new ReadableStream({
102         start: function(c) {
103             c.error(theError);
104         }
105     });
106
107     new ReadableStreamReader(rs); // Constructing directly should not throw.
108 }, 'Constructing a ReadableStreamReader directly should be OK if the stream is errored');
109
110 var test1 = async_test('Reading from a reader for an empty stream will wait until a chunk is available');
111 test1.step(function() {
112     var controller;
113     var rs = new ReadableStream({
114         start: function(c) {
115             controller = c;
116         }
117     });
118     var reader = rs.getReader();
119
120     reader.read().then(test1.step_func(function(result) {
121         assert_object_equals(result, { value: 'a', done: false }, 'read() should fulfill with the enqueued chunk');
122         test1.done();
123     }));
124
125     controller.enqueue('a');
126 });
127
128 var test2 = async_test('cancel() on a reader releases the reader before calling through');
129 test2.step(function() {
130     var cancelCalled = false;
131     var passedReason = new Error('it wasn\'t the right time, sorry');
132     var rs = new ReadableStream({
133         cancel: function(reason) {
134             cancelCalled = true;
135             rs.getReader(); // Should be able to get another reader without error.
136             assert_equals(reason, passedReason, 'the cancellation reason is passed through to the underlying source');
137         }
138     });
139
140     var reader = rs.getReader();
141     reader.cancel(passedReason).then(
142         test2.step_func(function() {
143             assert_true(cancelCalled);
144             test2.done('reader.cancel() should fulfill');
145         }),
146         test2.step_func(function(e) { assert_unreached('reader.cancel() should not reject'); }));
147 });
148
149 var test3 = async_test('closed should be fulfilled after stream is closed (.closed access before acquiring)');
150 test3.step(function() {
151     var controller;
152     var rs = new ReadableStream({
153         start: function(c) {
154             controller = c;
155         }
156     });
157
158     var reader = rs.getReader();
159     reader.closed.then(test3.step_func(function() {
160         test3.done('reader closed should be fulfilled');
161     }));
162
163     controller.close();
164 });
165
166 var test4 = async_test('closed should be fulfilled after reader releases its lock (multiple stream locks)');
167 test4.step(function() {
168     var controller;
169     var reader1Closed = false;
170     var rs = new ReadableStream({
171         start: function(c) {
172             controller = c;
173         }
174     });
175
176     var reader1 = rs.getReader();
177
178     reader1.releaseLock();
179
180     var reader2 = rs.getReader();
181     controller.close();
182
183     reader1.closed.then(test4.step_func(function() {
184         reader1Closed = true;
185     }));
186
187     reader2.closed.then(test4.step_func(function() {
188         assert_true(reader1Closed);
189         test4.done('reader2 closed should be fulfilled');
190     }));
191 });
192
193 // var test5 = async_test('Multiple readers can access the stream in sequence');
194 // test5.step(function() {
195 //     var readCount = 0;
196 //     var rs = new ReadableStream({
197 //         start: function(c) {
198 //             c.enqueue('a');
199 //             c.enqueue('b');
200 //             c.close();
201 //         }
202 //     });
203
204 //     var reader1 = rs.getReader();
205 //     reader1.read().then(test5.step_func(function(r) {
206 //         assert_object_equals(r, { value: 'a', done: false }, 'reading the first chunk from reader1 works');
207 //         ++readCount;
208 //     }));
209 //     reader1.releaseLock();
210
211 //     var reader2 = rs.getReader();
212 //     reader2.read().then(test5.step_func(function(r) {
213 //         assert_object_equals(r, { value: 'b', done: false }, 'reading the second chunk from reader2 works');
214 //         ++readCount;
215 //     }));
216 //     reader2.releaseLock();
217
218 //     setTimeout(test5.step_func(function() {
219 //         assert_equals(readCount, 2);
220 //         test5.done();
221 //     }), standardTimeout);
222 // });
223
224 var test6 = async_test('Cannot use an already-released reader to unlock a stream again');
225 test6.step(function() {
226     var rs = new ReadableStream({
227         start: function(c) {
228             c.enqueue('a');
229         }
230     });
231
232     var reader1 = rs.getReader();
233     reader1.releaseLock();
234
235     var reader2 = rs.getReader();
236
237     reader1.releaseLock();
238     reader2.read().then(test6.step_func(function(result) {
239         assert_object_equals(result, { value: 'a', done: false }, 'read() should still work on reader2 even after reader1 is released');
240         test6.done();
241     }));
242 });
243
244 // var test7 = async_test('cancel() on a released reader is a no-op and does not pass through');
245 // test7.step(function() {
246 //     var readCounts = 0;
247 //     var cancelled = false;
248 //     var rs = new ReadableStream({
249 //         start: function(c) {
250 //             c.enqueue('a');
251 //         },
252 //         cancel: function() {
253 //             assert_unreached('underlying source cancel should not be called');
254 //         }
255 //     });
256
257 //     var reader = rs.getReader();
258 //     reader.releaseLock();
259 //     reader.cancel().then(test7.step_func(function(v) {
260 //         assert_equals(v, undefined, 'cancel() on the reader should fulfill with undefined')
261 //         cancelled = true;
262 //     }));
263
264 //     var reader2 = rs.getReader();
265 //     reader2.read().then(test7.step_func(function(r) {
266 //         assert_object_equals(r, { value: 'a', done: false }, 'a new reader should be able to read a chunk');
267 //         ++readCounts;
268 //     }));
269
270 //     setTimeout(test7.step_func(function() {
271 //         assert_true(cancelled);
272 //         assert_equals(readCounts, 1);
273 //         test7.done();
274 //     }), standardTimeout);
275 // });
276
277 var test8 = async_test('Getting a second reader after erroring the stream should succeed');
278 test8.step(function() {
279     var controller;
280     var receivedErrors = 0;
281     var theError = new Error('bad');
282     var rs = new ReadableStream({
283         start: function(c) {
284             controller = c;
285         }
286     });
287
288     var reader1 = rs.getReader();
289
290     reader1.closed.catch(test8.step_func(function(e) {
291         assert_equals(e, theError, 'the first reader closed getter should be rejected with the error');
292         ++receivedErrors;
293     }));
294
295     reader1.read().catch(test8.step_func(function(e) {
296         assert_equals(e, theError, 'the first reader read() should be rejected with the error');
297         ++receivedErrors;
298     }));
299
300     assert_throws(new TypeError(), function() { rs.getReader(); }, 'trying to get another reader before erroring should throw');
301
302     controller.error(theError);
303
304     rs.getReader().closed.catch(test8.step_func(function(e) {
305         assert_equals(e, theError, 'the second reader closed getter should be rejected with the error');
306         ++receivedErrors;
307     }));
308
309     rs.getReader().read().catch(test8.step_func(function(e) {
310         assert_equals(e, theError, 'the third reader read() should be rejected with the error');
311         assert_equals(++receivedErrors, 4);
312         test8.done();
313     }));
314 });
315 </script>