tor-browser

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

close.any.js (14234B)


      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(() => {
     13  const ws = new WritableStream({
     14    close() {
     15      return 'Hello';
     16    }
     17  });
     18 
     19  const writer = ws.getWriter();
     20 
     21  const closePromise = writer.close();
     22  return closePromise.then(value => assert_equals(value, undefined, 'fulfillment value must be undefined'));
     23 }, 'fulfillment value of writer.close() call must be undefined even if the underlying sink returns a non-undefined ' +
     24    'value');
     25 
     26 promise_test(() => {
     27  let controller;
     28  let resolveClose;
     29  const ws = new WritableStream({
     30    start(c) {
     31      controller = c;
     32    },
     33    close() {
     34      return new Promise(resolve => {
     35        resolveClose = resolve;
     36      });
     37    }
     38  });
     39 
     40  const writer = ws.getWriter();
     41 
     42  const closePromise = writer.close();
     43  return flushAsyncEvents().then(() => {
     44    controller.error(error1);
     45    return flushAsyncEvents();
     46  }).then(() => {
     47    resolveClose();
     48    return Promise.all([
     49      closePromise,
     50      writer.closed,
     51      flushAsyncEvents().then(() => writer.closed)]);
     52  });
     53 }, 'when sink calls error asynchronously while sink close is in-flight, the stream should not become errored');
     54 
     55 promise_test(() => {
     56  let controller;
     57  const passedError = new Error('error me');
     58  const ws = new WritableStream({
     59    start(c) {
     60      controller = c;
     61    },
     62    close() {
     63      controller.error(passedError);
     64    }
     65  });
     66 
     67  const writer = ws.getWriter();
     68 
     69  return writer.close().then(() => writer.closed);
     70 }, 'when sink calls error synchronously while closing, the stream should not become errored');
     71 
     72 promise_test(t => {
     73  const ws = new WritableStream({
     74    close() {
     75      throw error1;
     76    }
     77  });
     78 
     79  const writer = ws.getWriter();
     80 
     81  return Promise.all([
     82    writer.write('y'),
     83    promise_rejects_exactly(t, error1, writer.close(), 'close() must reject with the error'),
     84    promise_rejects_exactly(t, error1, writer.closed, 'closed must reject with the error')
     85  ]);
     86 }, 'when the sink throws during close, and the close is requested while a write is still in-flight, the stream should ' +
     87   'become errored during the close');
     88 
     89 promise_test(() => {
     90  const ws = new WritableStream({
     91    write(chunk, controller) {
     92      controller.error(error1);
     93      return new Promise(() => {});
     94    }
     95  });
     96 
     97  const writer = ws.getWriter();
     98  writer.write('a');
     99 
    100  return delay(0).then(() => {
    101    writer.releaseLock();
    102  });
    103 }, 'releaseLock on a stream with a pending write in which the stream has been errored');
    104 
    105 promise_test(() => {
    106  let controller;
    107  const ws = new WritableStream({
    108    start(c) {
    109      controller = c;
    110    },
    111    close() {
    112      controller.error(error1);
    113      return new Promise(() => {});
    114    }
    115  });
    116 
    117  const writer = ws.getWriter();
    118  writer.close();
    119 
    120  return delay(0).then(() => {
    121    writer.releaseLock();
    122  });
    123 }, 'releaseLock on a stream with a pending close in which controller.error() was called');
    124 
    125 promise_test(() => {
    126  const ws = recordingWritableStream();
    127 
    128  const writer = ws.getWriter();
    129 
    130  return writer.ready.then(() => {
    131    assert_equals(writer.desiredSize, 1, 'desiredSize should be 1');
    132 
    133    writer.close();
    134    assert_equals(writer.desiredSize, 1, 'desiredSize should be still 1');
    135 
    136    return writer.ready.then(v => {
    137      assert_equals(v, undefined, 'ready promise should be fulfilled with undefined');
    138      assert_array_equals(ws.events, ['close'], 'write and abort should not be called');
    139    });
    140  });
    141 }, 'when close is called on a WritableStream in writable state, ready should return a fulfilled promise');
    142 
    143 promise_test(() => {
    144  const ws = recordingWritableStream({
    145    write() {
    146      return new Promise(() => {});
    147    }
    148  });
    149 
    150  const writer = ws.getWriter();
    151 
    152  return writer.ready.then(() => {
    153    writer.write('a');
    154 
    155    assert_equals(writer.desiredSize, 0, 'desiredSize should be 0');
    156 
    157    let calledClose = false;
    158    return Promise.all([
    159      writer.ready.then(v => {
    160        assert_equals(v, undefined, 'ready promise should be fulfilled with undefined');
    161        assert_true(calledClose, 'ready should not be fulfilled before writer.close() is called');
    162        assert_array_equals(ws.events, ['write', 'a'], 'sink abort() should not be called');
    163      }),
    164      flushAsyncEvents().then(() => {
    165        writer.close();
    166        calledClose = true;
    167      })
    168    ]);
    169  });
    170 }, 'when close is called on a WritableStream in waiting state, ready promise should be fulfilled');
    171 
    172 promise_test(() => {
    173  let asyncCloseFinished = false;
    174  const ws = recordingWritableStream({
    175    close() {
    176      return flushAsyncEvents().then(() => {
    177        asyncCloseFinished = true;
    178      });
    179    }
    180  });
    181 
    182  const writer = ws.getWriter();
    183  return writer.ready.then(() => {
    184    writer.write('a');
    185 
    186    writer.close();
    187 
    188    return writer.ready.then(v => {
    189      assert_false(asyncCloseFinished, 'ready promise should be fulfilled before async close completes');
    190      assert_equals(v, undefined, 'ready promise should be fulfilled with undefined');
    191      assert_array_equals(ws.events, ['write', 'a', 'close'], 'sink abort() should not be called');
    192    });
    193  });
    194 }, 'when close is called on a WritableStream in waiting state, ready should be fulfilled immediately even if close ' +
    195    'takes a long time');
    196 
    197 promise_test(t => {
    198  const rejection = { name: 'letter' };
    199  const ws = new WritableStream({
    200    close() {
    201      return {
    202        then(onFulfilled, onRejected) { onRejected(rejection); }
    203      };
    204    }
    205  });
    206  return promise_rejects_exactly(t, rejection, ws.getWriter().close(), 'close() should return a rejection');
    207 }, 'returning a thenable from close() should work');
    208 
    209 promise_test(t => {
    210  const ws = new WritableStream();
    211  const writer = ws.getWriter();
    212  return writer.ready.then(() => {
    213    const closePromise = writer.close();
    214    const closedPromise = writer.closed;
    215    writer.releaseLock();
    216    return Promise.all([
    217      closePromise,
    218      promise_rejects_js(t, TypeError, closedPromise, '.closed promise should be rejected')
    219    ]);
    220  });
    221 }, 'releaseLock() should not change the result of sync close()');
    222 
    223 promise_test(t => {
    224  const ws = new WritableStream({
    225    close() {
    226      return flushAsyncEvents();
    227    }
    228  });
    229  const writer = ws.getWriter();
    230  return writer.ready.then(() => {
    231    const closePromise = writer.close();
    232    const closedPromise = writer.closed;
    233    writer.releaseLock();
    234    return Promise.all([
    235      closePromise,
    236      promise_rejects_js(t, TypeError, closedPromise, '.closed promise should be rejected')
    237    ]);
    238  });
    239 }, 'releaseLock() should not change the result of async close()');
    240 
    241 promise_test(() => {
    242  let resolveClose;
    243  const ws = new WritableStream({
    244    close() {
    245      const promise = new Promise(resolve => {
    246        resolveClose = resolve;
    247      });
    248      return promise;
    249    }
    250  });
    251  const writer = ws.getWriter();
    252  const closePromise = writer.close();
    253  writer.releaseLock();
    254  return delay(0).then(() => {
    255    resolveClose();
    256    return closePromise.then(() => {
    257      assert_equals(ws.getWriter().desiredSize, 0, 'desiredSize should be 0');
    258    });
    259  });
    260 }, 'close() should set state to CLOSED even if writer has detached');
    261 
    262 promise_test(() => {
    263  let resolveClose;
    264  const ws = new WritableStream({
    265    close() {
    266      const promise = new Promise(resolve => {
    267        resolveClose = resolve;
    268      });
    269      return promise;
    270    }
    271  });
    272  const writer = ws.getWriter();
    273  writer.close();
    274  writer.releaseLock();
    275  return delay(0).then(() => {
    276    const abortingWriter = ws.getWriter();
    277    const abortPromise = abortingWriter.abort();
    278    abortingWriter.releaseLock();
    279    resolveClose();
    280    return abortPromise;
    281  });
    282 }, 'the promise returned by async abort during close should resolve');
    283 
    284 // Though the order in which the promises are fulfilled or rejected is arbitrary, we're checking it for
    285 // interoperability. We can change the order as long as we file bugs on all implementers to update to the latest tests
    286 // to keep them interoperable.
    287 
    288 promise_test(() => {
    289  const ws = new WritableStream({});
    290 
    291  const writer = ws.getWriter();
    292 
    293  const closePromise = writer.close();
    294 
    295  const events = [];
    296  return Promise.all([
    297    closePromise.then(() => {
    298      events.push('closePromise');
    299    }),
    300    writer.closed.then(() => {
    301      events.push('closed');
    302    })
    303  ]).then(() => {
    304    assert_array_equals(events, ['closePromise', 'closed'],
    305                        'promises must fulfill/reject in the expected order');
    306  });
    307 }, 'promises must fulfill/reject in the expected order on closure');
    308 
    309 promise_test(() => {
    310  const ws = new WritableStream({});
    311 
    312  // Wait until the WritableStream starts so that the close() call gets processed. Otherwise, abort() will be
    313  // processed without waiting for completion of the close().
    314  return delay(0).then(() => {
    315    const writer = ws.getWriter();
    316 
    317    const closePromise = writer.close();
    318    const abortPromise = writer.abort(error1);
    319 
    320    const events = [];
    321    return Promise.all([
    322      closePromise.then(() => {
    323        events.push('closePromise');
    324      }),
    325      abortPromise.then(() => {
    326        events.push('abortPromise');
    327      }),
    328      writer.closed.then(() => {
    329        events.push('closed');
    330      })
    331    ]).then(() => {
    332      assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'],
    333                          'promises must fulfill/reject in the expected order');
    334    });
    335  });
    336 }, 'promises must fulfill/reject in the expected order on aborted closure');
    337 
    338 promise_test(t => {
    339  const ws = new WritableStream({
    340    close() {
    341      return Promise.reject(error1);
    342    }
    343  });
    344 
    345  // Wait until the WritableStream starts so that the close() call gets processed.
    346  return delay(0).then(() => {
    347    const writer = ws.getWriter();
    348 
    349    const closePromise = writer.close();
    350    const abortPromise = writer.abort(error2);
    351 
    352    const events = [];
    353    closePromise.catch(() => events.push('closePromise'));
    354    abortPromise.catch(() => events.push('abortPromise'));
    355    writer.closed.catch(() => events.push('closed'));
    356    return Promise.all([
    357      promise_rejects_exactly(t, error1, closePromise,
    358                             'closePromise must reject with the error returned from the sink\'s close method'),
    359      promise_rejects_exactly(t, error1, abortPromise,
    360                             'abortPromise must reject with the error returned from the sink\'s close method'),
    361      promise_rejects_exactly(t, error2, writer.closed,
    362                              'writer.closed must reject with error2')
    363    ]).then(() => {
    364      assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'],
    365                          'promises must fulfill/reject in the expected order');
    366    });
    367  });
    368 }, 'promises must fulfill/reject in the expected order on aborted and errored closure');
    369 
    370 promise_test(t => {
    371  let resolveWrite;
    372  let controller;
    373  const ws = new WritableStream({
    374    write(chunk, c) {
    375      controller = c;
    376      return new Promise(resolve => {
    377        resolveWrite = resolve;
    378      });
    379    }
    380  });
    381  const writer = ws.getWriter();
    382  return writer.ready.then(() => {
    383    const writePromise = writer.write('c');
    384    controller.error(error1);
    385    const closePromise = writer.close();
    386    let closeRejected = false;
    387    closePromise.catch(() => {
    388      closeRejected = true;
    389    });
    390    return flushAsyncEvents().then(() => {
    391      assert_false(closeRejected);
    392      resolveWrite();
    393      return Promise.all([
    394        writePromise,
    395        promise_rejects_exactly(t, error1, closePromise, 'close() should reject')
    396      ]).then(() => {
    397        assert_true(closeRejected);
    398      });
    399    });
    400  });
    401 }, 'close() should not reject until no sink methods are in flight');
    402 
    403 promise_test(() => {
    404  const ws = new WritableStream();
    405  const writer1 = ws.getWriter();
    406  return writer1.close().then(() => {
    407    writer1.releaseLock();
    408    const writer2 = ws.getWriter();
    409    const ready = writer2.ready;
    410    assert_equals(ready.constructor, Promise);
    411    return ready;
    412  });
    413 }, 'ready promise should be initialised as fulfilled for a writer on a closed stream');
    414 
    415 promise_test(() => {
    416  const ws = new WritableStream();
    417  ws.close();
    418  const writer = ws.getWriter();
    419  return writer.closed;
    420 }, 'close() on a writable stream should work');
    421 
    422 promise_test(t => {
    423  const ws = new WritableStream();
    424  ws.getWriter();
    425  return promise_rejects_js(t, TypeError, ws.close(), 'close should reject');
    426 }, 'close() on a locked stream should reject');
    427 
    428 promise_test(t => {
    429  const ws = new WritableStream({
    430    start(controller) {
    431      controller.error(error1);
    432    }
    433  });
    434  return promise_rejects_exactly(t, error1, ws.close(), 'close should reject with error1');
    435 }, 'close() on an erroring stream should reject');
    436 
    437 promise_test(t => {
    438  const ws = new WritableStream({
    439    start(controller) {
    440      controller.error(error1);
    441    }
    442  });
    443  const writer = ws.getWriter();
    444  return promise_rejects_exactly(t, error1, writer.closed, 'closed should reject with the error').then(() => {
    445    writer.releaseLock();
    446    return promise_rejects_js(t, TypeError, ws.close(), 'close should reject');
    447  });
    448 }, 'close() on an errored stream should reject');
    449 
    450 promise_test(t => {
    451  const ws = new WritableStream();
    452  const writer = ws.getWriter();
    453  return writer.close().then(() => {
    454    return promise_rejects_js(t, TypeError, ws.close(), 'close should reject');
    455  });
    456 }, 'close() on an closed stream should reject');
    457 
    458 promise_test(t => {
    459  const ws = new WritableStream({
    460    close() {
    461      return new Promise(() => {});
    462    }
    463  });
    464 
    465  const writer = ws.getWriter();
    466  writer.close();
    467  writer.releaseLock();
    468 
    469  return promise_rejects_js(t, TypeError, ws.close(), 'close should reject');
    470 }, 'close() on a stream with a pending close should reject');
    471 
    472 // See https://github.com/whatwg/streams/issues/1341.
    473 promise_test(async t => {
    474  const ws = new WritableStream();
    475  const writer = ws.getWriter();
    476 
    477  await writer.write(1);
    478  await writer.close();
    479 
    480  return promise_rejects_js(t, TypeError, writer.write(2), 'write should reject');
    481 }, 'write() on a closed stream should reject');