from.any.js (17093B)
1 // META: global=window,worker,shadowrealm 2 // META: script=../resources/test-utils.js 3 'use strict'; 4 5 const iterableFactories = [ 6 ['an array of values', () => { 7 return ['a', 'b']; 8 }], 9 10 ['an array of promises', () => { 11 return [ 12 Promise.resolve('a'), 13 Promise.resolve('b') 14 ]; 15 }], 16 17 ['an array iterator', () => { 18 return ['a', 'b'][Symbol.iterator](); 19 }], 20 21 ['a string', () => { 22 // This iterates over the code points of the string. 23 return 'ab'; 24 }], 25 26 ['a Set', () => { 27 return new Set(['a', 'b']); 28 }], 29 30 ['a Set iterator', () => { 31 return new Set(['a', 'b'])[Symbol.iterator](); 32 }], 33 34 ['a sync generator', () => { 35 function* syncGenerator() { 36 yield 'a'; 37 yield 'b'; 38 } 39 40 return syncGenerator(); 41 }], 42 43 ['an async generator', () => { 44 async function* asyncGenerator() { 45 yield 'a'; 46 yield 'b'; 47 } 48 49 return asyncGenerator(); 50 }], 51 52 ['a sync iterable of values', () => { 53 const chunks = ['a', 'b']; 54 const iterator = { 55 next() { 56 return { 57 done: chunks.length === 0, 58 value: chunks.shift() 59 }; 60 } 61 }; 62 const iterable = { 63 [Symbol.iterator]: () => iterator 64 }; 65 return iterable; 66 }], 67 68 ['a sync iterable of promises', () => { 69 const chunks = ['a', 'b']; 70 const iterator = { 71 next() { 72 return chunks.length === 0 ? { done: true } : { 73 done: false, 74 value: Promise.resolve(chunks.shift()) 75 }; 76 } 77 }; 78 const iterable = { 79 [Symbol.iterator]: () => iterator 80 }; 81 return iterable; 82 }], 83 84 ['an async iterable', () => { 85 const chunks = ['a', 'b']; 86 const asyncIterator = { 87 next() { 88 return Promise.resolve({ 89 done: chunks.length === 0, 90 value: chunks.shift() 91 }) 92 } 93 }; 94 const asyncIterable = { 95 [Symbol.asyncIterator]: () => asyncIterator 96 }; 97 return asyncIterable; 98 }], 99 100 ['a ReadableStream', () => { 101 return new ReadableStream({ 102 start(c) { 103 c.enqueue('a'); 104 c.enqueue('b'); 105 c.close(); 106 } 107 }); 108 }], 109 110 ['a ReadableStream async iterator', () => { 111 return new ReadableStream({ 112 start(c) { 113 c.enqueue('a'); 114 c.enqueue('b'); 115 c.close(); 116 } 117 })[Symbol.asyncIterator](); 118 }] 119 ]; 120 121 for (const [label, factory] of iterableFactories) { 122 promise_test(async () => { 123 124 const iterable = factory(); 125 const rs = ReadableStream.from(iterable); 126 assert_equals(rs.constructor, ReadableStream, 'from() should return a ReadableStream'); 127 128 const reader = rs.getReader(); 129 assert_object_equals(await reader.read(), { value: 'a', done: false }, 'first read should be correct'); 130 assert_object_equals(await reader.read(), { value: 'b', done: false }, 'second read should be correct'); 131 assert_object_equals(await reader.read(), { value: undefined, done: true }, 'third read should be done'); 132 await reader.closed; 133 134 }, `ReadableStream.from accepts ${label}`); 135 } 136 137 const badIterables = [ 138 ['null', null], 139 ['undefined', undefined], 140 ['0', 0], 141 ['NaN', NaN], 142 ['true', true], 143 ['{}', {}], 144 ['Object.create(null)', Object.create(null)], 145 ['a function', () => 42], 146 ['a symbol', Symbol()], 147 ['an object with a non-callable @@iterator method', { 148 [Symbol.iterator]: 42 149 }], 150 ['an object with a non-callable @@asyncIterator method', { 151 [Symbol.asyncIterator]: 42 152 }], 153 ['an object with an @@iterator method returning a non-object', { 154 [Symbol.iterator]: () => 42 155 }], 156 ['an object with an @@asyncIterator method returning a non-object', { 157 [Symbol.asyncIterator]: () => 42 158 }], 159 ]; 160 161 for (const [label, iterable] of badIterables) { 162 test(() => { 163 assert_throws_js(TypeError, () => ReadableStream.from(iterable), 'from() should throw a TypeError') 164 }, `ReadableStream.from throws on invalid iterables; specifically ${label}`); 165 } 166 167 test(() => { 168 const theError = new Error('a unique string'); 169 const iterable = { 170 [Symbol.iterator]() { 171 throw theError; 172 } 173 }; 174 175 assert_throws_exactly(theError, () => ReadableStream.from(iterable), 'from() should re-throw the error'); 176 }, `ReadableStream.from re-throws errors from calling the @@iterator method`); 177 178 test(() => { 179 const theError = new Error('a unique string'); 180 const iterable = { 181 [Symbol.asyncIterator]() { 182 throw theError; 183 } 184 }; 185 186 assert_throws_exactly(theError, () => ReadableStream.from(iterable), 'from() should re-throw the error'); 187 }, `ReadableStream.from re-throws errors from calling the @@asyncIterator method`); 188 189 test(t => { 190 const theError = new Error('a unique string'); 191 const iterable = { 192 [Symbol.iterator]: t.unreached_func('@@iterator should not be called'), 193 [Symbol.asyncIterator]() { 194 throw theError; 195 } 196 }; 197 198 assert_throws_exactly(theError, () => ReadableStream.from(iterable), 'from() should re-throw the error'); 199 }, `ReadableStream.from ignores @@iterator if @@asyncIterator exists`); 200 201 test(() => { 202 const theError = new Error('a unique string'); 203 const iterable = { 204 [Symbol.asyncIterator]: null, 205 [Symbol.iterator]() { 206 throw theError 207 } 208 }; 209 210 assert_throws_exactly(theError, () => ReadableStream.from(iterable), 'from() should re-throw the error'); 211 }, `ReadableStream.from ignores a null @@asyncIterator`); 212 213 promise_test(async () => { 214 215 const iterable = { 216 async next() { 217 return { value: undefined, done: true }; 218 }, 219 [Symbol.asyncIterator]: () => iterable 220 }; 221 222 const rs = ReadableStream.from(iterable); 223 const reader = rs.getReader(); 224 225 const read = await reader.read(); 226 assert_object_equals(read, { value: undefined, done: true }, 'first read should be done'); 227 228 await reader.closed; 229 230 }, `ReadableStream.from accepts an empty iterable`); 231 232 promise_test(async t => { 233 234 const theError = new Error('a unique string'); 235 236 const iterable = { 237 async next() { 238 throw theError; 239 }, 240 [Symbol.asyncIterator]: () => iterable 241 }; 242 243 const rs = ReadableStream.from(iterable); 244 const reader = rs.getReader(); 245 246 await Promise.all([ 247 promise_rejects_exactly(t, theError, reader.read()), 248 promise_rejects_exactly(t, theError, reader.closed) 249 ]); 250 251 }, `ReadableStream.from: stream errors when next() rejects`); 252 253 promise_test(async t => { 254 const theError = new Error('a unique string'); 255 256 const iterable = { 257 next() { 258 throw theError; 259 }, 260 [Symbol.asyncIterator]: () => iterable 261 }; 262 263 const rs = ReadableStream.from(iterable); 264 const reader = rs.getReader(); 265 266 await Promise.all([ 267 promise_rejects_exactly(t, theError, reader.read()), 268 promise_rejects_exactly(t, theError, reader.closed) 269 ]); 270 271 }, 'ReadableStream.from: stream errors when next() throws synchronously'); 272 273 promise_test(async t => { 274 275 const iterable = { 276 next() { 277 return 42; // not a promise or an iterator result 278 }, 279 [Symbol.asyncIterator]: () => iterable 280 }; 281 282 const rs = ReadableStream.from(iterable); 283 const reader = rs.getReader(); 284 285 await Promise.all([ 286 promise_rejects_js(t, TypeError, reader.read()), 287 promise_rejects_js(t, TypeError, reader.closed) 288 ]); 289 290 }, 'ReadableStream.from: stream errors when next() returns a non-object'); 291 292 promise_test(async t => { 293 294 const iterable = { 295 next() { 296 return Promise.resolve(42); // not an iterator result 297 }, 298 [Symbol.asyncIterator]: () => iterable 299 }; 300 301 const rs = ReadableStream.from(iterable); 302 const reader = rs.getReader(); 303 304 await Promise.all([ 305 promise_rejects_js(t, TypeError, reader.read()), 306 promise_rejects_js(t, TypeError, reader.closed) 307 ]); 308 309 }, 'ReadableStream.from: stream errors when next() fulfills with a non-object'); 310 311 promise_test(async t => { 312 313 const iterable = { 314 next() { 315 return new Promise(() => {}); 316 }, 317 [Symbol.asyncIterator]: () => iterable 318 }; 319 320 const rs = ReadableStream.from(iterable); 321 const reader = rs.getReader(); 322 323 await Promise.race([ 324 reader.read().then(t.unreached_func('read() should not resolve'), t.unreached_func('read() should not reject')), 325 reader.closed.then(t.unreached_func('closed should not resolve'), t.unreached_func('closed should not reject')), 326 flushAsyncEvents() 327 ]); 328 329 }, 'ReadableStream.from: stream stalls when next() never settles'); 330 331 promise_test(async () => { 332 333 let nextCalls = 0; 334 let nextArgs; 335 const iterable = { 336 async next(...args) { 337 nextCalls += 1; 338 nextArgs = args; 339 return { value: 'a', done: false }; 340 }, 341 [Symbol.asyncIterator]: () => iterable 342 }; 343 344 const rs = ReadableStream.from(iterable); 345 const reader = rs.getReader(); 346 347 await flushAsyncEvents(); 348 assert_equals(nextCalls, 0, 'next() should not be called yet'); 349 350 const read = await reader.read(); 351 assert_object_equals(read, { value: 'a', done: false }, 'first read should be correct'); 352 assert_equals(nextCalls, 1, 'next() should be called after first read()'); 353 assert_array_equals(nextArgs, [], 'next() should be called with no arguments'); 354 355 }, `ReadableStream.from: calls next() after first read()`); 356 357 promise_test(async t => { 358 359 const theError = new Error('a unique string'); 360 361 let returnCalls = 0; 362 let returnArgs; 363 let resolveReturn; 364 const iterable = { 365 next: t.unreached_func('next() should not be called'), 366 throw: t.unreached_func('throw() should not be called'), 367 async return(...args) { 368 returnCalls += 1; 369 returnArgs = args; 370 await new Promise(r => resolveReturn = r); 371 return { done: true }; 372 }, 373 [Symbol.asyncIterator]: () => iterable 374 }; 375 376 const rs = ReadableStream.from(iterable); 377 const reader = rs.getReader(); 378 assert_equals(returnCalls, 0, 'return() should not be called yet'); 379 380 let cancelResolved = false; 381 const cancelPromise = reader.cancel(theError).then(() => { 382 cancelResolved = true; 383 }); 384 385 await flushAsyncEvents(); 386 assert_equals(returnCalls, 1, 'return() should be called'); 387 assert_array_equals(returnArgs, [theError], 'return() should be called with cancel reason'); 388 assert_false(cancelResolved, 'cancel() should not resolve while promise from return() is pending'); 389 390 resolveReturn(); 391 await Promise.all([ 392 cancelPromise, 393 reader.closed 394 ]); 395 396 }, `ReadableStream.from: cancelling the returned stream calls and awaits return()`); 397 398 promise_test(async t => { 399 400 let nextCalls = 0; 401 let returnCalls = 0; 402 403 const iterable = { 404 async next() { 405 nextCalls += 1; 406 return { value: undefined, done: true }; 407 }, 408 throw: t.unreached_func('throw() should not be called'), 409 async return() { 410 returnCalls += 1; 411 }, 412 [Symbol.asyncIterator]: () => iterable 413 }; 414 415 const rs = ReadableStream.from(iterable); 416 const reader = rs.getReader(); 417 418 const read = await reader.read(); 419 assert_object_equals(read, { value: undefined, done: true }, 'first read should be done'); 420 assert_equals(nextCalls, 1, 'next() should be called once'); 421 422 await reader.closed; 423 assert_equals(returnCalls, 0, 'return() should not be called'); 424 425 }, `ReadableStream.from: return() is not called when iterator completes normally`); 426 427 promise_test(async t => { 428 429 const theError = new Error('a unique string'); 430 431 const iterable = { 432 next: t.unreached_func('next() should not be called'), 433 throw: t.unreached_func('throw() should not be called'), 434 // no return method 435 [Symbol.asyncIterator]: () => iterable 436 }; 437 438 const rs = ReadableStream.from(iterable); 439 const reader = rs.getReader(); 440 441 await Promise.all([ 442 reader.cancel(theError), 443 reader.closed 444 ]); 445 446 }, `ReadableStream.from: cancel() resolves when return() method is missing`); 447 448 promise_test(async t => { 449 450 const theError = new Error('a unique string'); 451 452 const iterable = { 453 next: t.unreached_func('next() should not be called'), 454 throw: t.unreached_func('throw() should not be called'), 455 return: 42, 456 [Symbol.asyncIterator]: () => iterable 457 }; 458 459 const rs = ReadableStream.from(iterable); 460 const reader = rs.getReader(); 461 462 await promise_rejects_js(t, TypeError, reader.cancel(theError), 'cancel() should reject with a TypeError'); 463 464 await reader.closed; 465 466 }, `ReadableStream.from: cancel() rejects when return() is not a method`); 467 468 promise_test(async t => { 469 470 const cancelReason = new Error('cancel reason'); 471 const rejectError = new Error('reject error'); 472 473 const iterable = { 474 next: t.unreached_func('next() should not be called'), 475 throw: t.unreached_func('throw() should not be called'), 476 async return() { 477 throw rejectError; 478 }, 479 [Symbol.asyncIterator]: () => iterable 480 }; 481 482 const rs = ReadableStream.from(iterable); 483 const reader = rs.getReader(); 484 485 await promise_rejects_exactly(t, rejectError, reader.cancel(cancelReason), 'cancel() should reject with error from return()'); 486 487 await reader.closed; 488 489 }, `ReadableStream.from: cancel() rejects when return() rejects`); 490 491 promise_test(async t => { 492 493 const cancelReason = new Error('cancel reason'); 494 const rejectError = new Error('reject error'); 495 496 const iterable = { 497 next: t.unreached_func('next() should not be called'), 498 throw: t.unreached_func('throw() should not be called'), 499 return() { 500 throw rejectError; 501 }, 502 [Symbol.asyncIterator]: () => iterable 503 }; 504 505 const rs = ReadableStream.from(iterable); 506 const reader = rs.getReader(); 507 508 await promise_rejects_exactly(t, rejectError, reader.cancel(cancelReason), 'cancel() should reject with error from return()'); 509 510 await reader.closed; 511 512 }, `ReadableStream.from: cancel() rejects when return() throws synchronously`); 513 514 promise_test(async t => { 515 516 const theError = new Error('a unique string'); 517 518 const iterable = { 519 next: t.unreached_func('next() should not be called'), 520 throw: t.unreached_func('throw() should not be called'), 521 async return() { 522 return 42; 523 }, 524 [Symbol.asyncIterator]: () => iterable 525 }; 526 527 const rs = ReadableStream.from(iterable); 528 const reader = rs.getReader(); 529 530 await promise_rejects_js(t, TypeError, reader.cancel(theError), 'cancel() should reject with a TypeError'); 531 532 await reader.closed; 533 534 }, `ReadableStream.from: cancel() rejects when return() fulfills with a non-object`); 535 536 promise_test(async () => { 537 538 let nextCalls = 0; 539 let reader; 540 let values = ['a', 'b', 'c']; 541 542 const iterable = { 543 async next() { 544 nextCalls += 1; 545 if (nextCalls === 1) { 546 reader.read(); 547 } 548 return { value: values.shift(), done: false }; 549 }, 550 [Symbol.asyncIterator]: () => iterable 551 }; 552 553 const rs = ReadableStream.from(iterable); 554 reader = rs.getReader(); 555 556 const read1 = await reader.read(); 557 assert_object_equals(read1, { value: 'a', done: false }, 'first read should be correct'); 558 await flushAsyncEvents(); 559 assert_equals(nextCalls, 2, 'next() should be called two times'); 560 561 const read2 = await reader.read(); 562 assert_object_equals(read2, { value: 'c', done: false }, 'second read should be correct'); 563 assert_equals(nextCalls, 3, 'next() should be called three times'); 564 565 }, `ReadableStream.from: reader.read() inside next()`); 566 567 promise_test(async () => { 568 569 let nextCalls = 0; 570 let returnCalls = 0; 571 let reader; 572 573 const iterable = { 574 async next() { 575 nextCalls++; 576 await reader.cancel(); 577 assert_equals(returnCalls, 1, 'return() should be called once'); 578 return { value: 'something else', done: false }; 579 }, 580 async return() { 581 returnCalls++; 582 }, 583 [Symbol.asyncIterator]: () => iterable 584 }; 585 586 const rs = ReadableStream.from(iterable); 587 reader = rs.getReader(); 588 589 const read = await reader.read(); 590 assert_object_equals(read, { value: undefined, done: true }, 'first read should be done'); 591 assert_equals(nextCalls, 1, 'next() should be called once'); 592 593 await reader.closed; 594 595 }, `ReadableStream.from: reader.cancel() inside next()`); 596 597 promise_test(async t => { 598 599 let returnCalls = 0; 600 let reader; 601 602 const iterable = { 603 next: t.unreached_func('next() should not be called'), 604 async return() { 605 returnCalls++; 606 await reader.cancel(); 607 return { done: true }; 608 }, 609 [Symbol.asyncIterator]: () => iterable 610 }; 611 612 const rs = ReadableStream.from(iterable); 613 reader = rs.getReader(); 614 615 await reader.cancel(); 616 assert_equals(returnCalls, 1, 'return() should be called once'); 617 618 await reader.closed; 619 620 }, `ReadableStream.from: reader.cancel() inside return()`); 621 622 promise_test(async t => { 623 624 let array = ['a', 'b']; 625 626 const rs = ReadableStream.from(array); 627 const reader = rs.getReader(); 628 629 const read1 = await reader.read(); 630 assert_object_equals(read1, { value: 'a', done: false }, 'first read should be correct'); 631 const read2 = await reader.read(); 632 assert_object_equals(read2, { value: 'b', done: false }, 'second read should be correct'); 633 634 array.push('c'); 635 636 const read3 = await reader.read(); 637 assert_object_equals(read3, { value: 'c', done: false }, 'third read after push() should be correct'); 638 const read4 = await reader.read(); 639 assert_object_equals(read4, { value: undefined, done: true }, 'fourth read should be done'); 640 641 await reader.closed; 642 643 }, `ReadableStream.from(array), push() to array while reading`);