1'use strict'; 2 3// These tests can be run against any readable stream produced by the web platform that meets the given descriptions. 4// For readable stream tests, the factory should return the stream. For reader tests, the factory should return a 5// { stream, reader } object. (You can use this to vary the time at which you acquire a reader.) 6 7self.templatedRSEmpty = (label, factory) => { 8 test(() => {}, 'Running templatedRSEmpty with ' + label); 9 10 test(() => { 11 12 const rs = factory(); 13 14 assert_equals(typeof rs.locked, 'boolean', 'has a boolean locked getter'); 15 assert_equals(typeof rs.cancel, 'function', 'has a cancel method'); 16 assert_equals(typeof rs.getReader, 'function', 'has a getReader method'); 17 assert_equals(typeof rs.pipeThrough, 'function', 'has a pipeThrough method'); 18 assert_equals(typeof rs.pipeTo, 'function', 'has a pipeTo method'); 19 assert_equals(typeof rs.tee, 'function', 'has a tee method'); 20 21 }, label + ': instances have the correct methods and properties'); 22 23 test(() => { 24 const rs = factory(); 25 26 assert_throws_js(TypeError, () => rs.getReader({ mode: '' }), 'empty string mode should throw'); 27 assert_throws_js(TypeError, () => rs.getReader({ mode: null }), 'null mode should throw'); 28 assert_throws_js(TypeError, () => rs.getReader({ mode: 'asdf' }), 'asdf mode should throw'); 29 assert_throws_js(TypeError, () => rs.getReader(5), '5 should throw'); 30 31 // Should not throw 32 rs.getReader(null); 33 34 }, label + ': calling getReader with invalid arguments should throw appropriate errors'); 35}; 36 37self.templatedRSClosed = (label, factory) => { 38 test(() => {}, 'Running templatedRSClosed with ' + label); 39 40 promise_test(() => { 41 42 const rs = factory(); 43 const cancelPromise1 = rs.cancel(); 44 const cancelPromise2 = rs.cancel(); 45 46 assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises'); 47 48 return Promise.all([ 49 cancelPromise1.then(v => assert_equals(v, undefined, 'first cancel() call should fulfill with undefined')), 50 cancelPromise2.then(v => assert_equals(v, undefined, 'second cancel() call should fulfill with undefined')) 51 ]); 52 53 }, label + ': cancel() should return a distinct fulfilled promise each time'); 54 55 test(() => { 56 57 const rs = factory(); 58 assert_false(rs.locked, 'locked getter should return false'); 59 60 }, label + ': locked should be false'); 61 62 test(() => { 63 64 const rs = factory(); 65 rs.getReader(); // getReader() should not throw. 66 67 }, label + ': getReader() should be OK'); 68 69 test(() => { 70 71 const rs = factory(); 72 73 const reader = rs.getReader(); 74 reader.releaseLock(); 75 76 const reader2 = rs.getReader(); // Getting a second reader should not throw. 77 reader2.releaseLock(); 78 79 rs.getReader(); // Getting a third reader should not throw. 80 81 }, label + ': should be able to acquire multiple readers if they are released in succession'); 82 83 test(() => { 84 85 const rs = factory(); 86 87 rs.getReader(); 88 89 assert_throws_js(TypeError, () => rs.getReader(), 'getting a second reader should throw'); 90 assert_throws_js(TypeError, () => rs.getReader(), 'getting a third reader should throw'); 91 92 }, label + ': should not be able to acquire a second reader if we don\'t release the first one'); 93}; 94 95self.templatedRSErrored = (label, factory, error) => { 96 test(() => {}, 'Running templatedRSErrored with ' + label); 97 98 promise_test(t => { 99 100 const rs = factory(); 101 const reader = rs.getReader(); 102 103 return Promise.all([ 104 promise_rejects_exactly(t, error, reader.closed), 105 promise_rejects_exactly(t, error, reader.read()) 106 ]); 107 108 }, label + ': getReader() should return a reader that acts errored'); 109 110 promise_test(t => { 111 112 const rs = factory(); 113 const reader = rs.getReader(); 114 115 return Promise.all([ 116 promise_rejects_exactly(t, error, reader.read()), 117 promise_rejects_exactly(t, error, reader.read()), 118 promise_rejects_exactly(t, error, reader.closed) 119 ]); 120 121 }, label + ': read() twice should give the error each time'); 122 123 test(() => { 124 const rs = factory(); 125 126 assert_false(rs.locked, 'locked getter should return false'); 127 }, label + ': locked should be false'); 128}; 129 130self.templatedRSErroredSyncOnly = (label, factory, error) => { 131 test(() => {}, 'Running templatedRSErroredSyncOnly with ' + label); 132 133 promise_test(t => { 134 135 const rs = factory(); 136 rs.getReader().releaseLock(); 137 const reader = rs.getReader(); // Calling getReader() twice does not throw (the stream is not locked). 138 139 return promise_rejects_exactly(t, error, reader.closed); 140 141 }, label + ': should be able to obtain a second reader, with the correct closed promise'); 142 143 test(() => { 144 145 const rs = factory(); 146 rs.getReader(); 147 148 assert_throws_js(TypeError, () => rs.getReader(), 'getting a second reader should throw a TypeError'); 149 assert_throws_js(TypeError, () => rs.getReader(), 'getting a third reader should throw a TypeError'); 150 151 }, label + ': should not be able to obtain additional readers if we don\'t release the first lock'); 152 153 promise_test(t => { 154 155 const rs = factory(); 156 const cancelPromise1 = rs.cancel(); 157 const cancelPromise2 = rs.cancel(); 158 159 assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises'); 160 161 return Promise.all([ 162 promise_rejects_exactly(t, error, cancelPromise1), 163 promise_rejects_exactly(t, error, cancelPromise2) 164 ]); 165 166 }, label + ': cancel() should return a distinct rejected promise each time'); 167 168 promise_test(t => { 169 170 const rs = factory(); 171 const reader = rs.getReader(); 172 const cancelPromise1 = reader.cancel(); 173 const cancelPromise2 = reader.cancel(); 174 175 assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises'); 176 177 return Promise.all([ 178 promise_rejects_exactly(t, error, cancelPromise1), 179 promise_rejects_exactly(t, error, cancelPromise2) 180 ]); 181 182 }, label + ': reader cancel() should return a distinct rejected promise each time'); 183}; 184 185self.templatedRSEmptyReader = (label, factory) => { 186 test(() => {}, 'Running templatedRSEmptyReader with ' + label); 187 188 test(() => { 189 190 const reader = factory().reader; 191 192 assert_true('closed' in reader, 'has a closed property'); 193 assert_equals(typeof reader.closed.then, 'function', 'closed property is thenable'); 194 195 assert_equals(typeof reader.cancel, 'function', 'has a cancel method'); 196 assert_equals(typeof reader.read, 'function', 'has a read method'); 197 assert_equals(typeof reader.releaseLock, 'function', 'has a releaseLock method'); 198 199 }, label + ': instances have the correct methods and properties'); 200 201 test(() => { 202 203 const stream = factory().stream; 204 205 assert_true(stream.locked, 'locked getter should return true'); 206 207 }, label + ': locked should be true'); 208 209 promise_test(t => { 210 211 const reader = factory().reader; 212 213 reader.read().then( 214 t.unreached_func('read() should not fulfill'), 215 t.unreached_func('read() should not reject') 216 ); 217 218 return delay(500); 219 220 }, label + ': read() should never settle'); 221 222 promise_test(t => { 223 224 const reader = factory().reader; 225 226 reader.read().then( 227 t.unreached_func('read() should not fulfill'), 228 t.unreached_func('read() should not reject') 229 ); 230 231 reader.read().then( 232 t.unreached_func('read() should not fulfill'), 233 t.unreached_func('read() should not reject') 234 ); 235 236 return delay(500); 237 238 }, label + ': two read()s should both never settle'); 239 240 test(() => { 241 242 const reader = factory().reader; 243 assert_not_equals(reader.read(), reader.read(), 'the promises returned should be distinct'); 244 245 }, label + ': read() should return distinct promises each time'); 246 247 test(() => { 248 249 const stream = factory().stream; 250 assert_throws_js(TypeError, () => stream.getReader(), 'stream.getReader() should throw a TypeError'); 251 252 }, label + ': getReader() again on the stream should fail'); 253 254 promise_test(async t => { 255 256 const streamAndReader = factory(); 257 const stream = streamAndReader.stream; 258 const reader = streamAndReader.reader; 259 260 const read1 = reader.read(); 261 const read2 = reader.read(); 262 const closed = reader.closed; 263 264 reader.releaseLock(); 265 266 assert_false(stream.locked, 'the stream should be unlocked'); 267 268 await Promise.all([ 269 promise_rejects_js(t, TypeError, read1, 'first read should reject'), 270 promise_rejects_js(t, TypeError, read2, 'second read should reject'), 271 promise_rejects_js(t, TypeError, closed, 'closed should reject') 272 ]); 273 274 }, label + ': releasing the lock should reject all pending read requests'); 275 276 promise_test(t => { 277 278 const reader = factory().reader; 279 reader.releaseLock(); 280 281 return Promise.all([ 282 promise_rejects_js(t, TypeError, reader.read()), 283 promise_rejects_js(t, TypeError, reader.read()) 284 ]); 285 286 }, label + ': releasing the lock should cause further read() calls to reject with a TypeError'); 287 288 promise_test(t => { 289 290 const reader = factory().reader; 291 292 const closedBefore = reader.closed; 293 reader.releaseLock(); 294 const closedAfter = reader.closed; 295 296 assert_equals(closedBefore, closedAfter, 'the closed promise should not change identity'); 297 298 return promise_rejects_js(t, TypeError, closedBefore); 299 300 }, label + ': releasing the lock should cause closed calls to reject with a TypeError'); 301 302 test(() => { 303 304 const streamAndReader = factory(); 305 const stream = streamAndReader.stream; 306 const reader = streamAndReader.reader; 307 308 reader.releaseLock(); 309 assert_false(stream.locked, 'locked getter should return false'); 310 311 }, label + ': releasing the lock should cause locked to become false'); 312 313 promise_test(() => { 314 315 const reader = factory().reader; 316 reader.cancel(); 317 318 return reader.read().then(r => { 319 assert_object_equals(r, { value: undefined, done: true }, 'read()ing from the reader should give a done result'); 320 }); 321 322 }, label + ': canceling via the reader should cause the reader to act closed'); 323 324 promise_test(t => { 325 326 const stream = factory().stream; 327 return promise_rejects_js(t, TypeError, stream.cancel()); 328 329 }, label + ': canceling via the stream should fail'); 330}; 331 332self.templatedRSClosedReader = (label, factory) => { 333 test(() => {}, 'Running templatedRSClosedReader with ' + label); 334 335 promise_test(() => { 336 337 const reader = factory().reader; 338 339 return reader.read().then(v => { 340 assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly'); 341 }); 342 343 }, label + ': read() should fulfill with { value: undefined, done: true }'); 344 345 promise_test(() => { 346 347 const reader = factory().reader; 348 349 return Promise.all([ 350 reader.read().then(v => { 351 assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly'); 352 }), 353 reader.read().then(v => { 354 assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly'); 355 }) 356 ]); 357 358 }, label + ': read() multiple times should fulfill with { value: undefined, done: true }'); 359 360 promise_test(() => { 361 362 const reader = factory().reader; 363 364 return reader.read().then(() => reader.read()).then(v => { 365 assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly'); 366 }); 367 368 }, label + ': read() should work when used within another read() fulfill callback'); 369 370 promise_test(() => { 371 372 const reader = factory().reader; 373 374 return reader.closed.then(v => assert_equals(v, undefined, 'reader closed should fulfill with undefined')); 375 376 }, label + ': closed should fulfill with undefined'); 377 378 promise_test(t => { 379 380 const reader = factory().reader; 381 382 const closedBefore = reader.closed; 383 reader.releaseLock(); 384 const closedAfter = reader.closed; 385 386 assert_not_equals(closedBefore, closedAfter, 'the closed promise should change identity'); 387 388 return Promise.all([ 389 closedBefore.then(v => assert_equals(v, undefined, 'reader.closed acquired before release should fulfill')), 390 promise_rejects_js(t, TypeError, closedAfter) 391 ]); 392 393 }, label + ': releasing the lock should cause closed to reject and change identity'); 394 395 promise_test(() => { 396 397 const reader = factory().reader; 398 const cancelPromise1 = reader.cancel(); 399 const cancelPromise2 = reader.cancel(); 400 const closedReaderPromise = reader.closed; 401 402 assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises'); 403 assert_not_equals(cancelPromise1, closedReaderPromise, 'cancel() promise 1 should be distinct from reader.closed'); 404 assert_not_equals(cancelPromise2, closedReaderPromise, 'cancel() promise 2 should be distinct from reader.closed'); 405 406 return Promise.all([ 407 cancelPromise1.then(v => assert_equals(v, undefined, 'first cancel() should fulfill with undefined')), 408 cancelPromise2.then(v => assert_equals(v, undefined, 'second cancel() should fulfill with undefined')) 409 ]); 410 411 }, label + ': cancel() should return a distinct fulfilled promise each time'); 412}; 413 414self.templatedRSErroredReader = (label, factory, error) => { 415 test(() => {}, 'Running templatedRSErroredReader with ' + label); 416 417 promise_test(t => { 418 419 const reader = factory().reader; 420 return promise_rejects_exactly(t, error, reader.closed); 421 422 }, label + ': closed should reject with the error'); 423 424 promise_test(t => { 425 426 const reader = factory().reader; 427 const closedBefore = reader.closed; 428 429 return promise_rejects_exactly(t, error, closedBefore).then(() => { 430 reader.releaseLock(); 431 432 const closedAfter = reader.closed; 433 assert_not_equals(closedBefore, closedAfter, 'the closed promise should change identity'); 434 435 return promise_rejects_js(t, TypeError, closedAfter); 436 }); 437 438 }, label + ': releasing the lock should cause closed to reject and change identity'); 439 440 promise_test(t => { 441 442 const reader = factory().reader; 443 return promise_rejects_exactly(t, error, reader.read()); 444 445 }, label + ': read() should reject with the error'); 446}; 447 448self.templatedRSTwoChunksOpenReader = (label, factory, chunks) => { 449 test(() => {}, 'Running templatedRSTwoChunksOpenReader with ' + label); 450 451 promise_test(() => { 452 453 const reader = factory().reader; 454 455 return Promise.all([ 456 reader.read().then(r => { 457 assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct'); 458 }), 459 reader.read().then(r => { 460 assert_object_equals(r, { value: chunks[1], done: false }, 'second result should be correct'); 461 }) 462 ]); 463 464 }, label + ': calling read() twice without waiting will eventually give both chunks (sequential)'); 465 466 promise_test(() => { 467 468 const reader = factory().reader; 469 470 return reader.read().then(r => { 471 assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct'); 472 473 return reader.read().then(r2 => { 474 assert_object_equals(r2, { value: chunks[1], done: false }, 'second result should be correct'); 475 }); 476 }); 477 478 }, label + ': calling read() twice without waiting will eventually give both chunks (nested)'); 479 480 test(() => { 481 482 const reader = factory().reader; 483 assert_not_equals(reader.read(), reader.read(), 'the promises returned should be distinct'); 484 485 }, label + ': read() should return distinct promises each time'); 486 487 promise_test(() => { 488 489 const reader = factory().reader; 490 491 const promise1 = reader.closed.then(v => { 492 assert_equals(v, undefined, 'reader closed should fulfill with undefined'); 493 }); 494 495 const promise2 = reader.read().then(r => { 496 assert_object_equals(r, { value: chunks[0], done: false }, 497 'promise returned before cancellation should fulfill with a chunk'); 498 }); 499 500 reader.cancel(); 501 502 const promise3 = reader.read().then(r => { 503 assert_object_equals(r, { value: undefined, done: true }, 504 'promise returned after cancellation should fulfill with an end-of-stream signal'); 505 }); 506 507 return Promise.all([promise1, promise2, promise3]); 508 509 }, label + ': cancel() after a read() should still give that single read result'); 510}; 511 512self.templatedRSTwoChunksClosedReader = function (label, factory, chunks) { 513 test(() => {}, 'Running templatedRSTwoChunksClosedReader with ' + label); 514 515 promise_test(() => { 516 517 const reader = factory().reader; 518 519 return Promise.all([ 520 reader.read().then(r => { 521 assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct'); 522 }), 523 reader.read().then(r => { 524 assert_object_equals(r, { value: chunks[1], done: false }, 'second result should be correct'); 525 }), 526 reader.read().then(r => { 527 assert_object_equals(r, { value: undefined, done: true }, 'third result should be correct'); 528 }) 529 ]); 530 531 }, label + ': third read(), without waiting, should give { value: undefined, done: true } (sequential)'); 532 533 promise_test(() => { 534 535 const reader = factory().reader; 536 537 return reader.read().then(r => { 538 assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct'); 539 540 return reader.read().then(r2 => { 541 assert_object_equals(r2, { value: chunks[1], done: false }, 'second result should be correct'); 542 543 return reader.read().then(r3 => { 544 assert_object_equals(r3, { value: undefined, done: true }, 'third result should be correct'); 545 }); 546 }); 547 }); 548 549 }, label + ': third read(), without waiting, should give { value: undefined, done: true } (nested)'); 550 551 promise_test(() => { 552 553 const streamAndReader = factory(); 554 const stream = streamAndReader.stream; 555 const reader = streamAndReader.reader; 556 557 assert_true(stream.locked, 'stream should start locked'); 558 559 const promise = reader.closed.then(v => { 560 assert_equals(v, undefined, 'reader closed should fulfill with undefined'); 561 assert_true(stream.locked, 'stream should remain locked'); 562 }); 563 564 reader.read(); 565 reader.read(); 566 567 return promise; 568 569 }, label + 570 ': draining the stream via read() should cause the reader closed promise to fulfill, but locked stays true'); 571 572 promise_test(() => { 573 574 const streamAndReader = factory(); 575 const stream = streamAndReader.stream; 576 const reader = streamAndReader.reader; 577 578 const promise = reader.closed.then(() => { 579 assert_true(stream.locked, 'the stream should start locked'); 580 reader.releaseLock(); // Releasing the lock after reader closed should not throw. 581 assert_false(stream.locked, 'the stream should end unlocked'); 582 }); 583 584 reader.read(); 585 reader.read(); 586 587 return promise; 588 589 }, label + ': releasing the lock after the stream is closed should cause locked to become false'); 590 591 promise_test(t => { 592 593 const reader = factory().reader; 594 595 reader.releaseLock(); 596 597 return Promise.all([ 598 promise_rejects_js(t, TypeError, reader.read()), 599 promise_rejects_js(t, TypeError, reader.read()), 600 promise_rejects_js(t, TypeError, reader.read()) 601 ]); 602 603 }, label + ': releasing the lock should cause further read() calls to reject with a TypeError'); 604 605 promise_test(() => { 606 607 const streamAndReader = factory(); 608 const stream = streamAndReader.stream; 609 const reader = streamAndReader.reader; 610 611 const readerClosed = reader.closed; 612 613 assert_equals(reader.closed, readerClosed, 'accessing reader.closed twice in succession gives the same value'); 614 615 const promise = reader.read().then(() => { 616 assert_equals(reader.closed, readerClosed, 'reader.closed is the same after read() fulfills'); 617 618 reader.releaseLock(); 619 620 assert_equals(reader.closed, readerClosed, 'reader.closed is the same after releasing the lock'); 621 622 const newReader = stream.getReader(); 623 return newReader.read(); 624 }); 625 626 assert_equals(reader.closed, readerClosed, 'reader.closed is the same after calling read()'); 627 628 return promise; 629 630 }, label + ': reader\'s closed property always returns the same promise'); 631}; 632 633self.templatedRSTeeCancel = (label, factory) => { 634 test(() => {}, `Running templatedRSTeeCancel with ${label}`); 635 636 promise_test(async () => { 637 638 const reason1 = new Error('We\'re wanted men.'); 639 const reason2 = new Error('I have the death sentence on twelve systems.'); 640 641 let resolve; 642 const promise = new Promise(r => resolve = r); 643 const rs = factory({ 644 cancel(reason) { 645 assert_array_equals(reason, [reason1, reason2], 646 'the cancel reason should be an array containing those from the branches'); 647 resolve(); 648 } 649 }); 650 651 const [branch1, branch2] = rs.tee(); 652 await Promise.all([ 653 branch1.cancel(reason1), 654 branch2.cancel(reason2), 655 promise 656 ]); 657 658 }, `${label}: canceling both branches should aggregate the cancel reasons into an array`); 659 660 promise_test(async () => { 661 662 const reason1 = new Error('This little one\'s not worth the effort.'); 663 const reason2 = new Error('Come, let me get you something.'); 664 665 let resolve; 666 const promise = new Promise(r => resolve = r); 667 const rs = factory({ 668 cancel(reason) { 669 assert_array_equals(reason, [reason1, reason2], 670 'the cancel reason should be an array containing those from the branches'); 671 resolve(); 672 } 673 }); 674 675 const [branch1, branch2] = rs.tee(); 676 await Promise.all([ 677 branch2.cancel(reason2), 678 branch1.cancel(reason1), 679 promise 680 ]); 681 682 }, `${label}: canceling both branches in reverse order should aggregate the cancel reasons into an array`); 683 684 promise_test(async t => { 685 686 const theError = { name: 'I\'ll be careful.' }; 687 const rs = factory({ 688 cancel() { 689 throw theError; 690 } 691 }); 692 693 const [branch1, branch2] = rs.tee(); 694 await Promise.all([ 695 promise_rejects_exactly(t, theError, branch1.cancel()), 696 promise_rejects_exactly(t, theError, branch2.cancel()) 697 ]); 698 699 }, `${label}: failing to cancel the original stream should cause cancel() to reject on branches`); 700 701 promise_test(async t => { 702 703 const theError = { name: 'You just watch yourself!' }; 704 let controller; 705 const stream = factory({ 706 start(c) { 707 controller = c; 708 } 709 }); 710 711 const [branch1, branch2] = stream.tee(); 712 controller.error(theError); 713 714 await Promise.all([ 715 promise_rejects_exactly(t, theError, branch1.cancel()), 716 promise_rejects_exactly(t, theError, branch2.cancel()) 717 ]); 718 719 }, `${label}: erroring a teed stream should properly handle canceled branches`); 720 721}; 722