tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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`);