tor-browser

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

error-propagation-forward.any.js (17344B)


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