tor-browser

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

general.any.js (14041B)


      1 // META: global=window,worker,shadowrealm
      2 // META: script=../resources/test-utils.js
      3 // META: script=../resources/rs-utils.js
      4 'use strict';
      5 
      6 test(() => {
      7  new TransformStream({ transform() { } });
      8 }, 'TransformStream can be constructed with a transform function');
      9 
     10 test(() => {
     11  new TransformStream();
     12  new TransformStream({});
     13 }, 'TransformStream can be constructed with no transform function');
     14 
     15 test(() => {
     16  const ts = new TransformStream({ transform() { } });
     17 
     18  const writer = ts.writable.getWriter();
     19  assert_equals(writer.desiredSize, 1, 'writer.desiredSize should be 1');
     20 }, 'TransformStream writable starts in the writable state');
     21 
     22 promise_test(() => {
     23  const ts = new TransformStream();
     24 
     25  const writer = ts.writable.getWriter();
     26  writer.write('a');
     27  assert_equals(writer.desiredSize, 0, 'writer.desiredSize should be 0 after write()');
     28 
     29  return ts.readable.getReader().read().then(result => {
     30    assert_equals(result.value, 'a',
     31      'result from reading the readable is the same as was written to writable');
     32    assert_false(result.done, 'stream should not be done');
     33 
     34    return delay(0).then(() => assert_equals(writer.desiredSize, 1, 'desiredSize should be 1 again'));
     35  });
     36 }, 'Identity TransformStream: can read from readable what is put into writable');
     37 
     38 promise_test(() => {
     39  let c;
     40  const ts = new TransformStream({
     41    start(controller) {
     42      c = controller;
     43    },
     44    transform(chunk) {
     45      c.enqueue(chunk.toUpperCase());
     46    }
     47  });
     48 
     49  const writer = ts.writable.getWriter();
     50  writer.write('a');
     51 
     52  return ts.readable.getReader().read().then(result => {
     53    assert_equals(result.value, 'A',
     54      'result from reading the readable is the transformation of what was written to writable');
     55    assert_false(result.done, 'stream should not be done');
     56  });
     57 }, 'Uppercaser sync TransformStream: can read from readable transformed version of what is put into writable');
     58 
     59 promise_test(() => {
     60  let c;
     61  const ts = new TransformStream({
     62    start(controller) {
     63      c = controller;
     64    },
     65    transform(chunk) {
     66      c.enqueue(chunk.toUpperCase());
     67      c.enqueue(chunk.toUpperCase());
     68    }
     69  });
     70 
     71  const writer = ts.writable.getWriter();
     72  writer.write('a');
     73 
     74  const reader = ts.readable.getReader();
     75 
     76  return reader.read().then(result1 => {
     77    assert_equals(result1.value, 'A',
     78      'the first chunk read is the transformation of the single chunk written');
     79    assert_false(result1.done, 'stream should not be done');
     80 
     81    return reader.read().then(result2 => {
     82      assert_equals(result2.value, 'A',
     83        'the second chunk read is also the transformation of the single chunk written');
     84      assert_false(result2.done, 'stream should not be done');
     85    });
     86  });
     87 }, 'Uppercaser-doubler sync TransformStream: can read both chunks put into the readable');
     88 
     89 promise_test(() => {
     90  let c;
     91  const ts = new TransformStream({
     92    start(controller) {
     93      c = controller;
     94    },
     95    transform(chunk) {
     96      return delay(0).then(() => c.enqueue(chunk.toUpperCase()));
     97    }
     98  });
     99 
    100  const writer = ts.writable.getWriter();
    101  writer.write('a');
    102 
    103  return ts.readable.getReader().read().then(result => {
    104    assert_equals(result.value, 'A',
    105      'result from reading the readable is the transformation of what was written to writable');
    106    assert_false(result.done, 'stream should not be done');
    107  });
    108 }, 'Uppercaser async TransformStream: can read from readable transformed version of what is put into writable');
    109 
    110 promise_test(() => {
    111  let doSecondEnqueue;
    112  let returnFromTransform;
    113  const ts = new TransformStream({
    114    transform(chunk, controller) {
    115      delay(0).then(() => controller.enqueue(chunk.toUpperCase()));
    116      doSecondEnqueue = () => controller.enqueue(chunk.toUpperCase());
    117      return new Promise(resolve => {
    118        returnFromTransform = resolve;
    119      });
    120    }
    121  });
    122 
    123  const reader = ts.readable.getReader();
    124 
    125  const writer = ts.writable.getWriter();
    126  writer.write('a');
    127 
    128  return reader.read().then(result1 => {
    129    assert_equals(result1.value, 'A',
    130      'the first chunk read is the transformation of the single chunk written');
    131    assert_false(result1.done, 'stream should not be done');
    132    doSecondEnqueue();
    133 
    134    return reader.read().then(result2 => {
    135      assert_equals(result2.value, 'A',
    136        'the second chunk read is also the transformation of the single chunk written');
    137      assert_false(result2.done, 'stream should not be done');
    138      returnFromTransform();
    139    });
    140  });
    141 }, 'Uppercaser-doubler async TransformStream: can read both chunks put into the readable');
    142 
    143 promise_test(() => {
    144  const ts = new TransformStream({ transform() { } });
    145 
    146  const writer = ts.writable.getWriter();
    147  writer.close();
    148 
    149  return Promise.all([writer.closed, ts.readable.getReader().closed]);
    150 }, 'TransformStream: by default, closing the writable closes the readable (when there are no queued writes)');
    151 
    152 promise_test(() => {
    153  let transformResolve;
    154  const transformPromise = new Promise(resolve => {
    155    transformResolve = resolve;
    156  });
    157  const ts = new TransformStream({
    158    transform() {
    159      return transformPromise;
    160    }
    161  }, undefined, { highWaterMark: 1 });
    162 
    163  const writer = ts.writable.getWriter();
    164  writer.write('a');
    165  writer.close();
    166 
    167  let rsClosed = false;
    168  ts.readable.getReader().closed.then(() => {
    169    rsClosed = true;
    170  });
    171 
    172  return delay(0).then(() => {
    173    assert_equals(rsClosed, false, 'readable is not closed after a tick');
    174    transformResolve();
    175 
    176    return writer.closed.then(() => {
    177      // TODO: Is this expectation correct?
    178      assert_equals(rsClosed, true, 'readable is closed at that point');
    179    });
    180  });
    181 }, 'TransformStream: by default, closing the writable waits for transforms to finish before closing both');
    182 
    183 promise_test(() => {
    184  let c;
    185  const ts = new TransformStream({
    186    start(controller) {
    187      c = controller;
    188    },
    189    transform() {
    190      c.enqueue('x');
    191      c.enqueue('y');
    192      return delay(0);
    193    }
    194  });
    195 
    196  const writer = ts.writable.getWriter();
    197  writer.write('a');
    198  writer.close();
    199 
    200  const readableChunks = readableStreamToArray(ts.readable);
    201 
    202  return writer.closed.then(() => {
    203    return readableChunks.then(chunks => {
    204      assert_array_equals(chunks, ['x', 'y'], 'both enqueued chunks can be read from the readable');
    205    });
    206  });
    207 }, 'TransformStream: by default, closing the writable closes the readable after sync enqueues and async done');
    208 
    209 promise_test(() => {
    210  let c;
    211  const ts = new TransformStream({
    212    start(controller) {
    213      c = controller;
    214    },
    215    transform() {
    216      return delay(0)
    217          .then(() => c.enqueue('x'))
    218          .then(() => c.enqueue('y'))
    219          .then(() => delay(0));
    220    }
    221  });
    222 
    223  const writer = ts.writable.getWriter();
    224  writer.write('a');
    225  writer.close();
    226 
    227  const readableChunks = readableStreamToArray(ts.readable);
    228 
    229  return writer.closed.then(() => {
    230    return readableChunks.then(chunks => {
    231      assert_array_equals(chunks, ['x', 'y'], 'both enqueued chunks can be read from the readable');
    232    });
    233  });
    234 }, 'TransformStream: by default, closing the writable closes the readable after async enqueues and async done');
    235 
    236 promise_test(() => {
    237  let c;
    238  const ts = new TransformStream({
    239    suffix: '-suffix',
    240 
    241    start(controller) {
    242      c = controller;
    243      c.enqueue('start' + this.suffix);
    244    },
    245 
    246    transform(chunk) {
    247      c.enqueue(chunk + this.suffix);
    248    },
    249 
    250    flush() {
    251      c.enqueue('flushed' + this.suffix);
    252    }
    253  });
    254 
    255  const writer = ts.writable.getWriter();
    256  writer.write('a');
    257  writer.close();
    258 
    259  const readableChunks = readableStreamToArray(ts.readable);
    260 
    261  return writer.closed.then(() => {
    262    return readableChunks.then(chunks => {
    263      assert_array_equals(chunks, ['start-suffix', 'a-suffix', 'flushed-suffix'], 'all enqueued chunks have suffixes');
    264    });
    265  });
    266 }, 'Transform stream should call transformer methods as methods');
    267 
    268 promise_test(() => {
    269  function functionWithOverloads() {}
    270  functionWithOverloads.apply = () => assert_unreached('apply() should not be called');
    271  functionWithOverloads.call = () => assert_unreached('call() should not be called');
    272  const ts = new TransformStream({
    273    start: functionWithOverloads,
    274    transform: functionWithOverloads,
    275    flush: functionWithOverloads
    276  });
    277  const writer = ts.writable.getWriter();
    278  writer.write('a');
    279  writer.close();
    280 
    281  return readableStreamToArray(ts.readable);
    282 }, 'methods should not not have .apply() or .call() called');
    283 
    284 promise_test(t => {
    285  let startCalled = false;
    286  let startDone = false;
    287  let transformDone = false;
    288  let flushDone = false;
    289  const ts = new TransformStream({
    290    start() {
    291      startCalled = true;
    292      return flushAsyncEvents().then(() => {
    293        startDone = true;
    294      });
    295    },
    296    transform() {
    297      return t.step(() => {
    298        assert_true(startDone, 'transform() should not be called until the promise returned from start() has resolved');
    299        return flushAsyncEvents().then(() => {
    300          transformDone = true;
    301        });
    302      });
    303    },
    304    flush() {
    305      return t.step(() => {
    306        assert_true(transformDone,
    307                    'flush() should not be called until the promise returned from transform() has resolved');
    308        return flushAsyncEvents().then(() => {
    309          flushDone = true;
    310        });
    311      });
    312    }
    313  }, undefined, { highWaterMark: 1 });
    314 
    315  assert_true(startCalled, 'start() should be called synchronously');
    316 
    317  const writer = ts.writable.getWriter();
    318  const writePromise = writer.write('a');
    319  return writer.close().then(() => {
    320    assert_true(flushDone, 'promise returned from flush() should have resolved');
    321    return writePromise;
    322  });
    323 }, 'TransformStream start, transform, and flush should be strictly ordered');
    324 
    325 promise_test(() => {
    326  let transformCalled = false;
    327  const ts = new TransformStream({
    328    transform() {
    329      transformCalled = true;
    330    }
    331  }, undefined, { highWaterMark: Infinity });
    332  // transform() is only called synchronously when there is no backpressure and all microtasks have run.
    333  return delay(0).then(() => {
    334    const writePromise = ts.writable.getWriter().write();
    335    assert_true(transformCalled, 'transform() should have been called');
    336    return writePromise;
    337  });
    338 }, 'it should be possible to call transform() synchronously');
    339 
    340 promise_test(() => {
    341  const ts = new TransformStream({}, undefined, { highWaterMark: 0 });
    342 
    343  const writer = ts.writable.getWriter();
    344  writer.close();
    345 
    346  return Promise.all([writer.closed, ts.readable.getReader().closed]);
    347 }, 'closing the writable should close the readable when there are no queued chunks, even with backpressure');
    348 
    349 test(() => {
    350  new TransformStream({
    351    start(controller) {
    352      controller.terminate();
    353      assert_throws_js(TypeError, () => controller.enqueue(), 'enqueue should throw');
    354    }
    355  });
    356 }, 'enqueue() should throw after controller.terminate()');
    357 
    358 promise_test(() => {
    359  let controller;
    360  const ts = new TransformStream({
    361    start(c) {
    362      controller = c;
    363    }
    364  });
    365  const cancelPromise = ts.readable.cancel();
    366  assert_throws_js(TypeError, () => controller.enqueue(), 'enqueue should throw');
    367  return cancelPromise;
    368 }, 'enqueue() should throw after readable.cancel()');
    369 
    370 test(() => {
    371  new TransformStream({
    372    start(controller) {
    373      controller.terminate();
    374      controller.terminate();
    375    }
    376  });
    377 }, 'controller.terminate() should do nothing the second time it is called');
    378 
    379 promise_test(t => {
    380  let controller;
    381  const ts = new TransformStream({
    382    start(c) {
    383      controller = c;
    384    }
    385  });
    386  const cancelReason = { name: 'cancelReason' };
    387  const cancelPromise = ts.readable.cancel(cancelReason);
    388  controller.terminate();
    389  return Promise.all([
    390    cancelPromise,
    391    promise_rejects_js(t, TypeError, ts.writable.getWriter().closed, 'closed should reject with TypeError')
    392  ]);
    393 }, 'terminate() should abort writable immediately after readable.cancel()');
    394 
    395 promise_test(t => {
    396  let controller;
    397  const ts = new TransformStream({
    398    start(c) {
    399      controller = c;
    400    }
    401  });
    402  const cancelReason = { name: 'cancelReason' };
    403  return ts.readable.cancel(cancelReason).then(() => {
    404    controller.terminate();
    405    return promise_rejects_exactly(t, cancelReason, ts.writable.getWriter().closed, 'closed should reject with TypeError');
    406  })
    407 }, 'terminate() should do nothing after readable.cancel() resolves');
    408 
    409 
    410 promise_test(() => {
    411  let calls = 0;
    412  new TransformStream({
    413    start() {
    414      ++calls;
    415    }
    416  });
    417  return flushAsyncEvents().then(() => {
    418    assert_equals(calls, 1, 'start() should have been called exactly once');
    419  });
    420 }, 'start() should not be called twice');
    421 
    422 test(() => {
    423  assert_throws_js(RangeError, () => new TransformStream({ readableType: 'bytes' }), 'constructor should throw');
    424 }, 'specifying a defined readableType should throw');
    425 
    426 test(() => {
    427  assert_throws_js(RangeError, () => new TransformStream({ writableType: 'bytes' }), 'constructor should throw');
    428 }, 'specifying a defined writableType should throw');
    429 
    430 test(() => {
    431  class Subclass extends TransformStream {
    432    extraFunction() {
    433      return true;
    434    }
    435  }
    436  assert_equals(
    437      Object.getPrototypeOf(Subclass.prototype), TransformStream.prototype,
    438      'Subclass.prototype\'s prototype should be TransformStream.prototype');
    439  assert_equals(Object.getPrototypeOf(Subclass), TransformStream,
    440                'Subclass\'s prototype should be TransformStream');
    441  const sub = new Subclass();
    442  assert_true(sub instanceof TransformStream,
    443              'Subclass object should be an instance of TransformStream');
    444  assert_true(sub instanceof Subclass,
    445              'Subclass object should be an instance of Subclass');
    446  const readableGetter = Object.getOwnPropertyDescriptor(
    447      TransformStream.prototype, 'readable').get;
    448  assert_equals(readableGetter.call(sub), sub.readable,
    449                'Subclass object should pass brand check');
    450  assert_true(sub.extraFunction(),
    451              'extraFunction() should be present on Subclass object');
    452 }, 'Subclassing TransformStream should work');