tor-browser

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

abort-signal-any-tests.js (8807B)


      1 // Tests for AbortSignal.any() and subclasses that don't use a controller.
      2 function abortSignalAnySignalOnlyTests(signalInterface) {
      3  const desc = `${signalInterface.name}.any()`
      4 
      5  test(t => {
      6    const signal = signalInterface.any([]);
      7    assert_false(signal.aborted);
      8  }, `${desc} works with an empty array of signals`);
      9 }
     10 
     11 // Tests for AbortSignal.any() and subclasses that use a controller.
     12 function abortSignalAnyTests(signalInterface, controllerInterface) {
     13  const suffix = `(using ${controllerInterface.name})`;
     14  const desc = `${signalInterface.name}.any()`;
     15 
     16  test(t => {
     17    const controller = new controllerInterface();
     18    const signal = controller.signal;
     19    const cloneSignal = signalInterface.any([signal]);
     20    assert_false(cloneSignal.aborted);
     21    assert_true("reason" in cloneSignal, "cloneSignal has reason property");
     22    assert_equals(cloneSignal.reason, undefined,
     23        "cloneSignal.reason is initially undefined");
     24    assert_not_equals(signal, cloneSignal,
     25        `${desc} returns a new signal.`);
     26 
     27    let eventFired = false;
     28    cloneSignal.onabort = t.step_func((e) => {
     29      assert_equals(e.target, cloneSignal,
     30          `The event target is the signal returned by ${desc}`);
     31      eventFired = true;
     32    });
     33 
     34    controller.abort("reason string");
     35    assert_true(signal.aborted);
     36    assert_true(cloneSignal.aborted);
     37    assert_true(eventFired);
     38    assert_equals(cloneSignal.reason, "reason string",
     39        `${desc} propagates the abort reason`);
     40  }, `${desc} follows a single signal ${suffix}`);
     41 
     42  test(t => {
     43    for (let i = 0; i < 3; ++i) {
     44      const controllers = [];
     45      for (let j = 0; j < 3; ++j) {
     46        controllers.push(new controllerInterface());
     47      }
     48      const combinedSignal = signalInterface.any(controllers.map(c => c.signal));
     49 
     50      let eventFired = false;
     51      combinedSignal.onabort = t.step_func((e) => {
     52        assert_equals(e.target, combinedSignal,
     53            `The event target is the signal returned by ${desc}`);
     54        eventFired = true;
     55      });
     56 
     57      controllers[i].abort();
     58      assert_true(eventFired);
     59      assert_true(combinedSignal.aborted);
     60      assert_true(combinedSignal.reason instanceof DOMException,
     61          "signal.reason is a DOMException");
     62      assert_equals(combinedSignal.reason.name, "AbortError",
     63          "signal.reason is a AbortError");
     64    }
     65  }, `${desc} follows multiple signals ${suffix}`);
     66 
     67  test(t => {
     68    const controllers = [];
     69    for (let i = 0; i < 3; ++i) {
     70      controllers.push(new controllerInterface());
     71    }
     72    controllers[1].abort("reason 1");
     73    controllers[2].abort("reason 2");
     74 
     75    const signal = signalInterface.any(controllers.map(c => c.signal));
     76    assert_true(signal.aborted);
     77    assert_equals(signal.reason, "reason 1",
     78        "The signal should be aborted with the first reason");
     79  }, `${desc} returns an aborted signal if passed an aborted signal ${suffix}`);
     80 
     81  test(t => {
     82    const controller = new controllerInterface();
     83    const signal = signalInterface.any([controller.signal, controller.signal]);
     84    assert_false(signal.aborted);
     85    controller.abort("reason");
     86    assert_true(signal.aborted);
     87    assert_equals(signal.reason, "reason");
     88  }, `${desc} can be passed the same signal more than once ${suffix}`);
     89 
     90  test(t => {
     91    const controller1 = new controllerInterface();
     92    controller1.abort("reason 1");
     93    const controller2 = new controllerInterface();
     94    controller2.abort("reason 2");
     95 
     96    const signal = signalInterface.any([controller1.signal, controller2.signal, controller1.signal]);
     97    assert_true(signal.aborted);
     98    assert_equals(signal.reason, "reason 1");
     99  }, `${desc} uses the first instance of a duplicate signal ${suffix}`);
    100 
    101  test(t => {
    102    for (let i = 0; i < 3; ++i) {
    103      const controllers = [];
    104      for (let j = 0; j < 3; ++j) {
    105        controllers.push(new controllerInterface());
    106      }
    107      const combinedSignal1 =
    108          signalInterface.any([controllers[0].signal, controllers[1].signal]);
    109      const combinedSignal2 =
    110          signalInterface.any([combinedSignal1, controllers[2].signal]);
    111 
    112      let eventFired = false;
    113      combinedSignal2.onabort = t.step_func((e) => {
    114        eventFired = true;
    115      });
    116 
    117      controllers[i].abort();
    118      assert_true(eventFired);
    119      assert_true(combinedSignal2.aborted);
    120      assert_true(combinedSignal2.reason instanceof DOMException,
    121          "signal.reason is a DOMException");
    122      assert_equals(combinedSignal2.reason.name, "AbortError",
    123          "signal.reason is a AbortError");
    124    }
    125  }, `${desc} signals are composable ${suffix}`);
    126 
    127  async_test(t => {
    128    const controller = new controllerInterface();
    129    const timeoutSignal = AbortSignal.timeout(5);
    130 
    131    const combinedSignal = signalInterface.any([controller.signal, timeoutSignal]);
    132 
    133    combinedSignal.onabort = t.step_func_done(() => {
    134      assert_true(combinedSignal.aborted);
    135      assert_true(combinedSignal.reason instanceof DOMException,
    136          "combinedSignal.reason is a DOMException");
    137      assert_equals(combinedSignal.reason.name, "TimeoutError",
    138          "combinedSignal.reason is a TimeoutError");
    139    });
    140  }, `${desc} works with signals returned by AbortSignal.timeout() ${suffix}`);
    141 
    142  test(t => {
    143    const controller = new controllerInterface();
    144    let combined = signalInterface.any([controller.signal]);
    145    combined = signalInterface.any([combined]);
    146    combined = signalInterface.any([combined]);
    147    combined = signalInterface.any([combined]);
    148 
    149    let eventFired = false;
    150    combined.onabort = () => {
    151      eventFired = true;
    152    }
    153 
    154    assert_false(eventFired);
    155    assert_false(combined.aborted);
    156 
    157    controller.abort("the reason");
    158 
    159    assert_true(eventFired);
    160    assert_true(combined.aborted);
    161    assert_equals(combined.reason, "the reason");
    162  }, `${desc} works with intermediate signals ${suffix}`);
    163 
    164  test(t => {
    165    const controller = new controllerInterface();
    166    const signals = [];
    167    // The first event should be dispatched on the originating signal.
    168    signals.push(controller.signal);
    169    // All dependents are linked to `controller.signal` (never to another
    170    // composite signal), so this is the order events should fire.
    171    signals.push(signalInterface.any([controller.signal]));
    172    signals.push(signalInterface.any([controller.signal]));
    173    signals.push(signalInterface.any([signals[0]]));
    174    signals.push(signalInterface.any([signals[1]]));
    175 
    176    let result = "";
    177    for (let i = 0; i < signals.length; i++) {
    178      signals[i].addEventListener('abort', () => {
    179        result += i;
    180      });
    181    }
    182    controller.abort();
    183    assert_equals(result, "01234");
    184  }, `Abort events for ${desc} signals fire in the right order ${suffix}`);
    185 
    186  test(t => {
    187    const controller = new controllerInterface();
    188    const signal1 = signalInterface.any([controller.signal]);
    189    const signal2 = signalInterface.any([signal1]);
    190    let eventFired = false;
    191 
    192    controller.signal.addEventListener('abort', () => {
    193      const signal3 = signalInterface.any([signal2]);
    194      assert_true(controller.signal.aborted);
    195      assert_true(signal1.aborted);
    196      assert_true(signal2.aborted);
    197      assert_true(signal3.aborted);
    198      eventFired = true;
    199    });
    200 
    201    controller.abort();
    202    assert_true(eventFired, "event fired");
    203  }, `Dependent signals for ${desc} are marked aborted before abort events fire ${suffix}`);
    204 
    205  test(t => {
    206    const controller1 = new controllerInterface();
    207    const controller2 = new controllerInterface();
    208    const signal = signalInterface.any([controller1.signal, controller2.signal]);
    209    let count = 0;
    210 
    211    controller1.signal.addEventListener('abort', () => {
    212      controller2.abort("reason 2");
    213    });
    214 
    215    signal.addEventListener('abort', () => {
    216      count++;
    217    });
    218 
    219    controller1.abort("reason 1");
    220    assert_equals(count, 1);
    221    assert_true(signal.aborted);
    222    assert_equals(signal.reason, "reason 1");
    223  }, `Dependent signals for ${desc} are aborted correctly for reentrant aborts ${suffix}`);
    224 
    225  test(t => {
    226    const source = signalInterface.abort();
    227    const dependent = signalInterface.any([source]);
    228    assert_true(source.reason instanceof DOMException);
    229    assert_equals(source.reason, dependent.reason);
    230  }, `Dependent signals for ${desc} should use the same DOMException instance from the already aborted source signal ${suffix}`);
    231 
    232  test(t => {
    233    const controller = new controllerInterface();
    234    const source = controller.signal;
    235    const dependent = signalInterface.any([source]);
    236    controller.abort();
    237    assert_true(source.reason instanceof DOMException);
    238    assert_equals(source.reason, dependent.reason);
    239  }, `Dependent signals for ${desc} should use the same DOMException instance from the source signal being aborted later ${suffix}`);
    240 }