"""Generates Canvas tests from YAML file definitions.
See README.md for instructions on how to run this test generator and for how to
add or modify tests.
This code generatror was originally written by Philip Taylor for use at
http://philip.html5.org/tests/canvas/suite/tests/
It has been adapted for use with the Web Platform Test Suite suite at
https://github.com/web-platform-tests/wpt/
"""
from typing import Any, Callable, Container, DefaultDict, FrozenSet
from typing import List, Mapping, MutableMapping, Set, Tuple, Union
import re
import collections
import copy
import dataclasses
import enum
import importlib
import math
import os
import pathlib
import sys
import textwrap
import jinja2
try:
import cairocffi as cairo # type: ignore
except ImportError:
import cairo
try:
# Compatible and lots faster.
import syck as yaml # type: ignore
except ImportError:
import yaml
class Error(Exception):
"""Base class for all exceptions raised by this module"""
class InvalidTestDefinitionError(Error):
"""Raised on invalid test definition."""
class _CanvasType(str, enum.Enum):
HTML_CANVAS = 'HtmlCanvas'
OFFSCREEN_CANVAS = 'OffscreenCanvas'
WORKER = 'Worker'
class _TemplateType(str, enum.Enum):
REFERENCE = 'reference'
HTML_REFERENCE = 'html_reference'
CAIRO_REFERENCE = 'cairo_reference'
IMG_REFERENCE = 'img_reference'
TESTHARNESS = 'testharness'
_REFERENCE_TEMPLATES = (_TemplateType.REFERENCE,
_TemplateType.HTML_REFERENCE,
_TemplateType.CAIRO_REFERENCE,
_TemplateType.IMG_REFERENCE)
_TestParams = Mapping[str, Any]
_MutableTestParams = MutableMapping[str, Any]
# All parameters that test definitions can specify to control test generation.
# Some have defaults values, used if the test definition doesn't specify them.
_TEST_DEFINITION_PARAMS = {
# Base parameters:
# Test name, which ultimately is used as filename. File variant dimension
# names (i.e. the 'file_variant_names' property below) are appended to this
# to produce unique filenames.
'name': None,
# The implementation of the test body. This should be JavaScript code,
# drawing on the canvas via the provided `canvas` and `ctx` objects.
# `canvas` will be an `HTMLCanvasElement` instance for 'HtmlCanvas' test
# type, else it will be an `OffscreenCanvas` instance. `ctx` will be a '2d'
# rendering context.
'code': None,
# Whether this test variant is enabled. This can be used to generate sparse
# variant grids, by making this parameter resolve to `True` only for certain
# combinations of variant dimensions, e.g.:
# - enabled: {{ variant_names[1] in ['foo', 'bar'] }})
'enabled': 'true',
# Textual description for this test. This is used both as text in the
# generated HTML page, and as testharness.js test fixture description, for
# JavaScript tests (i.e. arguments to the `test()`, `async_test()` and
# `promise_test()` functions).
'desc': '',
# If specified, 'notes:' adds a `
` tag in the page showing this
# note.
'notes': '',
# Size of the canvas to use.
'size': (100, 50),
# Parameters controlling test runner execution:
# For reftests, specifies the tolerance in pixel difference allowed for the
# test to pass. By default, the test and reference must produce identical
# images. To allow a certain difference, specify as, for instance:
# fuzzy: maxDifference=0-2; totalPixels=0-1234
# In this example, up to 1234 pixels can differ by at most a difference of
# 2 on a any color channels.
'fuzzy': None,
# Used to control the test timeout, in case the test takes too long to
# complete. For instance: `timeout: long`.
'timeout': None,
# Parameters controlling what type of tests should be generated:
# List of the canvas types for which the test should be generated. Defaults
# to generating the test for all types. Options are:
# - 'HtmlCanvas': to test using an HTMLCanvasElement.
# - 'Offscreen': to test using an OffscreenCanvas on the main thread.
# - 'Worker': to test using an OffscreenCanvas in a worker.
'canvas_types': list(_CanvasType),
# Specifies the type of test fixture to generate.
#
# For testharness.js JavaScript tests, valid options are "sync", "async" or
# "promise", which produce standard `test()`, `async_test()` and
# `promise_test()` testharness fixtures respectively. For compatibility with
# old tests, leaving `test_type` unspecified produces a test using the
# legacy `_addTest` helper from canvas-tests.js. These are synchronous tests
# that can be made asynchronous by calling `deferTest()` and calling
# `t.done()` when the test completes. New tests should prefer specifying
# `test_type` explicitly.
#
# For reftests, generated tests are by default synchronous (if 'test_type'
# is not specified). Setting 'test_type' to "promise" makes it possible to
# use async/await syntax in the test body. In both cases, the test completes
# when the test body returns. Asynchronous APIs can be supported by wrapping
# them in promises awaiting on their completion.
'test_type': None,
# Causes the test generator to generate a reftest instead of a JavaScript
# test. Similarly to the `code:` parameter, 'reference:' should be set to
# JavaScript code drawing to a provided `canvas` object via a provided `ctx`
# 2D rendering context. The code generator will generate a reference HTML
# file that the test runner will use to validate the test result. Cannot be
# used in combination with 'html_reference:', 'cairo_reference:' or
# 'img_reference:'.
'reference': None,
# Similar to 'reference:', but the value is an HTML document instead of
# JavaScript drawing to a canvas. This is useful to use the DOM or an SVG
# drawing as a reference to compare the test result against. Cannot be used
# in combination with 'reference:', 'cairo_reference:' or 'img_reference:'.
'html_reference': None,
# Similar to 'reference:', but the value is Python code generating an image
# using the pycairo library. The Python code is provided with a `surface`
# and `cr` variable, instances of `cairo.ImageSurface` and `cairo.Context`
# respectively. Cannot be used in combination with 'reference:',
# 'html_reference:' or 'img_reference:'.
'cairo_reference': None,
# Similar to 'reference', but the value is the path to an image resource
# file to use as reference. When using 'cairo_reference:', the generated
# image path is assigned to 'img_reference:', for the template to use. A
# test could technically set 'img_reference:' directly, specifying a
# pre-generated image. This can be useful for plain and trivial images (e.g.
# '/images/green-100x50.png', but any non-trivial pre-generated image should
# be avoided because these can't easily be inspected and maintained if it
# needs to be revisited in the future. Cannot be used in combination with
# 'reference:', 'html_reference:' or 'cairo_reference:'.
'img_reference': None,
# Parameters adding HTML tags in the generated HTML files:
# Additional HTML attributes to pass to the '