| 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 |