tor-browser

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

test_async_iterable.html (13088B)


      1 <!-- Any copyright is dedicated to the Public Domain.
      2 - http://creativecommons.org/publicdomain/zero/1.0/ -->
      3 <!DOCTYPE HTML>
      4 <html>
      5  <head>
      6    <title>Test Async Iterable Interface</title>
      7    <script src="/tests/SimpleTest/SimpleTest.js"></script>
      8    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
      9  </head>
     10  <body>
     11    <script class="testbody" type="application/javascript">
     12 
     13 add_task(async function init() {
     14  await SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]});
     15 });
     16 
     17 const singleValues = Array(10).fill(0).map((_, i) => i * 9 % 7);
     18 
     19 async function check_single_result_values(values, multiplier = 1) {
     20  is(values.length, 10, `AsyncIterableSingle: should return 10 elements`);
     21  for (let i = 0; i < 10; i++) {
     22    let expected = singleValues[i] * multiplier;
     23    is(values[i], expected,
     24      `AsyncIterableSingle: should be ${expected}, get ${values[i]}`);
     25  }
     26 }
     27 
     28 async function check_single_result(itr, multiplier = 1) {
     29  let values = [];
     30  for await (let v of itr) {
     31    values.push(v);
     32  }
     33  check_single_result_values(values, multiplier);
     34 }
     35 
     36 async function test_data_single() {
     37  info(`AsyncIterableSingle: Testing simple iterable creation and functionality`);
     38 
     39  // eslint-disable-next-line no-undef
     40  let itr = new TestInterfaceAsyncIterableSingle({ failToInit: true });
     41  let initFailed = false;
     42  try {
     43    itr.values();
     44  } catch (e) {
     45    initFailed = true;
     46  }
     47  ok(initFailed,
     48     "AsyncIterableSingle: A failure in asynchronous iterator initialization " +
     49     "steps should propagate to the caller of the asynchronous iterator's " +
     50     "constructor.");
     51 
     52  // eslint-disable-next-line no-undef
     53  itr = new TestInterfaceAsyncIterableSingle();
     54  is(itr.values, itr[Symbol.asyncIterator],
     55    `AsyncIterableSingle: Should be using @@asyncIterator for 'values'`);
     56 
     57  await check_single_result(itr);
     58  await check_single_result(itr.values());
     59 
     60  // eslint-disable-next-line no-undef
     61  itr = new TestInterfaceAsyncIterableSingleWithArgs();
     62  is(itr.values, itr[Symbol.asyncIterator],
     63    `AsyncIterableSingleWithArgs: Should be using @@asyncIterator for 'values'`);
     64 
     65  await check_single_result(itr, 1);
     66  await check_single_result(itr.values({ multiplier: 2 }), 2);
     67 
     68  // eslint-disable-next-line no-undef
     69  itr = new TestInterfaceAsyncIterableSingle();
     70  let itrValues = itr.values();
     71  let values = [];
     72  for (let i = 0; i < 10; ++i) {
     73    values.push(itrValues.next());
     74  }
     75  check_single_result_values(await Promise.all(values).then(v => v.map(w => w.value)));
     76 
     77  // Test that there is only one ongoing promise at a time.
     78  // Async iterables return a promise that is then resolved with the iterator
     79  // value. We create an array of unresolved promises here, one promise for
     80  // every result that we expect from the iterator. We pass that array of
     81  // promises to the .value() method of the
     82  // TestInterfaceAsyncIterableSingleWithArgs, and it will chain the resolving
     83  // of each resulting iterator value on the corresponding promise from this
     84  // array. We then resolve the promises in the array one by one in reverse
     85  // order. This tries to make sure that the iterator always resolves the
     86  // promises in the order of iteration.
     87  let unblockers = [];
     88  let blockingPromises = [];
     89  for (let i = 0; i < 10; ++i) {
     90    let unblocker;
     91    let promise = new Promise((resolve) => {
     92      unblocker = resolve;
     93    });
     94    unblockers.push(unblocker);
     95    blockingPromises.push(promise);
     96  }
     97 
     98  // eslint-disable-next-line no-undef
     99  itr = new TestInterfaceAsyncIterableSingleWithArgs();
    100  itrValues = itr.values({ blockingPromises });
    101  values = [];
    102  for (let i = 0; i < 10; ++i) {
    103    values.push(itrValues.next());
    104  }
    105  unblockers.reverse();
    106  for (let unblocker of unblockers) {
    107    unblocker();
    108  }
    109 
    110  check_single_result_values(await Promise.all(values).then(v => v.map(w => w.value)));
    111 
    112  // eslint-disable-next-line no-undef
    113  itr = new TestInterfaceAsyncIterableSingleWithArgs();
    114 
    115  let callCount = itr.returnCallCount;
    116 
    117  let i = 0;
    118  for await (let v of itr) {
    119    if (++i > 1) {
    120      break;
    121    }
    122    values.push(v);
    123  }
    124 
    125  is(itr.returnCallCount, callCount + 1,
    126     `AsyncIterableSingle: breaking out of for-await-of loop should call "return"`);
    127  is(itr.returnLastCalledWith, undefined,
    128     `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm should be called with the argument that was passed in.`);
    129 
    130  // eslint-disable-next-line no-undef
    131  itr = new TestInterfaceAsyncIterableSingleWithArgs();
    132 
    133  async function * yieldFromIterator () {
    134    yield * itr
    135  }
    136 
    137  let yieldingIterator = yieldFromIterator();
    138 
    139  let result = await yieldingIterator.next();
    140  is(result.value, singleValues[0],
    141     `AsyncIterableSingle: should be ${singleValues[0]}, get ${result.value}`);
    142  result = await yieldingIterator.next();
    143  is(result.value, singleValues[1],
    144     `AsyncIterableSingle: should be ${singleValues[1]}, get ${result.value}`);
    145 
    146  result = await yieldingIterator.return("abcd");
    147  is(typeof result, "object",
    148     `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
    149  is(result.done, true,
    150     `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
    151  is(result.value, "abcd",
    152     `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
    153  is(itr.returnLastCalledWith, "abcd",
    154     `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm should be called with the argument that was passed in.`);
    155 
    156  result = await yieldingIterator.return("efgh");
    157  is(typeof result, "object",
    158     `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
    159  is(result.done, true,
    160     `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
    161  is(result.value, "efgh",
    162     `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
    163  is(itr.returnLastCalledWith, "abcd",
    164     `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm shouldn't be called if the iterator's 'is finished' flag is true already.`);
    165 
    166  // eslint-disable-next-line no-undef
    167  itr = new TestInterfaceAsyncIterableSingleWithArgs();
    168  itrValues = itr.values({ failNextAfter: 1 });
    169  await itrValues.next().then(({ value, done }) => {
    170    is(value, singleValues[0], "First value is correct");
    171    ok(!done, "Expecting more values");
    172    return itrValues.next();
    173  }).then(() => {
    174    ok(false, "Second call to next() should convert failure to a rejected promise.");
    175    return itrValues.next();
    176  }).catch(() => {
    177    ok(true, "Second call to next() should convert failure to a rejected promise.");
    178    return itrValues.next();
    179  }).then(({ done }) => {
    180    ok(done, "An earlier failure in next() should set the async iterator's 'is finished' flag to true.");
    181  }).catch(() => {
    182    ok(false, "An earlier failure in next() shouldn't cause subsequent calls to return a rejected promise.");
    183  });
    184 
    185  // eslint-disable-next-line no-undef
    186  itr = new TestInterfaceAsyncIterableSingleWithArgs();
    187  itrValues = itr.values({ throwFromNext: true });
    188  await itrValues.next().then(() => {
    189    ok(false, "Should have rejected from the exception");
    190  }).catch(() => {
    191    ok(true, "Should have rejected from the exception");
    192  });
    193 
    194  // eslint-disable-next-line no-undef
    195  itr = new TestInterfaceAsyncIterableSingleWithArgs();
    196  itrValues = itr.values({ throwFromReturn: () => { throw new DOMException("Throw from return", "InvalidStateError"); } });
    197  await itrValues.return().then(() => {
    198    ok(false, "Should have rejected from the exception");
    199  }).catch(() => {
    200    ok(true, "Should have rejected from the exception");
    201  });
    202 }
    203 
    204 async function test_data_double() {
    205  info(`AsyncIterableDouble: Testing simple iterable creation and functionality`);
    206 
    207  // eslint-disable-next-line no-undef
    208  let itr = new TestInterfaceAsyncIterableDouble();
    209  is(itr.entries, itr[Symbol.asyncIterator],
    210    `AsyncIterableDouble: Should be using @@asyncIterator for 'entries'`);
    211 
    212  let elements = [["a", "b"], ["c", "d"], ["e", "f"]];
    213  let key_itr = itr.keys();
    214  let value_itr = itr.values();
    215  let entries_itr = itr.entries();
    216  let key = await key_itr.next();
    217  let value = await value_itr.next();
    218  let entry = await entries_itr.next();
    219  for (let i = 0; i < 3; ++i) {
    220    is(key.value, elements[i][0], `AsyncIterableDouble: Key.value should be ${elements[i][0]}, got ${key.value}`);
    221    is(key.done, false, `AsyncIterableDouble: Key.done should be false, got ${key.done}`);
    222    is(value.value, elements[i][1], `AsyncIterableDouble: Value.value should be ${elements[i][1]}, got ${value.value}`);
    223    is(value.done, false, `AsyncIterableDouble: Value.done should be false, got ${value.done}`);
    224    is(entry.value[0], elements[i][0], `AsyncIterableDouble: Entry.value[0] should be ${elements[i][0]}, got ${entry.value[0]}`);
    225    is(entry.value[1], elements[i][1], `AsyncIterableDouble: Entry.value[1] should be ${elements[i][1]}, got ${entry.value[1]}`);
    226    is(entry.done, false, `AsyncIterableDouble: Entry.done should be false, got ${entry.done}`);
    227 
    228    key = await key_itr.next();
    229    value = await value_itr.next();
    230    entry = await entries_itr.next();
    231  }
    232  is(key.value, undefined, `AsyncIterableDouble: Key.value should be ${undefined}, got ${key.value}`);
    233  is(key.done, true, `AsyncIterableDouble: Key.done should be true, got ${key.done}`);
    234  is(value.value, undefined, `AsyncIterableDouble: Value.value should be ${undefined}, got ${value.value}`);
    235  is(value.done, true, `AsyncIterableDouble: Value.done should be true, got ${value.done}`);
    236  is(entry.value, undefined, `AsyncIterableDouble: Entry.value should be ${undefined}, got ${entry.value}`);
    237  is(entry.done, true, `AsyncIterableDouble: Entry.done should be true, got ${entry.done}`);
    238 
    239  let idx = 0;
    240  for await (let [itrkey, itrvalue] of itr) {
    241    is(itrkey, elements[idx][0], `AsyncIterableDouble: Looping at ${idx} should have key ${elements[idx][0]}, got ${key}`);
    242    is(itrvalue, elements[idx][1], `AsyncIterableDouble: Looping at ${idx} should have value ${elements[idx][1]}, got ${value}`);
    243    ++idx;
    244  }
    245  is(idx, 3, `AsyncIterableDouble: Should have 3 loops of for-await-of, got ${idx}`);
    246 }
    247 
    248 async function test_data_double_union() {
    249  info(`AsyncIterableDoubleUnion: Testing simple iterable creation and functionality`);
    250 
    251  // eslint-disable-next-line no-undef
    252  let itr = new TestInterfaceAsyncIterableDoubleUnion();
    253  is(itr.entries, itr[Symbol.asyncIterator],
    254    `AsyncIterableDoubleUnion: Should be using @@asyncIterator for 'entries'`);
    255 
    256  let elements = [["long", 1], ["string", "a"]];
    257  let key_itr = itr.keys();
    258  let value_itr = itr.values();
    259  let entries_itr = itr.entries();
    260  let key = await key_itr.next();
    261  let value = await value_itr.next();
    262  let entry = await entries_itr.next();
    263  for (let i = 0; i < 2; ++i) {
    264    is(key.value, elements[i][0], `AsyncIterableDoubleUnion: Key.value should be ${elements[i][0]}, got ${key.value}`);
    265    is(key.done, false, `AsyncIterableDoubleUnion: Key.done should be false, got ${key.done}`);
    266    is(value.value, elements[i][1], `AsyncIterableDoubleUnion: Value.value should be ${elements[i][1]}, got ${value.value}`);
    267    is(value.done, false, `AsyncIterableDoubleUnion: Value.done should be false, got ${value.done}`);
    268    is(entry.value[0], elements[i][0], `AsyncIterableDoubleUnion: Entry.value[0] should be ${elements[i][0]}, got ${entry.value[0]}`);
    269    is(entry.value[1], elements[i][1], `AsyncIterableDoubleUnion: Entry.value[1] should be ${elements[i][1]}, got ${entry.value[1]}`);
    270    is(entry.done, false, `AsyncIterableDoubleUnion: Entry.done should be false, got ${entry.done}`);
    271 
    272    key = await key_itr.next();
    273    value = await value_itr.next();
    274    entry = await entries_itr.next();
    275  }
    276  is(key.value, undefined, `AsyncIterableDoubleUnion: Key.value should be ${undefined}, got ${key.value}`);
    277  is(key.done, true, `AsyncIterableDoubleUnion: Key.done should be true, got ${key.done}`);
    278  is(value.value, undefined, `AsyncIterableDoubleUnion: Value.value should be ${undefined}, got ${value.value}`);
    279  is(value.done, true, `AsyncIterableDoubleUnion: Value.done should be true, got ${value.done}`);
    280  is(entry.value, undefined, `AsyncIterableDoubleUnion: Entry.value should be ${undefined}, got ${entry.value}`);
    281  is(entry.done, true, `AsyncIterableDoubleUnion: Entry.done should be true, got ${entry.done}`);
    282 
    283  let idx = 0;
    284  for await (let [itrkey, itrvalue] of itr) {
    285    is(itrkey, elements[idx][0], `AsyncIterableDoubleUnion: Looping at ${idx} should have key ${elements[idx][0]}, got ${key}`);
    286    is(itrvalue, elements[idx][1], `AsyncIterableDoubleUnion: Looping at ${idx} should have value ${elements[idx][1]}, got ${value}`);
    287    ++idx;
    288  }
    289  is(idx, 2, `AsyncIterableDoubleUnion: Should have 2 loops of for-await-of, got ${idx}`);
    290 }
    291 
    292 add_task(async function do_tests() {
    293  await test_data_single();
    294  await test_data_double();
    295  await test_data_double_union();
    296 });
    297 
    298    </script>
    299  </body>
    300 </html>