tor-browser

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

rs-test-templates.js (23854B)


      1 'use strict';
      2 
      3 // These tests can be run against any readable stream produced by the web platform that meets the given descriptions.
      4 // For readable stream tests, the factory should return the stream. For reader tests, the factory should return a
      5 // { stream, reader } object. (You can use this to vary the time at which you acquire a reader.)
      6 
      7 self.templatedRSEmpty = (label, factory) => {
      8  test(() => {}, 'Running templatedRSEmpty with ' + label);
      9 
     10  test(() => {
     11 
     12    const rs = factory();
     13 
     14    assert_equals(typeof rs.locked, 'boolean', 'has a boolean locked getter');
     15    assert_equals(typeof rs.cancel, 'function', 'has a cancel method');
     16    assert_equals(typeof rs.getReader, 'function', 'has a getReader method');
     17    assert_equals(typeof rs.pipeThrough, 'function', 'has a pipeThrough method');
     18    assert_equals(typeof rs.pipeTo, 'function', 'has a pipeTo method');
     19    assert_equals(typeof rs.tee, 'function', 'has a tee method');
     20 
     21  }, label + ': instances have the correct methods and properties');
     22 
     23  test(() => {
     24    const rs = factory();
     25 
     26    assert_throws_js(TypeError, () => rs.getReader({ mode: '' }), 'empty string mode should throw');
     27    assert_throws_js(TypeError, () => rs.getReader({ mode: null }), 'null mode should throw');
     28    assert_throws_js(TypeError, () => rs.getReader({ mode: 'asdf' }), 'asdf mode should throw');
     29    assert_throws_js(TypeError, () => rs.getReader(5), '5 should throw');
     30 
     31    // Should not throw
     32    rs.getReader(null);
     33 
     34  }, label + ': calling getReader with invalid arguments should throw appropriate errors');
     35 };
     36 
     37 self.templatedRSClosed = (label, factory) => {
     38  test(() => {}, 'Running templatedRSClosed with ' + label);
     39 
     40  promise_test(() => {
     41 
     42    const rs = factory();
     43    const cancelPromise1 = rs.cancel();
     44    const cancelPromise2 = rs.cancel();
     45 
     46    assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
     47 
     48    return Promise.all([
     49      cancelPromise1.then(v => assert_equals(v, undefined, 'first cancel() call should fulfill with undefined')),
     50      cancelPromise2.then(v => assert_equals(v, undefined, 'second cancel() call should fulfill with undefined'))
     51    ]);
     52 
     53  }, label + ': cancel() should return a distinct fulfilled promise each time');
     54 
     55  test(() => {
     56 
     57    const rs = factory();
     58    assert_false(rs.locked, 'locked getter should return false');
     59 
     60  }, label + ': locked should be false');
     61 
     62  test(() => {
     63 
     64    const rs = factory();
     65    rs.getReader(); // getReader() should not throw.
     66 
     67  }, label + ': getReader() should be OK');
     68 
     69  test(() => {
     70 
     71    const rs = factory();
     72 
     73    const reader = rs.getReader();
     74    reader.releaseLock();
     75 
     76    const reader2 = rs.getReader(); // Getting a second reader should not throw.
     77    reader2.releaseLock();
     78 
     79    rs.getReader(); // Getting a third reader should not throw.
     80 
     81  }, label + ': should be able to acquire multiple readers if they are released in succession');
     82 
     83  test(() => {
     84 
     85    const rs = factory();
     86 
     87    rs.getReader();
     88 
     89    assert_throws_js(TypeError, () => rs.getReader(), 'getting a second reader should throw');
     90    assert_throws_js(TypeError, () => rs.getReader(), 'getting a third reader should throw');
     91 
     92  }, label + ': should not be able to acquire a second reader if we don\'t release the first one');
     93 };
     94 
     95 self.templatedRSErrored = (label, factory, error) => {
     96  test(() => {}, 'Running templatedRSErrored with ' + label);
     97 
     98  promise_test(t => {
     99 
    100    const rs = factory();
    101    const reader = rs.getReader();
    102 
    103    return Promise.all([
    104      promise_rejects_exactly(t, error, reader.closed),
    105      promise_rejects_exactly(t, error, reader.read())
    106    ]);
    107 
    108  }, label + ': getReader() should return a reader that acts errored');
    109 
    110  promise_test(t => {
    111 
    112    const rs = factory();
    113    const reader = rs.getReader();
    114 
    115    return Promise.all([
    116      promise_rejects_exactly(t, error, reader.read()),
    117      promise_rejects_exactly(t, error, reader.read()),
    118      promise_rejects_exactly(t, error, reader.closed)
    119    ]);
    120 
    121  }, label + ': read() twice should give the error each time');
    122 
    123  test(() => {
    124    const rs = factory();
    125 
    126    assert_false(rs.locked, 'locked getter should return false');
    127  }, label + ': locked should be false');
    128 };
    129 
    130 self.templatedRSErroredSyncOnly = (label, factory, error) => {
    131  test(() => {}, 'Running templatedRSErroredSyncOnly with ' + label);
    132 
    133  promise_test(t => {
    134 
    135    const rs = factory();
    136    rs.getReader().releaseLock();
    137    const reader = rs.getReader(); // Calling getReader() twice does not throw (the stream is not locked).
    138 
    139    return promise_rejects_exactly(t, error, reader.closed);
    140 
    141  }, label + ': should be able to obtain a second reader, with the correct closed promise');
    142 
    143  test(() => {
    144 
    145    const rs = factory();
    146    rs.getReader();
    147 
    148    assert_throws_js(TypeError, () => rs.getReader(), 'getting a second reader should throw a TypeError');
    149    assert_throws_js(TypeError, () => rs.getReader(), 'getting a third reader should throw a TypeError');
    150 
    151  }, label + ': should not be able to obtain additional readers if we don\'t release the first lock');
    152 
    153  promise_test(t => {
    154 
    155    const rs = factory();
    156    const cancelPromise1 = rs.cancel();
    157    const cancelPromise2 = rs.cancel();
    158 
    159    assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
    160 
    161    return Promise.all([
    162      promise_rejects_exactly(t, error, cancelPromise1),
    163      promise_rejects_exactly(t, error, cancelPromise2)
    164    ]);
    165 
    166  }, label + ': cancel() should return a distinct rejected promise each time');
    167 
    168  promise_test(t => {
    169 
    170    const rs = factory();
    171    const reader = rs.getReader();
    172    const cancelPromise1 = reader.cancel();
    173    const cancelPromise2 = reader.cancel();
    174 
    175    assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
    176 
    177    return Promise.all([
    178      promise_rejects_exactly(t, error, cancelPromise1),
    179      promise_rejects_exactly(t, error, cancelPromise2)
    180    ]);
    181 
    182  }, label + ': reader cancel() should return a distinct rejected promise each time');
    183 };
    184 
    185 self.templatedRSEmptyReader = (label, factory) => {
    186  test(() => {}, 'Running templatedRSEmptyReader with ' + label);
    187 
    188  test(() => {
    189 
    190    const reader = factory().reader;
    191 
    192    assert_true('closed' in reader, 'has a closed property');
    193    assert_equals(typeof reader.closed.then, 'function', 'closed property is thenable');
    194 
    195    assert_equals(typeof reader.cancel, 'function', 'has a cancel method');
    196    assert_equals(typeof reader.read, 'function', 'has a read method');
    197    assert_equals(typeof reader.releaseLock, 'function', 'has a releaseLock method');
    198 
    199  }, label + ': instances have the correct methods and properties');
    200 
    201  test(() => {
    202 
    203    const { stream } = factory();
    204 
    205    assert_true(stream.locked, 'locked getter should return true');
    206 
    207  }, label + ': locked should be true');
    208 
    209  promise_test(t => {
    210 
    211    const { read } = factory();
    212 
    213    read().then(
    214      t.unreached_func('read() should not fulfill'),
    215      t.unreached_func('read() should not reject')
    216    );
    217 
    218    return delay(500);
    219 
    220  }, label + ': read() should never settle');
    221 
    222  promise_test(t => {
    223 
    224    const { read } = factory();
    225 
    226    read().then(
    227      t.unreached_func('read() should not fulfill'),
    228      t.unreached_func('read() should not reject')
    229    );
    230 
    231    read().then(
    232      t.unreached_func('read() should not fulfill'),
    233      t.unreached_func('read() should not reject')
    234    );
    235 
    236    return delay(500);
    237 
    238  }, label + ': two read()s should both never settle');
    239 
    240  test(() => {
    241 
    242    const { read } = factory();
    243    assert_not_equals(read(), read(), 'the promises returned should be distinct');
    244 
    245  }, label + ': read() should return distinct promises each time');
    246 
    247  test(() => {
    248 
    249    const { stream } = factory();
    250    assert_throws_js(TypeError, () => stream.getReader(), 'stream.getReader() should throw a TypeError');
    251 
    252  }, label + ': getReader() again on the stream should fail');
    253 
    254  promise_test(async t => {
    255 
    256    const { stream, reader, read } = factory();
    257 
    258    const read1 = read();
    259    const read2 = read();
    260    const closed = reader.closed;
    261 
    262    reader.releaseLock();
    263 
    264    assert_false(stream.locked, 'the stream should be unlocked');
    265 
    266    await Promise.all([
    267      promise_rejects_js(t, TypeError, read1, 'first read should reject'),
    268      promise_rejects_js(t, TypeError, read2, 'second read should reject'),
    269      promise_rejects_js(t, TypeError, closed, 'closed should reject')
    270    ]);
    271 
    272  }, label + ': releasing the lock should reject all pending read requests');
    273 
    274  promise_test(t => {
    275 
    276    const { reader, read } = factory();
    277    reader.releaseLock();
    278 
    279    return Promise.all([
    280      promise_rejects_js(t, TypeError, read()),
    281      promise_rejects_js(t, TypeError, read())
    282    ]);
    283 
    284  }, label + ': releasing the lock should cause further read() calls to reject with a TypeError');
    285 
    286  promise_test(t => {
    287 
    288    const { reader } = factory();
    289 
    290    const closedBefore = reader.closed;
    291    reader.releaseLock();
    292    const closedAfter = reader.closed;
    293 
    294    assert_equals(closedBefore, closedAfter, 'the closed promise should not change identity');
    295 
    296    return promise_rejects_js(t, TypeError, closedBefore);
    297 
    298  }, label + ': releasing the lock should cause closed calls to reject with a TypeError');
    299 
    300  test(() => {
    301 
    302    const { stream, reader } = factory();
    303 
    304    reader.releaseLock();
    305    assert_false(stream.locked, 'locked getter should return false');
    306 
    307  }, label + ': releasing the lock should cause locked to become false');
    308 
    309  promise_test(() => {
    310 
    311    const { reader, read } = factory();
    312    reader.cancel();
    313 
    314    return read().then(r => {
    315      assert_object_equals(r, { value: undefined, done: true }, 'read()ing from the reader should give a done result');
    316    });
    317 
    318  }, label + ': canceling via the reader should cause the reader to act closed');
    319 
    320  promise_test(t => {
    321 
    322    const { stream } = factory();
    323    return promise_rejects_js(t, TypeError, stream.cancel());
    324 
    325  }, label + ': canceling via the stream should fail');
    326 };
    327 
    328 self.templatedRSClosedReader = (label, factory) => {
    329  test(() => {}, 'Running templatedRSClosedReader with ' + label);
    330 
    331  promise_test(() => {
    332 
    333    const reader = factory().reader;
    334 
    335    return reader.read().then(v => {
    336      assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
    337    });
    338 
    339  }, label + ': read() should fulfill with { value: undefined, done: true }');
    340 
    341  promise_test(() => {
    342 
    343    const reader = factory().reader;
    344 
    345    return Promise.all([
    346      reader.read().then(v => {
    347        assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
    348      }),
    349      reader.read().then(v => {
    350        assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
    351      })
    352    ]);
    353 
    354  }, label + ': read() multiple times should fulfill with { value: undefined, done: true }');
    355 
    356  promise_test(() => {
    357 
    358    const reader = factory().reader;
    359 
    360    return reader.read().then(() => reader.read()).then(v => {
    361      assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
    362    });
    363 
    364  }, label + ': read() should work when used within another read() fulfill callback');
    365 
    366  promise_test(() => {
    367 
    368    const reader = factory().reader;
    369 
    370    return reader.closed.then(v => assert_equals(v, undefined, 'reader closed should fulfill with undefined'));
    371 
    372  }, label + ': closed should fulfill with undefined');
    373 
    374  promise_test(t => {
    375 
    376    const reader = factory().reader;
    377 
    378    const closedBefore = reader.closed;
    379    reader.releaseLock();
    380    const closedAfter = reader.closed;
    381 
    382    assert_not_equals(closedBefore, closedAfter, 'the closed promise should change identity');
    383 
    384    return Promise.all([
    385      closedBefore.then(v => assert_equals(v, undefined, 'reader.closed acquired before release should fulfill')),
    386      promise_rejects_js(t, TypeError, closedAfter)
    387    ]);
    388 
    389  }, label + ': releasing the lock should cause closed to reject and change identity');
    390 
    391  promise_test(() => {
    392 
    393    const reader = factory().reader;
    394    const cancelPromise1 = reader.cancel();
    395    const cancelPromise2 = reader.cancel();
    396    const closedReaderPromise = reader.closed;
    397 
    398    assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
    399    assert_not_equals(cancelPromise1, closedReaderPromise, 'cancel() promise 1 should be distinct from reader.closed');
    400    assert_not_equals(cancelPromise2, closedReaderPromise, 'cancel() promise 2 should be distinct from reader.closed');
    401 
    402    return Promise.all([
    403      cancelPromise1.then(v => assert_equals(v, undefined, 'first cancel() should fulfill with undefined')),
    404      cancelPromise2.then(v => assert_equals(v, undefined, 'second cancel() should fulfill with undefined'))
    405    ]);
    406 
    407  }, label + ': cancel() should return a distinct fulfilled promise each time');
    408 };
    409 
    410 self.templatedRSErroredReader = (label, factory, error) => {
    411  test(() => {}, 'Running templatedRSErroredReader with ' + label);
    412 
    413  promise_test(t => {
    414 
    415    const reader = factory().reader;
    416    return promise_rejects_exactly(t, error, reader.closed);
    417 
    418  }, label + ': closed should reject with the error');
    419 
    420  promise_test(t => {
    421 
    422    const reader = factory().reader;
    423    const closedBefore = reader.closed;
    424 
    425    return promise_rejects_exactly(t, error, closedBefore).then(() => {
    426      reader.releaseLock();
    427 
    428      const closedAfter = reader.closed;
    429      assert_not_equals(closedBefore, closedAfter, 'the closed promise should change identity');
    430 
    431      return promise_rejects_js(t, TypeError, closedAfter);
    432    });
    433 
    434  }, label + ': releasing the lock should cause closed to reject and change identity');
    435 
    436  promise_test(t => {
    437 
    438    const reader = factory().reader;
    439    return promise_rejects_exactly(t, error, reader.read());
    440 
    441  }, label + ': read() should reject with the error');
    442 };
    443 
    444 self.templatedRSTwoChunksOpenReader = (label, factory, chunks) => {
    445  test(() => {}, 'Running templatedRSTwoChunksOpenReader with ' + label);
    446 
    447  promise_test(() => {
    448 
    449    const reader = factory().reader;
    450 
    451    return Promise.all([
    452      reader.read().then(r => {
    453        assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
    454      }),
    455      reader.read().then(r => {
    456        assert_object_equals(r, { value: chunks[1], done: false }, 'second result should be correct');
    457      })
    458    ]);
    459 
    460  }, label + ': calling read() twice without waiting will eventually give both chunks (sequential)');
    461 
    462  promise_test(() => {
    463 
    464    const reader = factory().reader;
    465 
    466    return reader.read().then(r => {
    467      assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
    468 
    469      return reader.read().then(r2 => {
    470        assert_object_equals(r2, { value: chunks[1], done: false }, 'second result should be correct');
    471      });
    472    });
    473 
    474  }, label + ': calling read() twice without waiting will eventually give both chunks (nested)');
    475 
    476  test(() => {
    477 
    478    const reader = factory().reader;
    479    assert_not_equals(reader.read(), reader.read(), 'the promises returned should be distinct');
    480 
    481  }, label + ': read() should return distinct promises each time');
    482 
    483  promise_test(() => {
    484 
    485    const reader = factory().reader;
    486 
    487    const promise1 = reader.closed.then(v => {
    488      assert_equals(v, undefined, 'reader closed should fulfill with undefined');
    489    });
    490 
    491    const promise2 = reader.read().then(r => {
    492      assert_object_equals(r, { value: chunks[0], done: false },
    493                           'promise returned before cancellation should fulfill with a chunk');
    494    });
    495 
    496    reader.cancel();
    497 
    498    const promise3 = reader.read().then(r => {
    499      assert_object_equals(r, { value: undefined, done: true },
    500                           'promise returned after cancellation should fulfill with an end-of-stream signal');
    501    });
    502 
    503    return Promise.all([promise1, promise2, promise3]);
    504 
    505  }, label + ': cancel() after a read() should still give that single read result');
    506 };
    507 
    508 self.templatedRSTwoChunksClosedReader = function (label, factory, chunks) {
    509  test(() => {}, 'Running templatedRSTwoChunksClosedReader with ' + label);
    510 
    511  promise_test(() => {
    512 
    513    const reader = factory().reader;
    514 
    515    return Promise.all([
    516      reader.read().then(r => {
    517        assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
    518      }),
    519      reader.read().then(r => {
    520        assert_object_equals(r, { value: chunks[1], done: false }, 'second result should be correct');
    521      }),
    522      reader.read().then(r => {
    523        assert_object_equals(r, { value: undefined, done: true }, 'third result should be correct');
    524      })
    525    ]);
    526 
    527  }, label + ': third read(), without waiting, should give { value: undefined, done: true } (sequential)');
    528 
    529  promise_test(() => {
    530 
    531    const reader = factory().reader;
    532 
    533    return reader.read().then(r => {
    534      assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
    535 
    536      return reader.read().then(r2 => {
    537        assert_object_equals(r2, { value: chunks[1], done: false }, 'second result should be correct');
    538 
    539        return reader.read().then(r3 => {
    540          assert_object_equals(r3, { value: undefined, done: true }, 'third result should be correct');
    541        });
    542      });
    543    });
    544 
    545  }, label + ': third read(), without waiting, should give { value: undefined, done: true } (nested)');
    546 
    547  promise_test(() => {
    548 
    549    const streamAndReader = factory();
    550    const stream = streamAndReader.stream;
    551    const reader = streamAndReader.reader;
    552 
    553    assert_true(stream.locked, 'stream should start locked');
    554 
    555    const promise = reader.closed.then(v => {
    556      assert_equals(v, undefined, 'reader closed should fulfill with undefined');
    557      assert_true(stream.locked, 'stream should remain locked');
    558    });
    559 
    560    reader.read();
    561    reader.read();
    562 
    563    return promise;
    564 
    565  }, label +
    566     ': draining the stream via read() should cause the reader closed promise to fulfill, but locked stays true');
    567 
    568  promise_test(() => {
    569 
    570    const streamAndReader = factory();
    571    const stream = streamAndReader.stream;
    572    const reader = streamAndReader.reader;
    573 
    574    const promise = reader.closed.then(() => {
    575      assert_true(stream.locked, 'the stream should start locked');
    576      reader.releaseLock(); // Releasing the lock after reader closed should not throw.
    577      assert_false(stream.locked, 'the stream should end unlocked');
    578    });
    579 
    580    reader.read();
    581    reader.read();
    582 
    583    return promise;
    584 
    585  }, label + ': releasing the lock after the stream is closed should cause locked to become false');
    586 
    587  promise_test(t => {
    588 
    589    const reader = factory().reader;
    590 
    591    reader.releaseLock();
    592 
    593    return Promise.all([
    594      promise_rejects_js(t, TypeError, reader.read()),
    595      promise_rejects_js(t, TypeError, reader.read()),
    596      promise_rejects_js(t, TypeError, reader.read())
    597    ]);
    598 
    599  }, label + ': releasing the lock should cause further read() calls to reject with a TypeError');
    600 
    601  promise_test(() => {
    602 
    603    const streamAndReader = factory();
    604    const stream = streamAndReader.stream;
    605    const reader = streamAndReader.reader;
    606 
    607    const readerClosed = reader.closed;
    608 
    609    assert_equals(reader.closed, readerClosed, 'accessing reader.closed twice in succession gives the same value');
    610 
    611    const promise = reader.read().then(() => {
    612      assert_equals(reader.closed, readerClosed, 'reader.closed is the same after read() fulfills');
    613 
    614      reader.releaseLock();
    615 
    616      assert_equals(reader.closed, readerClosed, 'reader.closed is the same after releasing the lock');
    617 
    618      const newReader = stream.getReader();
    619      return newReader.read();
    620    });
    621 
    622    assert_equals(reader.closed, readerClosed, 'reader.closed is the same after calling read()');
    623 
    624    return promise;
    625 
    626  }, label + ': reader\'s closed property always returns the same promise');
    627 };
    628 
    629 self.templatedRSTeeCancel = (label, factory) => {
    630  test(() => {}, `Running templatedRSTeeCancel with ${label}`);
    631 
    632  promise_test(async () => {
    633 
    634    const reason1 = new Error('We\'re wanted men.');
    635    const reason2 = new Error('I have the death sentence on twelve systems.');
    636 
    637    let resolve;
    638    const promise = new Promise(r => resolve = r);
    639    const rs = factory({
    640      cancel(reason) {
    641        assert_array_equals(reason, [reason1, reason2],
    642          'the cancel reason should be an array containing those from the branches');
    643        resolve();
    644      }
    645    });
    646 
    647    const [branch1, branch2] = rs.tee();
    648    await Promise.all([
    649      branch1.cancel(reason1),
    650      branch2.cancel(reason2),
    651      promise
    652    ]);
    653 
    654  }, `${label}: canceling both branches should aggregate the cancel reasons into an array`);
    655 
    656  promise_test(async () => {
    657 
    658    const reason1 = new Error('This little one\'s not worth the effort.');
    659    const reason2 = new Error('Come, let me get you something.');
    660 
    661    let resolve;
    662    const promise = new Promise(r => resolve = r);
    663    const rs = factory({
    664      cancel(reason) {
    665        assert_array_equals(reason, [reason1, reason2],
    666          'the cancel reason should be an array containing those from the branches');
    667        resolve();
    668      }
    669    });
    670 
    671    const [branch1, branch2] = rs.tee();
    672    await Promise.all([
    673      branch2.cancel(reason2),
    674      branch1.cancel(reason1),
    675      promise
    676    ]);
    677 
    678  }, `${label}: canceling both branches in reverse order should aggregate the cancel reasons into an array`);
    679 
    680  promise_test(async t => {
    681 
    682    const theError = { name: 'I\'ll be careful.' };
    683    const rs = factory({
    684      cancel() {
    685        throw theError;
    686      }
    687    });
    688 
    689    const [branch1, branch2] = rs.tee();
    690    await Promise.all([
    691      promise_rejects_exactly(t, theError, branch1.cancel()),
    692      promise_rejects_exactly(t, theError, branch2.cancel())
    693    ]);
    694 
    695  }, `${label}: failing to cancel the original stream should cause cancel() to reject on branches`);
    696 
    697  promise_test(async t => {
    698 
    699    const theError = { name: 'You just watch yourself!' };
    700    let controller;
    701    const stream = factory({
    702      start(c) {
    703        controller = c;
    704      }
    705    });
    706 
    707    const [branch1, branch2] = stream.tee();
    708    controller.error(theError);
    709 
    710    await Promise.all([
    711      promise_rejects_exactly(t, theError, branch1.cancel()),
    712      promise_rejects_exactly(t, theError, branch2.cancel())
    713    ]);
    714 
    715  }, `${label}: erroring a teed stream should properly handle canceled branches`);
    716 
    717 };
    718 
    719 self.templatedRSThrowAfterCloseOrError = (label, factory) => {
    720  test(() => {}, 'Running templatedRSThrowAfterCloseOrError with ' + label);
    721 
    722  const theError = new Error('a unique string');
    723 
    724  promise_test(async t => {
    725    let controller;
    726    const stream = factory({
    727      start: t.step_func((c) => {
    728        controller = c;
    729      })
    730    });
    731 
    732    controller.close();
    733 
    734    assert_throws_js(TypeError, () => controller.enqueue(new Uint8Array([1])));
    735  }, `${label}: enqueue() throws after close()`);
    736 
    737  promise_test(async t => {
    738    let controller;
    739    const stream = factory({
    740      start: t.step_func((c) => {
    741        controller = c;
    742      })
    743    });
    744 
    745    controller.enqueue(new Uint8Array([1]));
    746    controller.close();
    747 
    748    assert_throws_js(TypeError, () => controller.enqueue(new Uint8Array([2])));
    749  }, `${label}: enqueue() throws after enqueue() and close()`);
    750 
    751  promise_test(async t => {
    752    let controller;
    753    const stream = factory({
    754      start: t.step_func((c) => {
    755        controller = c;
    756      })
    757    });
    758 
    759    controller.error(theError);
    760 
    761    assert_throws_js(TypeError, () => controller.enqueue(new Uint8Array([1])));
    762  }, `${label}: enqueue() throws after error()`);
    763 
    764  promise_test(async t => {
    765    let controller;
    766    const stream = factory({
    767      start: t.step_func((c) => {
    768        controller = c;
    769      })
    770    });
    771 
    772    controller.error(theError);
    773 
    774    assert_throws_js(TypeError, () => controller.close());
    775  }, `${label}: close() throws after error()`);
    776 };