OLD | NEW |
| (Empty) |
1 'use strict'; | |
2 | |
3 if (self.importScripts) { | |
4 self.importScripts('../resources/rs-utils.js'); | |
5 self.importScripts('/resources/testharness.js'); | |
6 } | |
7 | |
8 let ReadableStreamReader; | |
9 | |
10 test(() => { | |
11 | |
12 // It's not exposed globally, but we test a few of its properties here. | |
13 ReadableStreamReader = (new ReadableStream()).getReader().constructor; | |
14 | |
15 }, 'Can get the ReadableStreamReader constructor indirectly'); | |
16 | |
17 test(() => { | |
18 | |
19 assert_throws(new TypeError(), () => new ReadableStreamReader('potato')); | |
20 assert_throws(new TypeError(), () => new ReadableStreamReader({})); | |
21 assert_throws(new TypeError(), () => new ReadableStreamReader()); | |
22 | |
23 }, 'ReadableStreamReader constructor should get a ReadableStream object as argum
ent'); | |
24 | |
25 test(() => { | |
26 | |
27 const methods = ['cancel', 'constructor', 'read', 'releaseLock']; | |
28 const properties = methods.concat(['closed']).sort(); | |
29 | |
30 const rsReader = new ReadableStreamReader(new ReadableStream()); | |
31 const proto = Object.getPrototypeOf(rsReader); | |
32 | |
33 assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties); | |
34 | |
35 for (const m of methods) { | |
36 const propDesc = Object.getOwnPropertyDescriptor(proto, m); | |
37 assert_equals(propDesc.enumerable, false, 'method should be non-enumerable')
; | |
38 assert_equals(propDesc.configurable, true, 'method should be configurable'); | |
39 assert_equals(propDesc.writable, true, 'method should be writable'); | |
40 assert_equals(typeof rsReader[m], 'function', 'should have be a method'); | |
41 } | |
42 | |
43 const closedPropDesc = Object.getOwnPropertyDescriptor(proto, 'closed'); | |
44 assert_equals(closedPropDesc.enumerable, false, 'closed should be non-enumerab
le'); | |
45 assert_equals(closedPropDesc.configurable, true, 'closed should be configurabl
e'); | |
46 assert_not_equals(closedPropDesc.get, undefined, 'closed should have a getter'
); | |
47 assert_equals(closedPropDesc.set, undefined, 'closed should not have a setter'
); | |
48 | |
49 assert_equals(rsReader.cancel.length, 1, 'cancel has 1 parameter'); | |
50 assert_not_equals(rsReader.closed, undefined, 'has a non-undefined closed prop
erty'); | |
51 assert_equals(typeof rsReader.closed.then, 'function', 'closed property is the
nable'); | |
52 assert_equals(typeof rsReader.constructor, 'function', 'has a constructor meth
od'); | |
53 assert_equals(rsReader.constructor.length, 1, 'constructor has 1 parameter'); | |
54 assert_equals(typeof rsReader.read, 'function', 'has a getReader method'); | |
55 assert_equals(rsReader.read.length, 0, 'read has no parameters'); | |
56 assert_equals(typeof rsReader.releaseLock, 'function', 'has a releaseLock meth
od'); | |
57 assert_equals(rsReader.releaseLock.length, 0, 'releaseLock has no parameters')
; | |
58 | |
59 }, 'ReadableStreamReader instances should have the correct list of properties'); | |
60 | |
61 test(() => { | |
62 | |
63 const rsReader = new ReadableStreamReader(new ReadableStream()); | |
64 assert_equals(rsReader.closed, rsReader.closed, 'closed should return the same
promise'); | |
65 | |
66 }, 'ReadableStreamReader closed should always return the same promise object'); | |
67 | |
68 test(() => { | |
69 | |
70 const rs = new ReadableStream(); | |
71 new ReadableStreamReader(rs); // Constructing directly the first time should b
e fine. | |
72 assert_throws(new TypeError(), () => new ReadableStreamReader(rs), | |
73 'constructing directly the second time should fail'); | |
74 | |
75 }, 'Constructing a ReadableStreamReader directly should fail if the stream is al
ready locked (via direct ' + | |
76 'construction)'); | |
77 | |
78 test(() => { | |
79 | |
80 const rs = new ReadableStream(); | |
81 new ReadableStreamReader(rs); // Constructing directly should be fine. | |
82 assert_throws(new TypeError(), () => rs.getReader(), 'getReader() should fail'
); | |
83 | |
84 }, 'Getting a ReadableStreamReader via getReader should fail if the stream is al
ready locked (via direct ' + | |
85 'construction)'); | |
86 | |
87 test(() => { | |
88 | |
89 const rs = new ReadableStream(); | |
90 rs.getReader(); // getReader() should be fine. | |
91 assert_throws(new TypeError(), () => new ReadableStreamReader(rs), 'constructi
ng directly should fail'); | |
92 | |
93 }, 'Constructing a ReadableStreamReader directly should fail if the stream is al
ready locked (via getReader)'); | |
94 | |
95 test(() => { | |
96 | |
97 const rs = new ReadableStream(); | |
98 rs.getReader(); // getReader() should be fine. | |
99 assert_throws(new TypeError(), () => rs.getReader(), 'getReader() should fail'
); | |
100 | |
101 }, 'Getting a ReadableStreamReader via getReader should fail if the stream is al
ready locked (via getReader)'); | |
102 | |
103 test(() => { | |
104 | |
105 const rs = new ReadableStream({ | |
106 start(c) { | |
107 c.close(); | |
108 } | |
109 }); | |
110 | |
111 new ReadableStreamReader(rs); // Constructing directly should not throw. | |
112 | |
113 }, 'Constructing a ReadableStreamReader directly should be OK if the stream is c
losed'); | |
114 | |
115 test(() => { | |
116 | |
117 const theError = new Error('don\'t say i didn\'t warn ya'); | |
118 const rs = new ReadableStream({ | |
119 start(c) { | |
120 c.error(theError); | |
121 } | |
122 }); | |
123 | |
124 new ReadableStreamReader(rs); // Constructing directly should not throw. | |
125 | |
126 }, 'Constructing a ReadableStreamReader directly should be OK if the stream is e
rrored'); | |
127 | |
128 promise_test(() => { | |
129 | |
130 let controller; | |
131 const rs = new ReadableStream({ | |
132 start(c) { | |
133 controller = c; | |
134 } | |
135 }); | |
136 const reader = rs.getReader(); | |
137 | |
138 const promise = reader.read().then(result => { | |
139 assert_object_equals(result, { value: 'a', done: false }, 'read() should ful
fill with the enqueued chunk'); | |
140 }); | |
141 | |
142 controller.enqueue('a'); | |
143 return promise; | |
144 | |
145 }, 'Reading from a reader for an empty stream will wait until a chunk is availab
le'); | |
146 | |
147 promise_test(() => { | |
148 | |
149 let cancelCalled = false; | |
150 const passedReason = new Error('it wasn\'t the right time, sorry'); | |
151 const rs = new ReadableStream({ | |
152 cancel(reason) { | |
153 assert_true(rs.locked, 'the stream should still be locked'); | |
154 assert_throws(new TypeError(), () => rs.getReader(), 'should not be able t
o get another reader'); | |
155 assert_equals(reason, passedReason, 'the cancellation reason is passed thr
ough to the underlying source'); | |
156 cancelCalled = true; | |
157 } | |
158 }); | |
159 | |
160 const reader = rs.getReader(); | |
161 return reader.cancel(passedReason).then(() => assert_true(cancelCalled)); | |
162 | |
163 }, 'cancel() on a reader does not release the reader'); | |
164 | |
165 promise_test(() => { | |
166 | |
167 let controller; | |
168 const rs = new ReadableStream({ | |
169 start(c) { | |
170 controller = c; | |
171 } | |
172 }); | |
173 | |
174 const reader = rs.getReader(); | |
175 const promise = reader.closed; | |
176 | |
177 controller.close(); | |
178 return promise; | |
179 | |
180 }, 'closed should be fulfilled after stream is closed (.closed access before acq
uiring)'); | |
181 | |
182 promise_test(t => { | |
183 | |
184 let controller; | |
185 const rs = new ReadableStream({ | |
186 start(c) { | |
187 controller = c; | |
188 } | |
189 }); | |
190 | |
191 const reader1 = rs.getReader(); | |
192 | |
193 reader1.releaseLock(); | |
194 | |
195 const reader2 = rs.getReader(); | |
196 controller.close(); | |
197 | |
198 return Promise.all([ | |
199 promise_rejects(t, new TypeError(), reader1.closed), | |
200 reader2.closed | |
201 ]); | |
202 | |
203 }, 'closed should be rejected after reader releases its lock (multiple stream lo
cks)'); | |
204 | |
205 promise_test(() => { | |
206 | |
207 const rs = new ReadableStream({ | |
208 start(c) { | |
209 c.enqueue('a'); | |
210 c.enqueue('b'); | |
211 c.close(); | |
212 } | |
213 }); | |
214 | |
215 const reader1 = rs.getReader(); | |
216 const promise1 = reader1.read().then(r => { | |
217 assert_object_equals(r, { value: 'a', done: false }, 'reading the first chun
k from reader1 works'); | |
218 }); | |
219 reader1.releaseLock(); | |
220 | |
221 const reader2 = rs.getReader(); | |
222 const promise2 = reader2.read().then(r => { | |
223 assert_object_equals(r, { value: 'b', done: false }, 'reading the second chu
nk from reader2 works'); | |
224 }); | |
225 reader2.releaseLock(); | |
226 | |
227 return Promise.all([promise1, promise2]); | |
228 | |
229 }, 'Multiple readers can access the stream in sequence'); | |
230 | |
231 promise_test(() => { | |
232 const rs = new ReadableStream({ | |
233 start(c) { | |
234 c.enqueue('a'); | |
235 } | |
236 }); | |
237 | |
238 const reader1 = rs.getReader(); | |
239 reader1.releaseLock(); | |
240 | |
241 const reader2 = rs.getReader(); | |
242 | |
243 // Should be a no-op | |
244 reader1.releaseLock(); | |
245 | |
246 return reader2.read().then(result => { | |
247 assert_object_equals(result, { value: 'a', done: false }, | |
248 'read() should still work on reader2 even after reader1
is released'); | |
249 }); | |
250 | |
251 }, 'Cannot use an already-released reader to unlock a stream again'); | |
252 | |
253 promise_test(t => { | |
254 | |
255 const rs = new ReadableStream({ | |
256 start(c) { | |
257 c.enqueue('a'); | |
258 }, | |
259 cancel() { | |
260 assert_unreached('underlying source cancel should not be called'); | |
261 } | |
262 }); | |
263 | |
264 const reader = rs.getReader(); | |
265 reader.releaseLock(); | |
266 const cancelPromise = reader.cancel(); | |
267 | |
268 const reader2 = rs.getReader(); | |
269 const readPromise = reader2.read().then(r => { | |
270 assert_object_equals(r, { value: 'a', done: false }, 'a new reader should be
able to read a chunk'); | |
271 }); | |
272 | |
273 return Promise.all([ | |
274 promise_rejects(t, new TypeError(), cancelPromise), | |
275 readPromise | |
276 ]); | |
277 | |
278 }, 'cancel() on a released reader is a no-op and does not pass through'); | |
279 | |
280 promise_test(t => { | |
281 | |
282 const promiseAsserts = []; | |
283 | |
284 let controller; | |
285 const theError = { name: 'unique error' }; | |
286 const rs = new ReadableStream({ | |
287 start(c) { | |
288 controller = c; | |
289 } | |
290 }); | |
291 | |
292 const reader1 = rs.getReader(); | |
293 | |
294 promiseAsserts.push( | |
295 promise_rejects(t, theError, reader1.closed), | |
296 promise_rejects(t, theError, reader1.read()) | |
297 ); | |
298 | |
299 assert_throws(new TypeError(), () => rs.getReader(), 'trying to get another re
ader before erroring should throw'); | |
300 | |
301 controller.error(theError); | |
302 | |
303 reader1.releaseLock(); | |
304 | |
305 const reader2 = rs.getReader(); | |
306 | |
307 promiseAsserts.push( | |
308 promise_rejects(t, theError, reader2.closed), | |
309 promise_rejects(t, theError, reader2.read()) | |
310 ); | |
311 | |
312 return Promise.all(promiseAsserts); | |
313 | |
314 }, 'Getting a second reader after erroring the stream and releasing the reader s
hould succeed'); | |
315 | |
316 promise_test(t => { | |
317 | |
318 let controller; | |
319 const rs = new ReadableStream({ | |
320 start(c) { | |
321 controller = c; | |
322 } | |
323 }); | |
324 | |
325 const promise = rs.getReader().closed.then( | |
326 t.unreached_func('closed promise should not be fulfilled when stream is erro
red'), | |
327 err => { | |
328 assert_equals(err, undefined, 'passed error should be undefined as it was'
); | |
329 } | |
330 ); | |
331 | |
332 controller.error(); | |
333 return promise; | |
334 | |
335 }, 'ReadableStreamReader closed promise should be rejected with undefined if tha
t is the error'); | |
336 | |
337 | |
338 promise_test(t => { | |
339 | |
340 const rs = new ReadableStream({ | |
341 start() { | |
342 return Promise.reject(); | |
343 } | |
344 }); | |
345 | |
346 return rs.getReader().read().then( | |
347 t.unreached_func('read promise should not be fulfilled when stream is errore
d'), | |
348 err => { | |
349 assert_equals(err, undefined, 'passed error should be undefined as it was'
); | |
350 } | |
351 ); | |
352 | |
353 }, 'ReadableStreamReader: if start rejects with no parameter, it should error th
e stream with an undefined error'); | |
354 | |
355 promise_test(t => { | |
356 | |
357 const theError = { name: 'unique string' }; | |
358 let controller; | |
359 const rs = new ReadableStream({ | |
360 start(c) { | |
361 controller = c; | |
362 } | |
363 }); | |
364 | |
365 const promise = promise_rejects(t, theError, rs.getReader().closed); | |
366 | |
367 controller.error(theError); | |
368 return promise; | |
369 | |
370 }, 'Erroring a ReadableStream after checking closed should reject ReadableStream
Reader closed promise'); | |
371 | |
372 promise_test(t => { | |
373 | |
374 const theError = { name: 'unique string' }; | |
375 let controller; | |
376 const rs = new ReadableStream({ | |
377 start(c) { | |
378 controller = c; | |
379 } | |
380 }); | |
381 | |
382 controller.error(theError); | |
383 | |
384 // Let's call getReader twice for extra test coverage of this code path. | |
385 rs.getReader().releaseLock(); | |
386 | |
387 return promise_rejects(t, theError, rs.getReader().closed); | |
388 | |
389 }, 'Erroring a ReadableStream before checking closed should reject ReadableStrea
mReader closed promise'); | |
390 | |
391 promise_test(() => { | |
392 | |
393 let controller; | |
394 const rs = new ReadableStream({ | |
395 start(c) { | |
396 controller = c; | |
397 } | |
398 }); | |
399 const reader = rs.getReader(); | |
400 | |
401 const promise = Promise.all([ | |
402 reader.read().then(result => { | |
403 assert_object_equals(result, { value: undefined, done: true }, 'read() sho
uld fulfill with close (1)'); | |
404 }), | |
405 reader.read().then(result => { | |
406 assert_object_equals(result, { value: undefined, done: true }, 'read() sho
uld fulfill with close (2)'); | |
407 }), | |
408 reader.closed | |
409 ]); | |
410 | |
411 controller.close(); | |
412 return promise; | |
413 | |
414 }, 'Reading twice on a stream that gets closed'); | |
415 | |
416 promise_test(() => { | |
417 | |
418 let controller; | |
419 const rs = new ReadableStream({ | |
420 start(c) { | |
421 controller = c; | |
422 } | |
423 }); | |
424 | |
425 controller.close(); | |
426 const reader = rs.getReader(); | |
427 | |
428 return Promise.all([ | |
429 reader.read().then(result => { | |
430 assert_object_equals(result, { value: undefined, done: true }, 'read() sho
uld fulfill with close (1)'); | |
431 }), | |
432 reader.read().then(result => { | |
433 assert_object_equals(result, { value: undefined, done: true }, 'read() sho
uld fulfill with close (2)'); | |
434 }), | |
435 reader.closed | |
436 ]); | |
437 | |
438 }, 'Reading twice on a closed stream'); | |
439 | |
440 promise_test(t => { | |
441 | |
442 let controller; | |
443 const rs = new ReadableStream({ | |
444 start(c) { | |
445 controller = c; | |
446 } | |
447 }); | |
448 | |
449 const myError = { name: 'mashed potatoes' }; | |
450 controller.error(myError); | |
451 | |
452 const reader = rs.getReader(); | |
453 | |
454 return Promise.all([ | |
455 promise_rejects(t, myError, reader.read()), | |
456 promise_rejects(t, myError, reader.read()), | |
457 promise_rejects(t, myError, reader.closed) | |
458 ]); | |
459 | |
460 }, 'Reading twice on an errored stream'); | |
461 | |
462 promise_test(t => { | |
463 | |
464 let controller; | |
465 const rs = new ReadableStream({ | |
466 start(c) { | |
467 controller = c; | |
468 } | |
469 }); | |
470 | |
471 const myError = { name: 'mashed potatoes' }; | |
472 const reader = rs.getReader(); | |
473 | |
474 const promise = Promise.all([ | |
475 promise_rejects(t, myError, reader.read()), | |
476 promise_rejects(t, myError, reader.read()), | |
477 promise_rejects(t, myError, reader.closed) | |
478 ]); | |
479 | |
480 controller.error(myError); | |
481 return promise; | |
482 | |
483 }, 'Reading twice on a stream that gets errored'); | |
484 | |
485 done(); | |
OLD | NEW |