tor-browser

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

canvas-promise-test.js (6005B)


      1 /**
      2 * Framework for executing tests with HTMLCanvasElement, main thread
      3 * OffscreenCanvas and worker OffscreenCanvas. Canvas tests are specified using
      4 * calls to `canvasPromiseTest`, which runs the test on the main thread, using
      5 * an HTML and an OffscreenCanvas. Calling `runCanvasTestsInWorker` at the
      6 * script level then re-execute the whole script in a worker, this time using
      7 * only OffscreenCanvas objects. Example usage:
      8 *
      9 * <script>
     10 * runCanvasTestsInWorker();
     11 *
     12 * canvasPromiseTest(async (canvas) => {
     13 *   // ...
     14 * }, "Sample test")
     15 * </script>
     16 */
     17 
     18 /**
     19 * Enum listing all test types emitted by `canvasPromiseTest()`.
     20 */
     21 const CanvasTestType = Object.freeze({
     22  HTML:   Symbol('html'),
     23  DETACHED_HTML:   Symbol('detached_html'),
     24  OFFSCREEN:  Symbol('offscreen'),
     25  PLACEHOLDER: Symbol('placeholder'),
     26  WORKER: Symbol('worker')
     27 });
     28 
     29 ALL_CANVAS_TEST_TYPES = Object.values(CanvasTestType);
     30 DEFAULT_CANVAS_TEST_TYPES = [
     31    CanvasTestType.HTML,
     32    CanvasTestType.OFFSCREEN,
     33    CanvasTestType.WORKER,
     34 ];
     35 HTML_CANVAS_ELEMENT_TEST_TYPES = [
     36    CanvasTestType.HTML,
     37    CanvasTestType.DETACHED_HTML,
     38 ];
     39 OFFSCREEN_CANVAS_TEST_TYPES = [
     40    CanvasTestType.OFFSCREEN,
     41    CanvasTestType.WORKER,
     42 ];
     43 MAIN_THREAD_CANVAS_TEST_TYPES = [
     44    CanvasTestType.HTML,
     45    CanvasTestType.DETACHED_HTML,
     46    CanvasTestType.OFFSCREEN,
     47    CanvasTestType.PLACEHOLDER,
     48 ];
     49 WORKER_CANVAS_TEST_TYPES = [
     50    CanvasTestType.WORKER,
     51 ];
     52 
     53 /**
     54 * Run `testBody` in a `promise_test` against multiple types of canvases. By
     55 * default, the test is executed against an HTMLCanvasElement, a main thread
     56 * OffscreenCanvas and a worker OffscreenCanvas, though `testTypes` can be used
     57 * only enable a subset of these. `testBody` must be a function accepting a
     58 * canvas as parameter and returning a promise that resolves on test completion.
     59 *
     60 * This function has two implementations. The version below runs the test on the
     61 * main thread and another version in `canvas-worker-test.js` runs it in a
     62 * worker. The worker invocation is launched by calling `runCanvasTestsInWorker`
     63 * at the script level.
     64 */
     65 function canvasPromiseTest(
     66    testBody, description,
     67    {testTypes = DEFAULT_CANVAS_TEST_TYPES} = {}) {
     68  if (testTypes.includes(CanvasTestType.WORKER)) {
     69    setup(() => {
     70      const currentScript = document.currentScript;
     71      assert_true(
     72          currentScript.classList.contains('runCanvasTestsInWorkerInvoked'),
     73          'runCanvasTestsInWorker() must be called in the current script ' +
     74          'before calling canvasPromiseTest with CanvasTestType.WORKER test ' +
     75          'type, or else the test won\'t have worker coverage.');
     76 
     77      currentScript.classList.add('canvasWorkerTestAdded');
     78    });
     79  }
     80 
     81  if (testTypes.includes(CanvasTestType.HTML)) {
     82    promise_test(async () => {
     83      if (!document.body) {
     84        document.documentElement.appendChild(document.createElement("body"));
     85      }
     86      const canvas = document.createElement('canvas');
     87      document.body.appendChild(canvas);
     88      await testBody(canvas, {canvasType: CanvasTestType.HTML});
     89      document.body.removeChild(canvas);
     90    }, 'HTMLCanvasElement: ' + description);
     91  }
     92 
     93  if (testTypes.includes(CanvasTestType.DETACHED_HTML)) {
     94    promise_test(() => testBody(document.createElement('canvas'),
     95                                {canvasType: CanvasTestType.DETACHED_HTML}),
     96                 'Detached HTMLCanvasElement: ' + description);
     97  }
     98 
     99  if (testTypes.includes(CanvasTestType.OFFSCREEN)) {
    100    promise_test(() => testBody(new OffscreenCanvas(300, 150),
    101                                {canvasType: CanvasTestType.OFFSCREEN}),
    102                 'OffscreenCanvas: ' + description);
    103  }
    104 
    105  if (testTypes.includes(CanvasTestType.PLACEHOLDER)) {
    106    promise_test(async () => {
    107      if (!document.body) {
    108        document.documentElement.appendChild(document.createElement("body"));
    109      }
    110      const placeholder = document.createElement('canvas');
    111      document.body.appendChild(placeholder);
    112      await testBody(placeholder.transferControlToOffscreen(),
    113                     {canvasType: CanvasTestType.PLACEHOLDER});
    114    }, 'PlaceholderCanvas: ' + description);
    115  }
    116 }
    117 
    118 /**
    119 * Run all the canvasPromiseTest from the current script in a worker.
    120 * If the tests depend on external scripts, these must be specified as a list
    121 * via the `dependencies` parameter so that the worker could load them.
    122 */
    123 function runCanvasTestsInWorker({dependencies = []} = {}) {
    124  const currentScript = document.currentScript;
    125  // Keep track of whether runCanvasTestsInWorker was invoked on the current
    126  // script. `canvasPromiseTest` will fail if `runCanvasTestsInWorker` hasn't
    127  // been called, to prevent accidentally omitting worker coverage.
    128  setup(() => {
    129    assert_false(
    130        currentScript.classList.contains('runCanvasTestsInWorkerInvoked'),
    131        'runCanvasTestsInWorker() can\'t be invoked twice on the same script.');
    132    currentScript.classList.add('runCanvasTestsInWorkerInvoked');
    133  });
    134 
    135  promise_setup(async () => {
    136    const allDeps = [
    137      '/resources/testharness.js',
    138      '/html/canvas/resources/canvas-promise-test.js',
    139      // canvas-promise-test-worker.js overrides parts of canvas-test.js.
    140      '/html/canvas/resources/canvas-promise-test-worker.js',
    141    ].concat(dependencies);
    142 
    143    const dependencyScripts =
    144       await Promise.all(allDeps.map(dep => fetch(dep).then(r => r.text())));
    145    const canvasTests = currentScript.textContent;
    146    const allScripts = dependencyScripts.concat([canvasTests, 'done();']);
    147 
    148    const workerBlob = new Blob(allScripts);
    149    const worker = new Worker(URL.createObjectURL(workerBlob));
    150    fetch_tests_from_worker(worker);
    151  });
    152 
    153  promise_setup(async () => {
    154    await new Promise(resolve => step_timeout(resolve, 0));
    155    assert_true(
    156        currentScript.classList.contains('canvasWorkerTestAdded'),
    157        'runCanvasTestsInWorker() should not be called if no ' +
    158        'canvasPromiseTest uses the CanvasTestType.WORKER test type.');
    159  });
    160 }