OLD | NEW |
| (Empty) |
1 'use strict'; | |
2 | |
3 if (self.importScripts) { | |
4 self.importScripts('../resources/test-utils.js'); | |
5 self.importScripts('../resources/rs-utils.js'); | |
6 self.importScripts('/resources/testharness.js'); | |
7 } | |
8 | |
9 test(() => { | |
10 | |
11 new ReadableStream(); // ReadableStream constructed with no parameters | |
12 new ReadableStream({ }); // ReadableStream constructed with an empty object as
parameter | |
13 new ReadableStream({ type: undefined }); // ReadableStream constructed with un
defined type | |
14 new ReadableStream(undefined); // ReadableStream constructed with undefined as
parameter | |
15 | |
16 let x; | |
17 new ReadableStream(x); // ReadableStream constructed with an undefined variabl
e as parameter | |
18 | |
19 }, 'ReadableStream can be constructed with no errors'); | |
20 | |
21 test(() => { | |
22 | |
23 assert_throws(new TypeError(), () => new ReadableStream(null), 'constructor sh
ould throw when the source is null'); | |
24 | |
25 }, 'ReadableStream can\'t be constructed with garbage'); | |
26 | |
27 test(() => { | |
28 | |
29 assert_throws(new RangeError(), () => new ReadableStream({ type: null }), | |
30 'constructor should throw when the type is null'); | |
31 assert_throws(new RangeError(), () => new ReadableStream({ type: '' }), | |
32 'constructor should throw when the type is empty string'); | |
33 assert_throws(new RangeError(), () => new ReadableStream({ type: 'asdf' }), | |
34 'constructor should throw when the type is asdf'); | |
35 | |
36 }, 'ReadableStream can\'t be constructed with an invalid type'); | |
37 | |
38 test(() => { | |
39 | |
40 const methods = ['cancel', 'constructor', 'getReader', 'pipeThrough', 'pipeTo'
, 'tee']; | |
41 const properties = methods.concat(['locked']).sort(); | |
42 | |
43 const rs = new ReadableStream(); | |
44 const proto = Object.getPrototypeOf(rs); | |
45 | |
46 assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties, 'sho
uld have all the correct methods'); | |
47 | |
48 for (const m of methods) { | |
49 const propDesc = Object.getOwnPropertyDescriptor(proto, m); | |
50 assert_false(propDesc.enumerable, 'method should be non-enumerable'); | |
51 assert_true(propDesc.configurable, 'method should be configurable'); | |
52 assert_true(propDesc.writable, 'method should be writable'); | |
53 assert_equals(typeof rs[m], 'function', 'method should be a function'); | |
54 } | |
55 | |
56 const lockedPropDesc = Object.getOwnPropertyDescriptor(proto, 'locked'); | |
57 assert_false(lockedPropDesc.enumerable, 'locked should be non-enumerable'); | |
58 assert_equals(lockedPropDesc.writable, undefined, 'locked should not be a data
property'); | |
59 assert_equals(typeof lockedPropDesc.get, 'function', 'locked should have a get
ter'); | |
60 assert_equals(lockedPropDesc.set, undefined, 'locked should not have a setter'
); | |
61 assert_true(lockedPropDesc.configurable, 'locked should be configurable'); | |
62 | |
63 assert_equals(rs.cancel.length, 1, 'cancel should have 1 parameter'); | |
64 assert_equals(rs.constructor.length, 0, 'constructor should have no parameters
'); | |
65 assert_equals(rs.getReader.length, 0, 'getReader should have no parameters'); | |
66 assert_equals(rs.tee.length, 0, 'tee should have no parameters'); | |
67 | |
68 }, 'ReadableStream instances should have the correct list of properties'); | |
69 | |
70 test(() => { | |
71 | |
72 assert_throws(new TypeError(), () => { | |
73 new ReadableStream({ start: 'potato' }); | |
74 }, 'constructor should throw when start is not a function'); | |
75 | |
76 }, 'ReadableStream constructor should throw for non-function start arguments'); | |
77 | |
78 test(() => { | |
79 | |
80 new ReadableStream({ cancel: '2' }); | |
81 | |
82 }, 'ReadableStream constructor can get initial garbage as cancel argument'); | |
83 | |
84 test(() => { | |
85 | |
86 new ReadableStream({ pull: { } }); | |
87 | |
88 }, 'ReadableStream constructor can get initial garbage as pull argument'); | |
89 | |
90 test(() => { | |
91 | |
92 let startCalled = false; | |
93 | |
94 const source = { | |
95 start(controller) { | |
96 assert_equals(this, source, 'source is this during start'); | |
97 | |
98 const methods = ['close', 'enqueue', 'error', 'constructor']; | |
99 const properties = ['desiredSize'].concat(methods).sort(); | |
100 const proto = Object.getPrototypeOf(controller); | |
101 | |
102 assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties, | |
103 'the controller should have the right properties'); | |
104 | |
105 for (const m of methods) { | |
106 const propDesc = Object.getOwnPropertyDescriptor(proto, m); | |
107 assert_equals(typeof controller[m], 'function', `should have a ${m} meth
od`); | |
108 assert_false(propDesc.enumerable, m + ' should be non-enumerable'); | |
109 assert_true(propDesc.configurable, m + ' should be configurable'); | |
110 assert_true(propDesc.writable, m + ' should be writable'); | |
111 } | |
112 | |
113 const desiredSizePropDesc = Object.getOwnPropertyDescriptor(proto, 'desire
dSize'); | |
114 assert_false(desiredSizePropDesc.enumerable, 'desiredSize should be non-en
umerable'); | |
115 assert_equals(desiredSizePropDesc.writable, undefined, 'desiredSize should
not be a data property'); | |
116 assert_equals(typeof desiredSizePropDesc.get, 'function', 'desiredSize sho
uld have a getter'); | |
117 assert_equals(desiredSizePropDesc.set, undefined, 'desiredSize should not
have a setter'); | |
118 assert_true(desiredSizePropDesc.configurable, 'desiredSize should be confi
gurable'); | |
119 | |
120 assert_equals(controller.close.length, 0, 'close should have no parameters
'); | |
121 assert_equals(controller.constructor.length, 5, 'constructor should have 4
parameter'); | |
122 assert_equals(controller.enqueue.length, 1, 'enqueue should have 1 paramet
er'); | |
123 assert_equals(controller.error.length, 1, 'error should have 1 parameter')
; | |
124 | |
125 startCalled = true; | |
126 } | |
127 }; | |
128 | |
129 new ReadableStream(source); | |
130 assert_true(startCalled); | |
131 | |
132 }, 'ReadableStream start should be called with the proper parameters'); | |
133 | |
134 test(() => { | |
135 | |
136 let startCalled = false; | |
137 const source = { | |
138 start(controller) { | |
139 const properties = ['close', 'constructor', 'desiredSize', 'enqueue', 'err
or']; | |
140 assert_array_equals(Object.getOwnPropertyNames(Object.getPrototypeOf(contr
oller)).sort(), properties, | |
141 'prototype should have the right properties'); | |
142 | |
143 controller.test = ''; | |
144 assert_array_equals(Object.getOwnPropertyNames(Object.getPrototypeOf(contr
oller)).sort(), properties, | |
145 'prototype should still have the right properties'); | |
146 assert_not_equals(Object.getOwnPropertyNames(controller).indexOf('test'),
-1, | |
147 '"test" should be a property of the controller'); | |
148 | |
149 startCalled = true; | |
150 } | |
151 }; | |
152 | |
153 new ReadableStream(source); | |
154 assert_true(startCalled); | |
155 | |
156 }, 'ReadableStream start controller parameter should be extensible'); | |
157 | |
158 promise_test(() => { | |
159 | |
160 function SimpleStreamSource() {} | |
161 let resolve; | |
162 const promise = new Promise(r => resolve = r); | |
163 SimpleStreamSource.prototype = { | |
164 start: resolve | |
165 }; | |
166 | |
167 new ReadableStream(new SimpleStreamSource()); | |
168 return promise; | |
169 | |
170 }, 'ReadableStream should be able to call start method within prototype chain of
its source'); | |
171 | |
172 promise_test(() => { | |
173 | |
174 const rs = new ReadableStream({ | |
175 start(c) { | |
176 return delay(5).then(() => { | |
177 c.enqueue('a'); | |
178 c.close(); | |
179 }); | |
180 } | |
181 }); | |
182 | |
183 const reader = rs.getReader(); | |
184 return reader.read().then(r => { | |
185 assert_object_equals(r, { value: 'a', done: false }, 'value read should be t
he one enqueued'); | |
186 return reader.closed; | |
187 }); | |
188 | |
189 }, 'ReadableStream start should be able to return a promise'); | |
190 | |
191 promise_test(() => { | |
192 | |
193 const theError = new Error('rejected!'); | |
194 const rs = new ReadableStream({ | |
195 start() { | |
196 return delay(1).then(() => { throw theError; }); | |
197 } | |
198 }); | |
199 | |
200 return rs.getReader().closed.then(() => { | |
201 assert_unreached('closed promise should be rejected'); | |
202 }, e => { | |
203 assert_equals(e, theError, 'promise should be rejected with the same error')
; | |
204 }); | |
205 | |
206 }, 'ReadableStream start should be able to return a promise and reject it'); | |
207 | |
208 promise_test(() => { | |
209 | |
210 const objects = [ | |
211 { potato: 'Give me more!' }, | |
212 'test', | |
213 1 | |
214 ]; | |
215 | |
216 const rs = new ReadableStream({ | |
217 start(c) { | |
218 for (const o of objects) { | |
219 c.enqueue(o); | |
220 } | |
221 c.close(); | |
222 } | |
223 }); | |
224 | |
225 const reader = rs.getReader(); | |
226 | |
227 return Promise.all([reader.read(), reader.read(), reader.read(), reader.closed
]).then(r => { | |
228 assert_object_equals(r[0], { value: objects[0], done: false }, 'value read s
hould be the one enqueued'); | |
229 assert_object_equals(r[1], { value: objects[1], done: false }, 'value read s
hould be the one enqueued'); | |
230 assert_object_equals(r[2], { value: objects[2], done: false }, 'value read s
hould be the one enqueued'); | |
231 }); | |
232 | |
233 }, 'ReadableStream should be able to enqueue different objects.'); | |
234 | |
235 promise_test(() => { | |
236 | |
237 const error = new Error('pull failure'); | |
238 const rs = new ReadableStream({ | |
239 pull() { | |
240 return Promise.reject(error); | |
241 } | |
242 }); | |
243 | |
244 const reader = rs.getReader(); | |
245 | |
246 let closed = false; | |
247 let read = false; | |
248 | |
249 return Promise.all([ | |
250 reader.closed.then(() => { | |
251 assert_unreached('closed should be rejected'); | |
252 }, e => { | |
253 closed = true; | |
254 assert_true(read); | |
255 assert_equals(e, error, 'closed should be rejected with the thrown error')
; | |
256 }), | |
257 reader.read().then(() => { | |
258 assert_unreached('read() should be rejected'); | |
259 }, e => { | |
260 read = true; | |
261 assert_false(closed); | |
262 assert_equals(e, error, 'read() should be rejected with the thrown error')
; | |
263 }) | |
264 ]); | |
265 | |
266 }, 'ReadableStream: if pull rejects, it should error the stream'); | |
267 | |
268 promise_test(() => { | |
269 | |
270 let pullCount = 0; | |
271 const startPromise = Promise.resolve(); | |
272 | |
273 new ReadableStream({ | |
274 start() { | |
275 return startPromise; | |
276 }, | |
277 pull() { | |
278 pullCount++; | |
279 } | |
280 }); | |
281 | |
282 return startPromise.then(() => { | |
283 assert_equals(pullCount, 1, 'pull should be called once start finishes'); | |
284 return delay(10); | |
285 }).then(() => { | |
286 assert_equals(pullCount, 1, 'pull should be called exactly once'); | |
287 }); | |
288 | |
289 }, 'ReadableStream: should only call pull once upon starting the stream'); | |
290 | |
291 promise_test(() => { | |
292 | |
293 let pullCount = 0; | |
294 | |
295 const rs = new ReadableStream({ | |
296 pull(c) { | |
297 // Don't enqueue immediately after start. We want the stream to be empty w
hen we call .read() on it. | |
298 if (pullCount > 0) { | |
299 c.enqueue(pullCount); | |
300 } | |
301 ++pullCount; | |
302 } | |
303 }); | |
304 | |
305 return delay(1).then(() => { | |
306 assert_equals(pullCount, 1, 'pull should be called once start finishes'); | |
307 | |
308 const reader = rs.getReader(); | |
309 const read = reader.read(); | |
310 assert_equals(pullCount, 2, 'pull should be called when read is called'); | |
311 return read; | |
312 }).then(result => { | |
313 assert_equals(pullCount, 3, 'pull should be called again in reaction to call
ing read'); | |
314 assert_object_equals(result, { value: 1, done: false }, 'the result read sho
uld be the one enqueued'); | |
315 }); | |
316 | |
317 }, 'ReadableStream: should call pull when trying to read from a started, empty s
tream'); | |
318 | |
319 promise_test(() => { | |
320 | |
321 let pullCount = 0; | |
322 const startPromise = Promise.resolve(); | |
323 | |
324 const rs = new ReadableStream({ | |
325 start(c) { | |
326 c.enqueue('a'); | |
327 return startPromise; | |
328 }, | |
329 pull() { | |
330 pullCount++; | |
331 } | |
332 }); | |
333 | |
334 const read = rs.getReader().read(); | |
335 assert_equals(pullCount, 0, 'calling read() should not cause pull to be called
yet'); | |
336 | |
337 return startPromise.then(() => { | |
338 assert_equals(pullCount, 1, 'pull should be called once start finishes'); | |
339 return read; | |
340 }).then(r => { | |
341 assert_object_equals(r, { value: 'a', done: false }, 'first read() should re
turn first chunk'); | |
342 assert_equals(pullCount, 1, 'pull should not have been called again'); | |
343 return delay(10); | |
344 }).then(() => { | |
345 assert_equals(pullCount, 1, 'pull should be called exactly once'); | |
346 }); | |
347 | |
348 }, 'ReadableStream: should only call pull once on a non-empty stream read from b
efore start fulfills'); | |
349 | |
350 promise_test(() => { | |
351 | |
352 let pullCount = 0; | |
353 const startPromise = Promise.resolve(); | |
354 | |
355 const rs = new ReadableStream({ | |
356 start(c) { | |
357 c.enqueue('a'); | |
358 return startPromise; | |
359 }, | |
360 pull() { | |
361 pullCount++; | |
362 } | |
363 }); | |
364 | |
365 return startPromise.then(() => { | |
366 assert_equals(pullCount, 0, 'pull should not be called once start finishes,
since the queue is full'); | |
367 | |
368 const read = rs.getReader().read(); | |
369 assert_equals(pullCount, 1, 'calling read() should cause pull to be called i
mmediately'); | |
370 return read; | |
371 }).then(r => { | |
372 assert_object_equals(r, { value: 'a', done: false }, 'first read() should re
turn first chunk'); | |
373 return delay(10); | |
374 }).then(() => { | |
375 assert_equals(pullCount, 1, 'pull should be called exactly once'); | |
376 }); | |
377 | |
378 }, 'ReadableStream: should only call pull once on a non-empty stream read from a
fter start fulfills'); | |
379 | |
380 promise_test(() => { | |
381 | |
382 let pullCount = 0; | |
383 let controller; | |
384 const startPromise = Promise.resolve(); | |
385 | |
386 const rs = new ReadableStream({ | |
387 start(c) { | |
388 controller = c; | |
389 return startPromise; | |
390 }, | |
391 pull() { | |
392 ++pullCount; | |
393 } | |
394 }); | |
395 | |
396 const reader = rs.getReader(); | |
397 return startPromise.then(() => { | |
398 assert_equals(pullCount, 1, 'pull should have been called once by the time t
he stream starts'); | |
399 | |
400 controller.enqueue('a'); | |
401 assert_equals(pullCount, 1, 'pull should not have been called again after en
queue'); | |
402 | |
403 return reader.read(); | |
404 }).then(() => { | |
405 assert_equals(pullCount, 2, 'pull should have been called again after read')
; | |
406 | |
407 return delay(10); | |
408 }).then(() => { | |
409 assert_equals(pullCount, 2, 'pull should be called exactly twice'); | |
410 }); | |
411 }, 'ReadableStream: should call pull in reaction to read()ing the last chunk, if
not draining'); | |
412 | |
413 promise_test(() => { | |
414 | |
415 let pullCount = 0; | |
416 let controller; | |
417 const startPromise = Promise.resolve(); | |
418 | |
419 const rs = new ReadableStream({ | |
420 start(c) { | |
421 controller = c; | |
422 return startPromise; | |
423 }, | |
424 pull() { | |
425 ++pullCount; | |
426 } | |
427 }); | |
428 | |
429 const reader = rs.getReader(); | |
430 | |
431 return startPromise.then(() => { | |
432 assert_equals(pullCount, 1, 'pull should have been called once by the time t
he stream starts'); | |
433 | |
434 controller.enqueue('a'); | |
435 assert_equals(pullCount, 1, 'pull should not have been called again after en
queue'); | |
436 | |
437 controller.close(); | |
438 | |
439 return reader.read(); | |
440 }).then(() => { | |
441 assert_equals(pullCount, 1, 'pull should not have been called a second time
after read'); | |
442 | |
443 return delay(10); | |
444 }).then(() => { | |
445 assert_equals(pullCount, 1, 'pull should be called exactly once'); | |
446 }); | |
447 | |
448 }, 'ReadableStream: should not call pull() in reaction to read()ing the last chu
nk, if draining'); | |
449 | |
450 promise_test(() => { | |
451 | |
452 let resolve; | |
453 let returnedPromise; | |
454 let timesCalled = 0; | |
455 const startPromise = Promise.resolve(); | |
456 | |
457 const rs = new ReadableStream({ | |
458 start() { | |
459 return startPromise; | |
460 }, | |
461 pull(c) { | |
462 c.enqueue(++timesCalled); | |
463 returnedPromise = new Promise(r => resolve = r); | |
464 return returnedPromise; | |
465 } | |
466 }); | |
467 const reader = rs.getReader(); | |
468 | |
469 return startPromise.then(() => { | |
470 return reader.read(); | |
471 }).then(result1 => { | |
472 assert_equals(timesCalled, 1, | |
473 'pull should have been called once after start, but not yet have been call
ed a second time'); | |
474 assert_object_equals(result1, { value: 1, done: false }, 'read() should fulf
ill with the enqueued value'); | |
475 | |
476 return delay(10); | |
477 }).then(() => { | |
478 assert_equals(timesCalled, 1, 'after 10 ms, pull should still only have been
called once'); | |
479 | |
480 resolve(); | |
481 return returnedPromise; | |
482 }).then(() => { | |
483 assert_equals(timesCalled, 2, | |
484 'after the promise returned by pull is fulfilled, pull should be called a
second time'); | |
485 }); | |
486 | |
487 }, 'ReadableStream: should not call pull until the previous pull call\'s promise
fulfills'); | |
488 | |
489 promise_test(() => { | |
490 | |
491 let timesCalled = 0; | |
492 const startPromise = Promise.resolve(); | |
493 | |
494 const rs = new ReadableStream( | |
495 { | |
496 start(c) { | |
497 c.enqueue('a'); | |
498 c.enqueue('b'); | |
499 c.enqueue('c'); | |
500 return startPromise; | |
501 }, | |
502 pull() { | |
503 ++timesCalled; | |
504 } | |
505 }, | |
506 { | |
507 size() { | |
508 return 1; | |
509 }, | |
510 highWaterMark: Infinity | |
511 } | |
512 ); | |
513 const reader = rs.getReader(); | |
514 | |
515 return startPromise.then(() => { | |
516 return reader.read(); | |
517 }).then(result1 => { | |
518 assert_object_equals(result1, { value: 'a', done: false }, 'first chunk shou
ld be as expected'); | |
519 | |
520 return reader.read(); | |
521 }).then(result2 => { | |
522 assert_object_equals(result2, { value: 'b', done: false }, 'second chunk sho
uld be as expected'); | |
523 | |
524 return reader.read(); | |
525 }).then(result3 => { | |
526 assert_object_equals(result3, { value: 'c', done: false }, 'third chunk shou
ld be as expected'); | |
527 | |
528 return delay(10); | |
529 }).then(() => { | |
530 // Once for after start, and once for every read. | |
531 assert_equals(timesCalled, 4, 'pull() should be called exactly four times'); | |
532 }); | |
533 | |
534 }, 'ReadableStream: should pull after start, and after every read'); | |
535 | |
536 promise_test(() => { | |
537 | |
538 let timesCalled = 0; | |
539 const startPromise = Promise.resolve(); | |
540 | |
541 const rs = new ReadableStream({ | |
542 start(c) { | |
543 c.enqueue('a'); | |
544 c.close(); | |
545 return startPromise; | |
546 }, | |
547 pull() { | |
548 ++timesCalled; | |
549 } | |
550 }); | |
551 | |
552 const reader = rs.getReader(); | |
553 return startPromise.then(() => { | |
554 assert_equals(timesCalled, 0, 'after start finishes, pull should not have be
en called'); | |
555 | |
556 return reader.read(); | |
557 }).then(() => { | |
558 assert_equals(timesCalled, 0, 'reading should not have triggered a pull call
'); | |
559 | |
560 return reader.closed; | |
561 }).then(() => { | |
562 assert_equals(timesCalled, 0, 'stream should have closed with still no calls
to pull'); | |
563 }); | |
564 | |
565 }, 'ReadableStream: should not call pull after start if the stream is now closed
'); | |
566 | |
567 promise_test(() => { | |
568 | |
569 let timesCalled = 0; | |
570 let resolve; | |
571 const ready = new Promise(r => resolve = r); | |
572 | |
573 new ReadableStream( | |
574 { | |
575 start() {}, | |
576 pull(c) { | |
577 c.enqueue(++timesCalled); | |
578 | |
579 if (timesCalled === 4) { | |
580 resolve(); | |
581 } | |
582 } | |
583 }, | |
584 { | |
585 size() { | |
586 return 1; | |
587 }, | |
588 highWaterMark: 4 | |
589 } | |
590 ); | |
591 | |
592 return ready.then(() => { | |
593 // after start: size = 0, pull() | |
594 // after enqueue(1): size = 1, pull() | |
595 // after enqueue(2): size = 2, pull() | |
596 // after enqueue(3): size = 3, pull() | |
597 // after enqueue(4): size = 4, do not pull | |
598 assert_equals(timesCalled, 4, 'pull() should have been called four times'); | |
599 }); | |
600 | |
601 }, 'ReadableStream: should call pull after enqueueing from inside pull (with no
read requests), if strategy allows'); | |
602 | |
603 promise_test(() => { | |
604 | |
605 let pullCalled = false; | |
606 | |
607 const rs = new ReadableStream({ | |
608 pull(c) { | |
609 pullCalled = true; | |
610 c.close(); | |
611 } | |
612 }); | |
613 | |
614 const reader = rs.getReader(); | |
615 return reader.closed.then(() => { | |
616 assert_true(pullCalled); | |
617 }); | |
618 | |
619 }, 'ReadableStream pull should be able to close a stream.'); | |
620 | |
621 promise_test(t => { | |
622 | |
623 const controllerError = { name: 'controller error' }; | |
624 | |
625 const rs = new ReadableStream({ | |
626 pull(c) { | |
627 c.error(controllerError); | |
628 } | |
629 }); | |
630 | |
631 return promise_rejects(t, controllerError, rs.getReader().closed); | |
632 | |
633 }, 'ReadableStream pull should be able to error a stream.'); | |
634 | |
635 promise_test(t => { | |
636 | |
637 const controllerError = { name: 'controller error' }; | |
638 const thrownError = { name: 'thrown error' }; | |
639 | |
640 const rs = new ReadableStream({ | |
641 pull(c) { | |
642 c.error(controllerError); | |
643 throw thrownError; | |
644 } | |
645 }); | |
646 | |
647 return promise_rejects(t, controllerError, rs.getReader().closed); | |
648 | |
649 }, 'ReadableStream pull should be able to error a stream and throw.'); | |
650 | |
651 test(() => { | |
652 | |
653 let startCalled = false; | |
654 | |
655 new ReadableStream({ | |
656 start(c) { | |
657 assert_equals(c.enqueue('a'), undefined, 'the first enqueue should return
undefined'); | |
658 c.close(); | |
659 | |
660 assert_throws(new TypeError(), () => c.enqueue('b'), 'enqueue after close
should throw a TypeError'); | |
661 startCalled = true; | |
662 } | |
663 }); | |
664 | |
665 assert_true(startCalled); | |
666 | |
667 }, 'ReadableStream: enqueue should throw when the stream is readable but drainin
g'); | |
668 | |
669 test(() => { | |
670 | |
671 let startCalled = false; | |
672 | |
673 new ReadableStream({ | |
674 start(c) { | |
675 c.close(); | |
676 | |
677 assert_throws(new TypeError(), () => c.enqueue('a'), 'enqueue after close
should throw a TypeError'); | |
678 startCalled = true; | |
679 } | |
680 }); | |
681 | |
682 assert_true(startCalled); | |
683 | |
684 }, 'ReadableStream: enqueue should throw when the stream is closed'); | |
685 | |
686 promise_test(() => { | |
687 | |
688 let startCalled = 0; | |
689 let pullCalled = 0; | |
690 let cancelCalled = 0; | |
691 | |
692 /* eslint-disable no-use-before-define */ | |
693 class Source { | |
694 start(c) { | |
695 startCalled++; | |
696 assert_equals(this, theSource, 'start() should be called with the correct
this'); | |
697 c.enqueue('a'); | |
698 } | |
699 | |
700 pull() { | |
701 pullCalled++; | |
702 assert_equals(this, theSource, 'pull() should be called with the correct t
his'); | |
703 } | |
704 | |
705 cancel() { | |
706 cancelCalled++; | |
707 assert_equals(this, theSource, 'cancel() should be called with the correct
this'); | |
708 } | |
709 } | |
710 /* eslint-enable no-use-before-define */ | |
711 | |
712 const theSource = new Source(); | |
713 theSource.debugName = 'the source object passed to the constructor'; // makes
test failures easier to diagnose | |
714 | |
715 const rs = new ReadableStream(theSource); | |
716 const reader = rs.getReader(); | |
717 | |
718 return reader.read().then(() => { | |
719 reader.releaseLock(); | |
720 rs.cancel(); | |
721 assert_equals(startCalled, 1); | |
722 assert_equals(pullCalled, 1); | |
723 assert_equals(cancelCalled, 1); | |
724 return rs.getReader().closed; | |
725 }); | |
726 | |
727 }, 'ReadableStream: should call underlying source methods as methods'); | |
728 | |
729 test(() => { | |
730 | |
731 let startCalled = false; | |
732 new ReadableStream({ | |
733 start(c) { | |
734 assert_equals(c.desiredSize, 1); | |
735 c.enqueue('a'); | |
736 assert_equals(c.desiredSize, 0); | |
737 c.enqueue('b'); | |
738 assert_equals(c.desiredSize, -1); | |
739 c.enqueue('c'); | |
740 assert_equals(c.desiredSize, -2); | |
741 c.enqueue('d'); | |
742 assert_equals(c.desiredSize, -3); | |
743 c.enqueue('e'); | |
744 startCalled = true; | |
745 } | |
746 }); | |
747 | |
748 assert_true(startCalled); | |
749 | |
750 }, 'ReadableStream strategies: the default strategy should give desiredSize of 1
to start, decreasing by 1 per enqueue'); | |
751 | |
752 promise_test(() => { | |
753 | |
754 let controller; | |
755 const rs = new ReadableStream({ | |
756 start(c) { | |
757 controller = c; | |
758 } | |
759 }); | |
760 const reader = rs.getReader(); | |
761 | |
762 assert_equals(controller.desiredSize, 1, 'desiredSize should start at 1'); | |
763 controller.enqueue('a'); | |
764 assert_equals(controller.desiredSize, 0, 'desiredSize should decrease to 0 aft
er first enqueue'); | |
765 | |
766 return reader.read().then(result1 => { | |
767 assert_object_equals(result1, { value: 'a', done: false }, 'first chunk read
should be correct'); | |
768 | |
769 assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 afte
r the first read'); | |
770 controller.enqueue('b'); | |
771 assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 af
ter the second enqueue'); | |
772 | |
773 return reader.read(); | |
774 }).then(result2 => { | |
775 assert_object_equals(result2, { value: 'b', done: false }, 'second chunk rea
d should be correct'); | |
776 | |
777 assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 afte
r the second read'); | |
778 controller.enqueue('c'); | |
779 assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 af
ter the third enqueue'); | |
780 | |
781 return reader.read(); | |
782 }).then(result3 => { | |
783 assert_object_equals(result3, { value: 'c', done: false }, 'third chunk read
should be correct'); | |
784 | |
785 assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 afte
r the third read'); | |
786 controller.enqueue('d'); | |
787 assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 af
ter the fourth enqueue'); | |
788 }); | |
789 | |
790 }, 'ReadableStream strategies: the default strategy should continue giving desir
edSize of 1 if the chunks are read immediately'); | |
791 | |
792 promise_test(t => { | |
793 | |
794 const randomSource = new RandomPushSource(8); | |
795 | |
796 const rs = new ReadableStream({ | |
797 start(c) { | |
798 assert_equals(typeof c, 'object', 'c should be an object in start'); | |
799 assert_equals(typeof c.enqueue, 'function', 'enqueue should be a function
in start'); | |
800 assert_equals(typeof c.close, 'function', 'close should be a function in s
tart'); | |
801 assert_equals(typeof c.error, 'function', 'error should be a function in s
tart'); | |
802 | |
803 randomSource.ondata = t.step_func(chunk => { | |
804 if (!c.enqueue(chunk) <= 0) { | |
805 randomSource.readStop(); | |
806 } | |
807 }); | |
808 | |
809 randomSource.onend = c.close.bind(c); | |
810 randomSource.onerror = c.error.bind(c); | |
811 }, | |
812 | |
813 pull(c) { | |
814 assert_equals(typeof c, 'object', 'c should be an object in pull'); | |
815 assert_equals(typeof c.enqueue, 'function', 'enqueue should be a function
in pull'); | |
816 assert_equals(typeof c.close, 'function', 'close should be a function in p
ull'); | |
817 | |
818 randomSource.readStart(); | |
819 } | |
820 }); | |
821 | |
822 return readableStreamToArray(rs).then(chunks => { | |
823 assert_equals(chunks.length, 8, '8 chunks should be read'); | |
824 for (const chunk of chunks) { | |
825 assert_equals(chunk.length, 128, 'chunk should have 128 bytes'); | |
826 } | |
827 }); | |
828 | |
829 }, 'ReadableStream integration test: adapting a random push source'); | |
830 | |
831 promise_test(() => { | |
832 | |
833 const rs = sequentialReadableStream(10); | |
834 | |
835 return readableStreamToArray(rs).then(chunks => { | |
836 assert_true(rs.source.closed, 'source should be closed after all chunks are
read'); | |
837 assert_array_equals(chunks, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'the expected 1
0 chunks should be read'); | |
838 }); | |
839 | |
840 }, 'ReadableStream integration test: adapting a sync pull source'); | |
841 | |
842 promise_test(() => { | |
843 | |
844 const rs = sequentialReadableStream(10, { async: true }); | |
845 | |
846 return readableStreamToArray(rs).then(chunks => { | |
847 assert_true(rs.source.closed, 'source should be closed after all chunks are
read'); | |
848 assert_array_equals(chunks, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'the expected 1
0 chunks should be read'); | |
849 }); | |
850 | |
851 }, 'ReadableStream integration test: adapting an async pull source'); | |
852 | |
853 done(); | |
OLD | NEW |