tor-browser

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

error-propagation-backward.any.js (19010B)


      1 // META: global=window,worker,shadowrealm
      2 // META: script=../resources/test-utils.js
      3 // META: script=../resources/recording-streams.js
      4 'use strict';
      5 
      6 const error1 = new Error('error1!');
      7 error1.name = 'error1';
      8 
      9 const error2 = new Error('error2!');
     10 error2.name = 'error2';
     11 
     12 promise_test(t => {
     13 
     14  const rs = recordingReadableStream();
     15 
     16  const ws = recordingWritableStream({
     17    start() {
     18      return Promise.reject(error1);
     19    }
     20  });
     21 
     22  return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error')
     23    .then(() => {
     24      assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
     25      assert_array_equals(ws.events, []);
     26    });
     27 
     28 }, 'Errors must be propagated backward: starts errored; preventCancel omitted; fulfilled cancel promise');
     29 
     30 promise_test(t => {
     31 
     32  const rs = recordingReadableStream();
     33 
     34  const ws = recordingWritableStream({
     35    write() {
     36      return Promise.reject(error1);
     37    }
     38  });
     39 
     40  const writer = ws.getWriter();
     41 
     42  return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
     43    .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
     44    .then(() => {
     45      writer.releaseLock();
     46 
     47      return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the write error')
     48        .then(() => {
     49          assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
     50          assert_array_equals(ws.events, ['write', 'Hello']);
     51        });
     52    });
     53 
     54 }, 'Errors must be propagated backward: becomes errored before piping due to write; preventCancel omitted; ' +
     55   'fulfilled cancel promise');
     56 
     57 promise_test(t => {
     58 
     59  const rs = recordingReadableStream({
     60    cancel() {
     61      throw error2;
     62    }
     63  });
     64 
     65  const ws = recordingWritableStream({
     66    write() {
     67      return Promise.reject(error1);
     68    }
     69  });
     70 
     71  const writer = ws.getWriter();
     72 
     73  return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
     74    .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
     75    .then(() => {
     76      writer.releaseLock();
     77 
     78      return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error')
     79        .then(() => {
     80          assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
     81          assert_array_equals(ws.events, ['write', 'Hello']);
     82        });
     83    });
     84 
     85 }, 'Errors must be propagated backward: becomes errored before piping due to write; preventCancel omitted; rejected ' +
     86   'cancel promise');
     87 
     88 for (const falsy of [undefined, null, false, +0, -0, NaN, '']) {
     89  const stringVersion = Object.is(falsy, -0) ? '-0' : String(falsy);
     90 
     91  promise_test(t => {
     92 
     93    const rs = recordingReadableStream();
     94 
     95    const ws = recordingWritableStream({
     96      write() {
     97        return Promise.reject(error1);
     98      }
     99    });
    100 
    101    const writer = ws.getWriter();
    102 
    103    return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
    104      .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
    105      .then(() => {
    106        writer.releaseLock();
    107 
    108        return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: falsy }),
    109                               'pipeTo must reject with the write error')
    110          .then(() => {
    111            assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
    112            assert_array_equals(ws.events, ['write', 'Hello']);
    113          });
    114      });
    115 
    116  }, `Errors must be propagated backward: becomes errored before piping due to write; preventCancel = ` +
    117     `${stringVersion} (falsy); fulfilled cancel promise`);
    118 }
    119 
    120 for (const truthy of [true, 'a', 1, Symbol(), { }]) {
    121  promise_test(t => {
    122 
    123    const rs = recordingReadableStream();
    124 
    125    const ws = recordingWritableStream({
    126      write() {
    127        return Promise.reject(error1);
    128      }
    129    });
    130 
    131    const writer = ws.getWriter();
    132 
    133    return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
    134      .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
    135      .then(() => {
    136        writer.releaseLock();
    137 
    138        return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: truthy }),
    139                               'pipeTo must reject with the write error')
    140          .then(() => {
    141            assert_array_equals(rs.eventsWithoutPulls, []);
    142            assert_array_equals(ws.events, ['write', 'Hello']);
    143          });
    144      });
    145 
    146  }, `Errors must be propagated backward: becomes errored before piping due to write; preventCancel = ` +
    147     `${String(truthy)} (truthy)`);
    148 }
    149 
    150 promise_test(t => {
    151 
    152  const rs = recordingReadableStream();
    153 
    154  const ws = recordingWritableStream({
    155    write() {
    156      return Promise.reject(error1);
    157    }
    158  });
    159 
    160  const writer = ws.getWriter();
    161 
    162  return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
    163    .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
    164    .then(() => {
    165      writer.releaseLock();
    166 
    167      return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true, preventAbort: true }),
    168                             'pipeTo must reject with the write error')
    169        .then(() => {
    170          assert_array_equals(rs.eventsWithoutPulls, []);
    171          assert_array_equals(ws.events, ['write', 'Hello']);
    172        });
    173    });
    174 
    175 }, 'Errors must be propagated backward: becomes errored before piping due to write, preventCancel = true; ' +
    176   'preventAbort = true');
    177 
    178 promise_test(t => {
    179 
    180  const rs = recordingReadableStream();
    181 
    182  const ws = recordingWritableStream({
    183    write() {
    184      return Promise.reject(error1);
    185    }
    186  });
    187 
    188  const writer = ws.getWriter();
    189 
    190  return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error')
    191    .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error'))
    192    .then(() => {
    193      writer.releaseLock();
    194 
    195      return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true, preventAbort: true, preventClose: true }),
    196                             'pipeTo must reject with the write error')
    197        .then(() => {
    198          assert_array_equals(rs.eventsWithoutPulls, []);
    199          assert_array_equals(ws.events, ['write', 'Hello']);
    200        });
    201    });
    202 
    203 }, 'Errors must be propagated backward: becomes errored before piping due to write; preventCancel = true, ' +
    204   'preventAbort = true, preventClose = true');
    205 
    206 promise_test(t => {
    207 
    208  const rs = recordingReadableStream({
    209    start(controller) {
    210      controller.enqueue('Hello');
    211    }
    212  });
    213 
    214  const ws = recordingWritableStream({
    215    write() {
    216      throw error1;
    217    }
    218  });
    219 
    220  return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => {
    221    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
    222    assert_array_equals(ws.events, ['write', 'Hello']);
    223  });
    224 
    225 }, 'Errors must be propagated backward: becomes errored during piping due to write; preventCancel omitted; fulfilled ' +
    226   'cancel promise');
    227 
    228 promise_test(t => {
    229 
    230  const rs = recordingReadableStream({
    231    start(controller) {
    232      controller.enqueue('Hello');
    233    },
    234    cancel() {
    235      throw error2;
    236    }
    237  });
    238 
    239  const ws = recordingWritableStream({
    240    write() {
    241      throw error1;
    242    }
    243  });
    244 
    245  return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error').then(() => {
    246    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
    247    assert_array_equals(ws.events, ['write', 'Hello']);
    248  });
    249 
    250 }, 'Errors must be propagated backward: becomes errored during piping due to write; preventCancel omitted; rejected ' +
    251   'cancel promise');
    252 
    253 promise_test(t => {
    254 
    255  const rs = recordingReadableStream({
    256    start(controller) {
    257      controller.enqueue('Hello');
    258    }
    259  });
    260 
    261  const ws = recordingWritableStream({
    262    write() {
    263      throw error1;
    264    }
    265  });
    266 
    267  return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 'pipeTo must reject with the same error')
    268  .then(() => {
    269    assert_array_equals(rs.eventsWithoutPulls, []);
    270    assert_array_equals(ws.events, ['write', 'Hello']);
    271  });
    272 
    273 }, 'Errors must be propagated backward: becomes errored during piping due to write; preventCancel = true');
    274 
    275 promise_test(t => {
    276 
    277  const rs = recordingReadableStream({
    278    start(controller) {
    279      controller.enqueue('a');
    280      controller.enqueue('b');
    281      controller.enqueue('c');
    282    }
    283  });
    284 
    285  const ws = recordingWritableStream({
    286    write() {
    287      if (ws.events.length > 2) {
    288        return delay(0).then(() => {
    289          throw error1;
    290        });
    291      }
    292      return undefined;
    293    }
    294  });
    295 
    296  return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => {
    297    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
    298    assert_array_equals(ws.events, ['write', 'a', 'write', 'b']);
    299  });
    300 
    301 }, 'Errors must be propagated backward: becomes errored during piping due to write, but async; preventCancel = ' +
    302   'false; fulfilled cancel promise');
    303 
    304 promise_test(t => {
    305 
    306  const rs = recordingReadableStream({
    307    start(controller) {
    308      controller.enqueue('a');
    309      controller.enqueue('b');
    310      controller.enqueue('c');
    311    },
    312    cancel() {
    313      throw error2;
    314    }
    315  });
    316 
    317  const ws = recordingWritableStream({
    318    write() {
    319      if (ws.events.length > 2) {
    320        return delay(0).then(() => {
    321          throw error1;
    322        });
    323      }
    324      return undefined;
    325    }
    326  });
    327 
    328  return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error').then(() => {
    329    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
    330    assert_array_equals(ws.events, ['write', 'a', 'write', 'b']);
    331  });
    332 
    333 }, 'Errors must be propagated backward: becomes errored during piping due to write, but async; preventCancel = ' +
    334   'false; rejected cancel promise');
    335 
    336 promise_test(t => {
    337 
    338  const rs = recordingReadableStream({
    339    start(controller) {
    340      controller.enqueue('a');
    341      controller.enqueue('b');
    342      controller.enqueue('c');
    343    }
    344  });
    345 
    346  const ws = recordingWritableStream({
    347    write() {
    348      if (ws.events.length > 2) {
    349        return delay(0).then(() => {
    350          throw error1;
    351        });
    352      }
    353      return undefined;
    354    }
    355  });
    356 
    357  return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 'pipeTo must reject with the same error')
    358  .then(() => {
    359    assert_array_equals(rs.eventsWithoutPulls, []);
    360    assert_array_equals(ws.events, ['write', 'a', 'write', 'b']);
    361  });
    362 
    363 }, 'Errors must be propagated backward: becomes errored during piping due to write, but async; preventCancel = true');
    364 
    365 promise_test(t => {
    366 
    367  const rs = recordingReadableStream();
    368 
    369  const ws = recordingWritableStream();
    370 
    371  const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
    372 
    373  t.step_timeout(() => ws.controller.error(error1), 10);
    374 
    375  return pipePromise.then(() => {
    376    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
    377    assert_array_equals(ws.events, []);
    378  });
    379 
    380 }, 'Errors must be propagated backward: becomes errored after piping; preventCancel omitted; fulfilled cancel promise');
    381 
    382 promise_test(t => {
    383 
    384  const rs = recordingReadableStream({
    385    cancel() {
    386      throw error2;
    387    }
    388  });
    389 
    390  const ws = recordingWritableStream();
    391 
    392  const pipePromise = promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error');
    393 
    394  t.step_timeout(() => ws.controller.error(error1), 10);
    395 
    396  return pipePromise.then(() => {
    397    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
    398    assert_array_equals(ws.events, []);
    399  });
    400 
    401 }, 'Errors must be propagated backward: becomes errored after piping; preventCancel omitted; rejected cancel promise');
    402 
    403 promise_test(t => {
    404 
    405  const rs = recordingReadableStream();
    406 
    407  const ws = recordingWritableStream();
    408 
    409  const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }),
    410                                              'pipeTo must reject with the same error');
    411 
    412  t.step_timeout(() => ws.controller.error(error1), 10);
    413 
    414  return pipePromise.then(() => {
    415    assert_array_equals(rs.eventsWithoutPulls, []);
    416    assert_array_equals(ws.events, []);
    417  });
    418 
    419 }, 'Errors must be propagated backward: becomes errored after piping; preventCancel = true');
    420 
    421 promise_test(t => {
    422 
    423  const rs = recordingReadableStream({
    424    start(controller) {
    425      controller.enqueue('a');
    426      controller.enqueue('b');
    427      controller.enqueue('c');
    428      controller.close();
    429    }
    430  });
    431 
    432  const ws = recordingWritableStream({
    433    write(chunk) {
    434      if (chunk === 'c') {
    435        return Promise.reject(error1);
    436      }
    437      return undefined;
    438    }
    439  });
    440 
    441  return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => {
    442    assert_array_equals(rs.eventsWithoutPulls, []);
    443    assert_array_equals(ws.events, ['write', 'a', 'write', 'b', 'write', 'c']);
    444  });
    445 
    446 }, 'Errors must be propagated backward: becomes errored after piping due to last write; source is closed; ' +
    447   'preventCancel omitted (but cancel is never called)');
    448 
    449 promise_test(t => {
    450 
    451  const rs = recordingReadableStream({
    452    start(controller) {
    453      controller.enqueue('a');
    454      controller.enqueue('b');
    455      controller.enqueue('c');
    456      controller.close();
    457    }
    458  });
    459 
    460  const ws = recordingWritableStream({
    461    write(chunk) {
    462      if (chunk === 'c') {
    463        return Promise.reject(error1);
    464      }
    465      return undefined;
    466    }
    467  });
    468 
    469  return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 'pipeTo must reject with the same error')
    470    .then(() => {
    471      assert_array_equals(rs.eventsWithoutPulls, []);
    472      assert_array_equals(ws.events, ['write', 'a', 'write', 'b', 'write', 'c']);
    473    });
    474 
    475 }, 'Errors must be propagated backward: becomes errored after piping due to last write; source is closed; ' +
    476   'preventCancel = true');
    477 
    478 promise_test(t => {
    479 
    480  const rs = recordingReadableStream();
    481 
    482  const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
    483 
    484  const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
    485 
    486  t.step_timeout(() => ws.controller.error(error1), 10);
    487 
    488  return pipePromise.then(() => {
    489    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
    490    assert_array_equals(ws.events, []);
    491  });
    492 
    493 }, 'Errors must be propagated backward: becomes errored after piping; dest never desires chunks; preventCancel = ' +
    494   'false; fulfilled cancel promise');
    495 
    496 promise_test(t => {
    497 
    498  const rs = recordingReadableStream({
    499    cancel() {
    500      throw error2;
    501    }
    502  });
    503 
    504  const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
    505 
    506  const pipePromise = promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error');
    507 
    508  t.step_timeout(() => ws.controller.error(error1), 10);
    509 
    510  return pipePromise.then(() => {
    511    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
    512    assert_array_equals(ws.events, []);
    513  });
    514 
    515 }, 'Errors must be propagated backward: becomes errored after piping; dest never desires chunks; preventCancel = ' +
    516   'false; rejected cancel promise');
    517 
    518 promise_test(t => {
    519 
    520  const rs = recordingReadableStream();
    521 
    522  const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
    523 
    524  const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }),
    525                                              'pipeTo must reject with the same error');
    526 
    527  t.step_timeout(() => ws.controller.error(error1), 10);
    528 
    529  return pipePromise.then(() => {
    530    assert_array_equals(rs.eventsWithoutPulls, []);
    531    assert_array_equals(ws.events, []);
    532  });
    533 
    534 }, 'Errors must be propagated backward: becomes errored after piping; dest never desires chunks; preventCancel = ' +
    535   'true');
    536 
    537 promise_test(() => {
    538 
    539  const rs = recordingReadableStream();
    540 
    541  const ws = recordingWritableStream();
    542 
    543  ws.abort(error1);
    544 
    545  return rs.pipeTo(ws).then(
    546    () => assert_unreached('the promise must not fulfill'),
    547    err => {
    548      assert_equals(err, error1, 'the promise must reject with error1');
    549 
    550      assert_array_equals(rs.eventsWithoutPulls, ['cancel', err]);
    551      assert_array_equals(ws.events, ['abort', error1]);
    552    }
    553  );
    554 
    555 }, 'Errors must be propagated backward: becomes errored before piping via abort; preventCancel omitted; fulfilled ' +
    556   'cancel promise');
    557 
    558 promise_test(t => {
    559 
    560  const rs = recordingReadableStream({
    561    cancel() {
    562      throw error2;
    563    }
    564  });
    565 
    566  const ws = recordingWritableStream();
    567 
    568  ws.abort(error1);
    569 
    570  return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error')
    571    .then(() => {
    572      return ws.getWriter().closed.then(
    573        () => assert_unreached('the promise must not fulfill'),
    574        err => {
    575          assert_equals(err, error1, 'the promise must reject with error1');
    576 
    577          assert_array_equals(rs.eventsWithoutPulls, ['cancel', err]);
    578          assert_array_equals(ws.events, ['abort', error1]);
    579        }
    580      );
    581    });
    582 
    583 }, 'Errors must be propagated backward: becomes errored before piping via abort; preventCancel omitted; rejected ' +
    584   'cancel promise');
    585 
    586 promise_test(t => {
    587 
    588  const rs = recordingReadableStream();
    589 
    590  const ws = recordingWritableStream();
    591 
    592  ws.abort(error1);
    593 
    594  return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true })).then(() => {
    595    assert_array_equals(rs.eventsWithoutPulls, []);
    596    assert_array_equals(ws.events, ['abort', error1]);
    597  });
    598 
    599 }, 'Errors must be propagated backward: becomes errored before piping via abort; preventCancel = true');
    600 
    601 promise_test(t => {
    602 
    603  const rs = recordingReadableStream();
    604 
    605  let resolveWriteCalled;
    606  const writeCalledPromise = new Promise(resolve => {
    607    resolveWriteCalled = resolve;
    608  });
    609 
    610  const ws = recordingWritableStream({
    611    write() {
    612      resolveWriteCalled();
    613      return flushAsyncEvents();
    614    }
    615  });
    616 
    617  const pipePromise = rs.pipeTo(ws);
    618 
    619  rs.controller.enqueue('a');
    620 
    621  return writeCalledPromise.then(() => {
    622    ws.controller.error(error1);
    623 
    624    return promise_rejects_exactly(t, error1, pipePromise);
    625  }).then(() => {
    626    assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]);
    627    assert_array_equals(ws.events, ['write', 'a']);
    628  });
    629 
    630 }, 'Errors must be propagated backward: erroring via the controller errors once pending write completes');