summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py')
-rw-r--r--testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py599
1 files changed, 325 insertions, 274 deletions
diff --git a/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py b/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py
index d7042810be..57077f6057 100644
--- a/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py
+++ b/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py
@@ -1,3 +1,4 @@
+"""Generates Canvas tests from YAML file definitions."""
# Current code status:
#
# This was originally written by Philip Taylor for use at
@@ -28,7 +29,8 @@
#
# * Test the tests, add new ones to Git, remove deleted ones from Git, etc.
-from typing import Any, DefaultDict, List, Mapping, Optional, Set, Tuple
+from typing import Any, DefaultDict, FrozenSet, List, Mapping, MutableMapping
+from typing import Optional, Set, Tuple
import re
import collections
@@ -36,10 +38,12 @@ import dataclasses
import enum
import importlib
import itertools
-import jinja2
import os
import pathlib
import sys
+import textwrap
+
+import jinja2
try:
import cairocffi as cairo # type: ignore
@@ -61,12 +65,12 @@ class InvalidTestDefinitionError(Error):
"""Raised on invalid test definition."""
-def _doubleQuoteEscape(string: str) -> str:
+def _double_quote_escape(string: str) -> str:
return string.replace('\\', '\\\\').replace('"', '\\"')
-def _escapeJS(string: str) -> str:
- string = _doubleQuoteEscape(string)
+def _escape_js(string: str) -> str:
+ string = _double_quote_escape(string)
# Kind of an ugly hack, for nicer failure-message output.
string = re.sub(r'\[(\w+)\]', r'[\\""+(\1)+"\\"]', string)
return string
@@ -135,15 +139,15 @@ def _expand_nonfinite(method: str, argstr: str, tail: str) -> str:
match = re.match('<(.*)>', arg)
if match is None:
raise InvalidTestDefinitionError(
- f"Expected arg to match format '<(.*)>', but was: {arg}")
+ f'Expected arg to match format "<(.*)>", but was: {arg}')
a = match.group(1)
args.append(a.split(' '))
calls = []
# Start with the valid argument list.
call = [args[j][0] for j in range(len(args))]
# For each argument alone, try setting it to all its invalid values:
- for i in range(len(args)):
- for a in args[i][1:]:
+ for i, arg in enumerate(args):
+ for a in arg[1:]:
c2 = call[:]
c2[i] = a
calls.append(c2)
@@ -162,7 +166,8 @@ def _expand_nonfinite(method: str, argstr: str, tail: str) -> str:
f(call, 0, 0)
- return '\n'.join('%s(%s)%s' % (method, ', '.join(c), tail) for c in calls)
+ str_calls = (', '.join(c) for c in calls)
+ return '\n'.join(f'{method}({params}){tail}' for params in str_calls)
def _get_test_sub_dir(name: str, name_to_sub_dir: Mapping[str, str]) -> str:
@@ -170,7 +175,7 @@ def _get_test_sub_dir(name: str, name_to_sub_dir: Mapping[str, str]) -> str:
if name.startswith(prefix):
return name_to_sub_dir[prefix]
raise InvalidTestDefinitionError(
- 'Test "%s" has no defined target directory mapping' % name)
+ f'Test "{name}" has no defined target directory mapping')
def _remove_extra_newlines(text: str) -> str:
@@ -183,6 +188,7 @@ def _remove_extra_newlines(text: str) -> str:
text = re.sub(r'\\-\n\s*', '', text, flags=re.MULTILINE | re.DOTALL)
return text
+
def _expand_test_code(code: str) -> str:
code = re.sub(r' @moz-todo', '', code)
@@ -215,48 +221,59 @@ def _expand_test_code(code: str) -> str:
flags=re.MULTILINE | re.DOTALL)
code = re.sub(
- r'@assert (.*) === (.*);', lambda m: '_assertSame(%s, %s, "%s", "%s");'
- % (m.group(1), m.group(2), _escapeJS(m.group(1)), _escapeJS(m.group(2))
- ), code)
+ r'@assert (.*) === (.*);', lambda m:
+ (f'_assertSame({m.group(1)}, {m.group(2)}, '
+ f'"{_escape_js(m.group(1))}", "{_escape_js(m.group(2))}");'), code)
code = re.sub(
r'@assert (.*) !== (.*);', lambda m:
- '_assertDifferent(%s, %s, "%s", "%s");' % (m.group(1), m.group(
- 2), _escapeJS(m.group(1)), _escapeJS(m.group(2))), code)
+ (f'_assertDifferent({m.group(1)}, {m.group(2)}, '
+ f'"{_escape_js(m.group(1))}", "{_escape_js(m.group(2))}");'), code)
code = re.sub(
- r'@assert (.*) =~ (.*);', lambda m: 'assert_regexp_match(%s, %s);' % (
- m.group(1), m.group(2)), code)
+ r'@assert (.*) =~ (.*);',
+ lambda m: f'assert_regexp_match({m.group(1)}, {m.group(2)});', code)
code = re.sub(
- r'@assert (.*);', lambda m: '_assert(%s, "%s");' % (m.group(
- 1), _escapeJS(m.group(1))), code)
+ r'@assert (.*);',
+ lambda m: f'_assert({m.group(1)}, "{_escape_js(m.group(1))}");', code)
- assert ('@' not in code)
+ assert '@' not in code
return code
-class CanvasType(str, enum.Enum):
- HTML_CANVAS = 'htmlcanvas'
- OFFSCREEN_CANVAS = 'offscreencanvas'
- WORKER = 'worker'
+_TestParams = Mapping[str, Any]
+_MutableTestParams = MutableMapping[str, Any]
+
+class _CanvasType(str, enum.Enum):
+ HTML_CANVAS = 'HtmlCanvas'
+ OFFSCREEN_CANVAS = 'OffscreenCanvas'
+ WORKER = 'Worker'
-def _get_enabled_canvas_types(test: Mapping[str, Any]) -> Set[CanvasType]:
- return {CanvasType(t.lower()) for t in test.get('canvasType', CanvasType)}
+
+class _TemplateType(str, enum.Enum):
+ REFERENCE = 'reference'
+ HTML_REFERENCE = 'html_reference'
+ TESTHARNESS = 'testharness'
@dataclasses.dataclass
-class TestConfig:
- out_dir: str
- image_out_dir: str
+class _OutputPaths:
+ element: str
+ offscreen: str
+
+ def sub_path(self, sub_dir: str):
+ """Create a new _OutputPaths that is a subpath of this _OutputPath."""
+ return _OutputPaths(element=os.path.join(self.element, sub_dir),
+ offscreen=os.path.join(self.offscreen, sub_dir))
-def _validate_test(test: Mapping[str, Any]):
+def _validate_test(test: _TestParams):
if test.get('expected', '') == 'green' and re.search(
r'@assert pixel .* 0,0,0,0;', test['code']):
- print('Probable incorrect pixel test in %s' % test['name'])
+ print(f'Probable incorrect pixel test in {test["name"]}')
if 'size' in test and (not isinstance(test['size'], list)
or len(test['size']) != 2):
@@ -264,21 +281,20 @@ def _validate_test(test: Mapping[str, Any]):
f'Invalid canvas size "{test["size"]}" in test {test["name"]}. '
'Expected an array with two numbers.')
- if 'test_type' in test and test['test_type'] != 'promise':
- raise InvalidTestDefinitionError(
- f'Test {test["name"]}\' test_type is invalid, it only accepts '
- '"promise" now for creating promise test type in the template '
- 'file.')
+ if test['template_type'] == _TemplateType.TESTHARNESS:
+ valid_test_types = {'sync', 'async', 'promise'}
+ else:
+ valid_test_types = {'promise'}
- if 'reference' in test and 'html_reference' in test:
+ test_type = test.get('test_type')
+ if test_type is not None and test_type not in valid_test_types:
raise InvalidTestDefinitionError(
- f'Test {test["name"]} is invalid, "reference" and "html_reference" '
- 'can\'t both be specified at the same time.')
+ f'Invalid test_type: {test_type}. '
+ f'Valid values are: {valid_test_types}.')
-def _render_template(jinja_env: jinja2.Environment,
- template: jinja2.Template,
- params: Mapping[str, Any]) -> str:
+def _render_template(jinja_env: jinja2.Environment, template: jinja2.Template,
+ params: _TestParams) -> str:
"""Renders the specified jinja template.
The template is repetitively rendered until no more changes are observed.
@@ -294,236 +310,276 @@ def _render_template(jinja_env: jinja2.Environment,
def _render(jinja_env: jinja2.Environment, template_name: str,
- params: Mapping[str, Any]):
- params = dict(params)
- params.update({
- # Render the code on its own, as it could contain templates expanding
- # to multuple lines. This is needed to get proper indentation of the
- # code in the main template.
- 'code': _render_template(jinja_env,
- jinja_env.from_string(params['code']),
- params)
- })
-
- return _render_template(jinja_env, jinja_env.get_template(template_name),
- params)
-
-
-def _write_reference_test(jinja_env: jinja2.Environment,
- params: Mapping[str, Any],
- enabled_tests: Set[CanvasType],
- canvas_path: str, offscreen_path: str):
- if CanvasType.HTML_CANVAS in enabled_tests:
- html_params = dict(params)
- html_params.update({'canvas_type': CanvasType.HTML_CANVAS.value})
- pathlib.Path(f'{canvas_path}.html').write_text(
- _render(jinja_env, "reftest_element.html", html_params), 'utf-8')
- if CanvasType.OFFSCREEN_CANVAS in enabled_tests:
- offscreen_params = dict(params)
- offscreen_params.update({
- 'canvas_type': CanvasType.OFFSCREEN_CANVAS.value
- })
- pathlib.Path(f'{offscreen_path}.html').write_text(
- _render(jinja_env, "reftest_offscreen.html", offscreen_params),
- 'utf-8')
- if CanvasType.WORKER in enabled_tests:
- worker_params = dict(params)
- worker_params.update({'canvas_type': CanvasType.WORKER.value})
- pathlib.Path(f'{offscreen_path}.w.html').write_text(
- _render(jinja_env, "reftest_worker.html", worker_params), 'utf-8')
-
- js_ref = params.get('reference', '')
- html_ref = params.get('html_reference', '')
- ref_params = dict(params)
- ref_params.update({
- 'is_test_reference': True,
- 'code': js_ref or html_ref
- })
- ref_template_name = 'reftest_element.html' if js_ref else 'reftest.html'
- if CanvasType.HTML_CANVAS in enabled_tests:
- pathlib.Path(f'{canvas_path}-expected.html').write_text(
- _render(jinja_env, ref_template_name, ref_params), 'utf-8')
- if {CanvasType.OFFSCREEN_CANVAS, CanvasType.WORKER} & enabled_tests:
- pathlib.Path(f'{offscreen_path}-expected.html').write_text(
- _render(jinja_env, ref_template_name, ref_params), 'utf-8')
-
-
-def _write_testharness_test(jinja_env: jinja2.Environment,
- params: Mapping[str, Any],
- enabled_tests: Set[CanvasType],
- canvas_path: str,
- offscreen_path: str):
- # Create test cases for canvas and offscreencanvas.
- if CanvasType.HTML_CANVAS in enabled_tests:
- html_params = dict(params)
- html_params.update({'canvas_type': CanvasType.HTML_CANVAS.value})
- pathlib.Path(f'{canvas_path}.html').write_text(
- _render(jinja_env, "testharness_element.html", html_params),
- 'utf-8')
-
- if CanvasType.OFFSCREEN_CANVAS in enabled_tests:
- offscreen_params = dict(params)
- offscreen_params.update({
- 'canvas_type': CanvasType.OFFSCREEN_CANVAS.value
- })
- pathlib.Path(f'{offscreen_path}.html').write_text(
- _render(jinja_env, "testharness_offscreen.html", offscreen_params),
- 'utf-8')
-
- if CanvasType.WORKER in enabled_tests:
- worker_params = dict(params)
- worker_params.update({'canvas_type': CanvasType.WORKER.value})
- pathlib.Path(f'{offscreen_path}.worker.js').write_text(
- _render(jinja_env, "testharness_worker.js", worker_params),
- 'utf-8')
-
-
-def _generate_expected_image(expected: str, name: str, sub_dir: str,
- enabled_canvas_types: Set[CanvasType],
- html_canvas_cfg: TestConfig,
- offscreen_canvas_cfg: TestConfig) -> str:
- """Creates a reference image using Cairo and returns the file location."""
- if expected == 'green':
- return '/images/green-100x50.png'
- if expected == 'clear':
- return '/images/clear-100x50.png'
- if ';' in expected:
- print('Found semicolon in %s' % name)
- expected = re.sub(
- r'^size (\d+) (\d+)',
- r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)'
- r'\ncr = cairo.Context(surface)', expected)
-
- if CanvasType.HTML_CANVAS in enabled_canvas_types:
- expected_canvas = (
- expected + "\nsurface.write_to_png('%s.png')\n" %
- os.path.join(html_canvas_cfg.image_out_dir, sub_dir, name))
- eval(compile(expected_canvas, '<test %s>' % name, 'exec'), {},
- {'cairo': cairo})
-
- if {CanvasType.OFFSCREEN_CANVAS, CanvasType.WORKER} & enabled_canvas_types:
- expected_offscreen = (
- expected + "\nsurface.write_to_png('%s.png')\n" %
- os.path.join(offscreen_canvas_cfg.image_out_dir, sub_dir, name))
- eval(compile(expected_offscreen, '<test %s>' % name, 'exec'), {},
- {'cairo': cairo})
-
- return '%s.png' % name
-
-
-def _generate_test(test: Mapping[str, Any], jinja_env: jinja2.Environment,
- name_to_sub_dir: Mapping[str, str],
- used_tests: DefaultDict[str, Set[CanvasType]],
- html_canvas_cfg: TestConfig,
- offscreen_canvas_cfg: TestConfig) -> None:
- _validate_test(test)
-
- name = test['name']
-
- sub_dir = _get_test_sub_dir(name, name_to_sub_dir)
- enabled_canvas_types = _get_enabled_canvas_types(test)
-
- # Defaults:
- params = {
- 'desc': '',
- 'size': [100, 50],
- }
-
- params.update(test)
-
- # Render parameters used in the test name.
- name = jinja_env.from_string(name).render(params)
- print('\r(%s)' % name, ' ' * 32, '\t')
-
- expected_img = None
- if 'expected' in test and test['expected'] is not None:
- expected_img = _generate_expected_image(test['expected'], name,
- sub_dir, enabled_canvas_types,
- html_canvas_cfg,
- offscreen_canvas_cfg)
-
- params.update({
- 'code': _expand_test_code(test['code']),
- 'expected_img': expected_img
- })
-
- already_tested = used_tests[name].intersection(enabled_canvas_types)
- if already_tested:
- raise InvalidTestDefinitionError(
- f'Test {name} is defined twice for types {already_tested}')
- used_tests[name].update(enabled_canvas_types)
+ params: _TestParams, output_file_name: str):
+ template = jinja_env.get_template(template_name)
+ file_content = _render_template(jinja_env, template, params)
+ pathlib.Path(output_file_name).write_text(file_content, 'utf-8')
+
+
+def _preprocess_code(jinja_env: jinja2.Environment, code: str,
+ params: _TestParams) -> str:
+ code = _expand_test_code(code)
+ # Render the code on its own, as it could contain templates expanding
+ # to multiple lines. This is needed to get proper indentation of the
+ # code in the main template.
+ code = _render_template(jinja_env, jinja_env.from_string(code), params)
+ return code
- canvas_path = os.path.join(html_canvas_cfg.out_dir, sub_dir, name)
- offscreen_path = os.path.join(offscreen_canvas_cfg.out_dir, sub_dir, name)
- if 'manual' in test:
- canvas_path += '-manual'
- offscreen_path += '-manual'
- if 'reference' in test or 'html_reference' in test:
- _write_reference_test(jinja_env, params, enabled_canvas_types,
- canvas_path, offscreen_path)
- else:
- _write_testharness_test(jinja_env, params, enabled_canvas_types,
- canvas_path, offscreen_path)
+class _Variant():
+
+ def __init__(self, params: _MutableTestParams) -> None:
+ self._params = params
+
+ @property
+ def params(self) -> _TestParams:
+ """Read-only getter for this variant's param dict."""
+ return self._params
+
+ @staticmethod
+ def create_with_defaults(test: _TestParams) -> '_Variant':
+ """Create a _Variant from the specified params.
+
+ Default values are added for certain parameters, if missing."""
+ params = {
+ 'desc': '',
+ 'size': [100, 50],
+ 'variant_names': [],
+ }
+ params.update(test)
+ return _Variant(params)
+ def _get_variant_name(self, jinja_env: jinja2.Environment) -> str:
+ name = self.params['name']
+ if self.params.get('append_variants_to_name', True):
+ name = '.'.join([name] + self.params['variant_names'])
-def _recursive_expand_variant_matrix(test_list: List[Mapping[str, Any]],
- variant_matrix: List[Mapping[str, Any]],
+ name = jinja_env.from_string(name).render(self.params)
+ return name
+
+ def _get_file_name(self) -> str:
+ file_name = self.params['name']
+
+ if 'manual' in self.params:
+ file_name += '-manual'
+
+ return file_name
+
+ def _get_canvas_types(self) -> FrozenSet[_CanvasType]:
+ canvas_types = self.params.get('canvas_types', _CanvasType)
+ invalid_types = {
+ type
+ for type in canvas_types if type not in list(_CanvasType)
+ }
+ if invalid_types:
+ raise InvalidTestDefinitionError(
+ f'Invalid canvas_types: {list(invalid_types)}. '
+ f'Accepted values are: {[t.value for t in _CanvasType]}')
+ return frozenset(_CanvasType(t) for t in canvas_types)
+
+ def _get_template_type(self) -> _TemplateType:
+ if 'reference' in self.params and 'html_reference' in self.params:
+ raise InvalidTestDefinitionError(
+ f'Test {self.params["name"]} is invalid, "reference" and '
+ '"html_reference" can\'t both be specified at the same time.')
+
+ if 'reference' in self.params:
+ return _TemplateType.REFERENCE
+ if 'html_reference' in self.params:
+ return _TemplateType.HTML_REFERENCE
+ return _TemplateType.TESTHARNESS
+
+ def finalize_params(self, jinja_env: jinja2.Environment) -> None:
+ """Finalize this variant by adding computed param fields."""
+ self._params['name'] = self._get_variant_name(jinja_env)
+ self._params['file_name'] = self._get_file_name()
+ self._params['canvas_types'] = self._get_canvas_types()
+ self._params['template_type'] = self._get_template_type()
+
+ if 'reference' in self._params:
+ self._params['reference'] = _preprocess_code(
+ jinja_env, self._params['reference'], self._params)
+
+ if 'html_reference' in self._params:
+ self._params['html_reference'] = _preprocess_code(
+ jinja_env, self._params['html_reference'], self._params)
+
+ code_params = dict(self.params)
+ if _CanvasType.HTML_CANVAS in self.params['canvas_types']:
+ code_params['canvas_type'] = _CanvasType.HTML_CANVAS.value
+ self._params['code_element'] = _preprocess_code(
+ jinja_env, self._params['code'], code_params)
+
+ if _CanvasType.OFFSCREEN_CANVAS in self.params['canvas_types']:
+ code_params['canvas_type'] = _CanvasType.OFFSCREEN_CANVAS.value
+ self._params['code_offscreen'] = _preprocess_code(
+ jinja_env, self._params['code'], code_params)
+
+ if _CanvasType.WORKER in self.params['canvas_types']:
+ code_params['canvas_type'] = _CanvasType.WORKER.value
+ self._params['code_worker'] = _preprocess_code(
+ jinja_env, self._params['code'], code_params)
+
+ _validate_test(self._params)
+
+ def generate_expected_image(self, output_dirs: _OutputPaths) -> None:
+ """Creates a reference image using Cairo and save filename in params."""
+ if 'expected' not in self.params:
+ return
+
+ expected = self.params['expected']
+ name = self.params['name']
+
+ if expected == 'green':
+ self._params['expected_img'] = '/images/green-100x50.png'
+ return
+ if expected == 'clear':
+ self._params['expected_img'] = '/images/clear-100x50.png'
+ return
+ if ';' in expected:
+ print(f'Found semicolon in {name}')
+ expected = re.sub(
+ r'^size (\d+) (\d+)',
+ r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)'
+ r'\ncr = cairo.Context(surface)', expected)
+
+ output_paths = output_dirs.sub_path(name)
+ if _CanvasType.HTML_CANVAS in self.params['canvas_types']:
+ expected_canvas = (
+ f'{expected}\n'
+ f'surface.write_to_png("{output_paths.element}.png")\n')
+ eval(compile(expected_canvas, f'<test {name}>', 'exec'), {},
+ {'cairo': cairo})
+
+ if {_CanvasType.OFFSCREEN_CANVAS, _CanvasType.WORKER
+ } & self.params['canvas_types']:
+ expected_offscreen = (
+ f'{expected}\n'
+ f'surface.write_to_png("{output_paths.offscreen}.png")\n')
+ eval(compile(expected_offscreen, f'<test {name}>', 'exec'), {},
+ {'cairo': cairo})
+
+ self._params['expected_img'] = f'{name}.png'
+
+ def _write_reference_test(self, jinja_env: jinja2.Environment,
+ output_files: _OutputPaths):
+ params = dict(self.params)
+ if _CanvasType.HTML_CANVAS in params['canvas_types']:
+ _render(jinja_env, 'reftest_element.html', params,
+ f'{output_files.element}.html')
+ if _CanvasType.OFFSCREEN_CANVAS in params['canvas_types']:
+ _render(jinja_env, 'reftest_offscreen.html', params,
+ f'{output_files.offscreen}.html')
+ if _CanvasType.WORKER in params['canvas_types']:
+ _render(jinja_env, 'reftest_worker.html', params,
+ f'{output_files.offscreen}.w.html')
+
+ params['is_test_reference'] = True
+ is_html_ref = params['template_type'] == _TemplateType.HTML_REFERENCE
+ ref_template = 'reftest.html' if is_html_ref else 'reftest_element.html'
+ if _CanvasType.HTML_CANVAS in params['canvas_types']:
+ _render(jinja_env, ref_template, params,
+ f'{output_files.element}-expected.html')
+ if {_CanvasType.OFFSCREEN_CANVAS, _CanvasType.WORKER
+ } & params['canvas_types']:
+ _render(jinja_env, ref_template, params,
+ f'{output_files.offscreen}-expected.html')
+
+ def _write_testharness_test(self, jinja_env: jinja2.Environment,
+ output_files: _OutputPaths):
+ # Create test cases for canvas and offscreencanvas.
+ if _CanvasType.HTML_CANVAS in self.params['canvas_types']:
+ _render(jinja_env, 'testharness_element.html', self.params,
+ f'{output_files.element}.html')
+
+ if _CanvasType.OFFSCREEN_CANVAS in self.params['canvas_types']:
+ _render(jinja_env, 'testharness_offscreen.html', self.params,
+ f'{output_files.offscreen}.html')
+
+ if _CanvasType.WORKER in self.params['canvas_types']:
+ _render(jinja_env, 'testharness_worker.js', self.params,
+ f'{output_files.offscreen}.worker.js')
+
+ def generate_test(self, jinja_env: jinja2.Environment,
+ output_dirs: _OutputPaths) -> None:
+ """Generate the test files to the specified output dirs."""
+ output_files = output_dirs.sub_path(self.params['file_name'])
+
+ if self.params['template_type'] in (_TemplateType.REFERENCE,
+ _TemplateType.HTML_REFERENCE):
+ self._write_reference_test(jinja_env, output_files)
+ else:
+ self._write_testharness_test(jinja_env, output_files)
+
+
+def _recursive_expand_variant_matrix(original_test: _TestParams,
+ variant_matrix: List[_TestParams],
current_selection: List[Tuple[str, Any]],
- original_test: Mapping[str, Any]):
+ test_variants: List[_Variant]):
if len(current_selection) == len(variant_matrix):
# Selection for each variant is done, so add a new test to test_list.
- test = original_test.copy()
+ test = dict(original_test)
variant_name_list = []
- should_append_variant_names = original_test.get(
- 'append_variants_to_name', True)
for variant_name, variant_params in current_selection:
- variant_name_list.append(variant_name)
- # Append variant name. Variant names starting with '_' are
- # not appended, which is useful to create variants with the same
- # name in different folders (element vs. offscreen).
- if (should_append_variant_names
- and not variant_name.startswith('_')):
- test['name'] += '.' + variant_name
test.update(variant_params)
- # Expose variant names as a list so they can be used from the yaml
- # files, which helps with better naming of tests.
- test.update({'variant_names': variant_name_list})
- test_list.append(test)
+ variant_name_list.append(variant_name)
+ # Expose variant names as a list so they can be used from the yaml
+ # files, which helps with better naming of tests.
+ test.update({'variant_names': variant_name_list})
+ test_variants.append(_Variant.create_with_defaults(test))
else:
# Continue the recursion with each possible selection for the current
# variant.
variant = variant_matrix[len(current_selection)]
for variant_options in variant.items():
current_selection.append(variant_options)
- _recursive_expand_variant_matrix(test_list, variant_matrix,
- current_selection, original_test)
+ _recursive_expand_variant_matrix(original_test, variant_matrix,
+ current_selection, test_variants)
current_selection.pop()
-def _expand_variant_matrix(
- variant_matrix: List[Mapping[str, Any]],
- original_test: Mapping[str, Any]) -> List[Mapping[str, Any]]:
+def _get_variants(test: _TestParams) -> List[_Variant]:
current_selection = []
- matrix_tests = []
- _recursive_expand_variant_matrix(matrix_tests, variant_matrix,
- current_selection, original_test)
- return matrix_tests
+ test_variants = []
+ variants = test.get('variants', [])
+ if not isinstance(variants, list):
+ raise InvalidTestDefinitionError(
+ textwrap.dedent("""
+ Variants must be specified as a list of variant dimensions, e.g.:
+ variants:
+ - dimension1-variant1:
+ param: ...
+ dimension1-variant2:
+ param: ...
+ - dimension2-variant1:
+ param: ...
+ dimension2-variant2:
+ param: ..."""))
+ _recursive_expand_variant_matrix(test, variants, current_selection,
+ test_variants)
+ return test_variants
+
+
+def _check_uniqueness(tested: DefaultDict[str, Set[_CanvasType]], name: str,
+ canvas_types: FrozenSet[_CanvasType]) -> None:
+ already_tested = tested[name].intersection(canvas_types)
+ if already_tested:
+ raise InvalidTestDefinitionError(
+ f'Test {name} is defined twice for types {already_tested}')
+ tested[name].update(canvas_types)
-def genTestUtils_union(NAME2DIRFILE: str) -> None:
- CANVASOUTPUTDIR = '../element'
- CANVASIMAGEOUTPUTDIR = '../element'
- OFFSCREENCANVASOUTPUTDIR = '../offscreen'
- OFFSCREENCANVASIMAGEOUTPUTDIR = '../offscreen'
+def generate_test_files(name_to_dir_file: str) -> None:
+ """Generate Canvas tests from YAML file definition."""
+ output_dirs = _OutputPaths(element='../element', offscreen='../offscreen')
jinja_env = jinja2.Environment(
- loader=jinja2.PackageLoader("gentestutilsunion"),
+ loader=jinja2.PackageLoader('gentestutilsunion'),
keep_trailing_newline=True,
trim_blocks=True,
lstrip_blocks=True)
- jinja_env.filters['double_quote_escape'] = _doubleQuoteEscape
+ jinja_env.filters['double_quote_escape'] = _double_quote_escape
# Run with --test argument to run unit tests.
if len(sys.argv) > 1 and sys.argv[1] == '--test':
@@ -531,16 +587,19 @@ def genTestUtils_union(NAME2DIRFILE: str) -> None:
doctest.testmod()
sys.exit()
- name_to_sub_dir = yaml.safe_load(pathlib.Path(NAME2DIRFILE).read_text())
+ name_to_sub_dir = (yaml.safe_load(
+ pathlib.Path(name_to_dir_file).read_text(encoding='utf-8')))
tests = []
test_yaml_directory = 'yaml-new'
- TESTSFILES = [
+ yaml_files = [
os.path.join(test_yaml_directory, f)
for f in os.listdir(test_yaml_directory) if f.endswith('.yaml')
]
- for t in sum(
- [yaml.safe_load(pathlib.Path(f).read_text()) for f in TESTSFILES], []):
+ for t in sum([
+ yaml.safe_load(pathlib.Path(f).read_text(encoding='utf-8'))
+ for f in yaml_files
+ ], []):
if 'DISABLED' in t:
continue
if 'meta' in t:
@@ -550,14 +609,11 @@ def genTestUtils_union(NAME2DIRFILE: str) -> None:
tests.append(t)
# Ensure the test output directories exist.
- testdirs = [
- CANVASOUTPUTDIR, OFFSCREENCANVASOUTPUTDIR, CANVASIMAGEOUTPUTDIR,
- OFFSCREENCANVASIMAGEOUTPUTDIR
- ]
+ test_dirs = [output_dirs.element, output_dirs.offscreen]
for sub_dir in set(name_to_sub_dir.values()):
- testdirs.append('%s/%s' % (CANVASOUTPUTDIR, sub_dir))
- testdirs.append('%s/%s' % (OFFSCREENCANVASOUTPUTDIR, sub_dir))
- for d in testdirs:
+ test_dirs.append(f'{output_dirs.element}/{sub_dir}')
+ test_dirs.append(f'{output_dirs.offscreen}/{sub_dir}')
+ for d in test_dirs:
try:
os.mkdir(d)
except FileExistsError:
@@ -565,24 +621,19 @@ def genTestUtils_union(NAME2DIRFILE: str) -> None:
used_tests = collections.defaultdict(set)
for test in tests:
- if 'variant_matrix' in test:
- variants = _expand_variant_matrix(test['variant_matrix'], test)
- elif 'variants' in test:
- variant_matrix = [test['variants']]
- variants = _expand_variant_matrix(variant_matrix, test)
- else:
- variants = [test]
-
- for variant in variants:
- _generate_test(variant,
- jinja_env,
- name_to_sub_dir,
- used_tests,
- html_canvas_cfg=TestConfig(
- out_dir=CANVASOUTPUTDIR,
- image_out_dir=CANVASIMAGEOUTPUTDIR),
- offscreen_canvas_cfg=TestConfig(
- out_dir=OFFSCREENCANVASOUTPUTDIR,
- image_out_dir=OFFSCREENCANVASIMAGEOUTPUTDIR))
+ print(test['name'])
+ for variant in _get_variants(test):
+ variant.finalize_params(jinja_env)
+ if test['name'] != variant.params['name']:
+ print(f' {variant.params["name"]}')
+
+ sub_dir = _get_test_sub_dir(variant.params['file_name'],
+ name_to_sub_dir)
+ output_sub_dirs = output_dirs.sub_path(sub_dir)
+
+ _check_uniqueness(used_tests, variant.params['name'],
+ variant.params['canvas_types'])
+ variant.generate_expected_image(output_sub_dirs)
+ variant.generate_test(jinja_env, output_sub_dirs)
print()