From 816fefd3b69dc9f18156eff586848b1b2f6d8c7f Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Thu, 10 Sep 2020 14:41:12 -0400 Subject: [PATCH] tests/tracing: use pytest_collect_file() hook for YAML files This patch applies the same strategy as b1c5cbc ("tests/config/yaml: use pytest_collect_file() hook for YAML files") to `tests/tracing`. This patch changes `tests/tracing/conftest.py` so that it implements a pytest_collect_file() Pytest hook to create Pytest file and item objects for each barectf YAML configuration file found in the `configs` directory. Thanks to the base name of this YAML file, pytest_collect_file() automatically finds the corresponding test-specific C source and expectation files. This makes is possible to remove `test_succeed_static_array.py` and avoid duplication and mismatches between existing YAML files and available test functions. Signed-off-by: Philippe Proulx --- tests/tracing/conftest.py | 168 ++++++++++++--------- tests/tracing/test_succeed_static_array.py | 61 -------- 2 files changed, 97 insertions(+), 132 deletions(-) delete mode 100644 tests/tracing/test_succeed_static_array.py diff --git a/tests/tracing/conftest.py b/tests/tracing/conftest.py index 2c3366b..331b39b 100644 --- a/tests/tracing/conftest.py +++ b/tests/tracing/conftest.py @@ -27,67 +27,87 @@ import os.path import barectf import shutil import subprocess - - -@pytest.fixture -def tracing_succeed_test(yaml_cfg_path, request, tmpdir): - def func(): - test_dir = os.path.dirname(request.fspath) - - # Use the test's module and function names to automatically find - # the test-specific expectation files. - # - # For: - # - # Test module name: - # `test_succeed_hello_there.py` - # - # Test function name: - # `test_how_are_you` - # - # The corresponding base expectation file path is - # `expect/succeed/hello-there/how-are-you'. - elems = [test_dir, 'expect'] - mod = request.module.__name__ - mod = mod.replace('test_', '') - mod = mod.replace('_', '-') - parts = mod.split('-') - elems.append(parts[0]) - elems.append('-'.join(parts[1:])) - func = request.function.__name__ - func = func.replace('test_', '') - func = func.replace('_', '-') - elems.append(func) - expect_base_path = os.path.join(*elems) - - # Use the test's module and function names to automatically find - # the test-specific C source file. - # - # For: - # - # Test module name: - # `test_succeed_hello_there.py` - # - # Test function name: - # `test_how_are_you` - # - # The corresponding expectation file path is - # `src/succeed/hello-there/how-are-you.c'. - elems = [test_dir, 'src'] - mod = request.module.__name__ - mod = mod.replace('test_', '') - mod = mod.replace('_', '-') - parts = mod.split('-') - elems.append(parts[0]) - elems.append('-'.join(parts[1:])) - func = request.function.__name__ - func = func.replace('test_', '') - func = func.replace('_', '-') - elems.append(f'{func}.c') - src_path = os.path.join(*elems) +import tempfile + + +def pytest_collect_file(parent, path): + yaml_ext = '.yaml' + + if path.ext != yaml_ext: + # not a YAML file: cancel + return + + # At the end of this loop, if `path` is + # `/home/jo/barectf/tests/tracing/configs/succeed/static-array/of-str.yaml`, + # for example, then `elems` is: + # + # * `of-str.yaml` + # * `static-array` + # * `succeed` + path_str = str(path) + elems = [] + + while True: + elem = os.path.basename(path_str) + + if elem == 'configs': + break + + elems.append(elem) + path_str = os.path.dirname(path_str) + + # create C source, expectation file, and support directory paths + base_dir = os.path.dirname(path_str) + base_name = elems[0].replace(yaml_ext, '') + rel_dir = os.path.join(*list(reversed(elems[1:]))) + src_path = os.path.join(*[base_dir, 'src', rel_dir, f'{base_name}.c']) + data_expect_path = os.path.join(*([base_dir, 'expect', rel_dir, f'{base_name}.data.expect'])) + metadata_expect_path = os.path.join(*([base_dir, 'expect', rel_dir, f'{base_name}.metadata.expect'])) + support_dir_path = os.path.join(base_dir, 'support') + + # create a unique test name + name = f'test-{"-".join(reversed(elems))}'.replace(yaml_ext, '') + + # create the file node + return _YamlFile.from_parent(parent, fspath=path, src_path=src_path, + data_expect_path=data_expect_path, + metadata_expect_path=metadata_expect_path, + support_dir_path=support_dir_path, name=name) + + +class _YamlFile(pytest.File): + def __init__(self, parent, fspath, src_path, data_expect_path, metadata_expect_path, + support_dir_path, name): + super().__init__(parent=parent, fspath=fspath) + self._name = name + self._src_path = src_path + self._data_expect_path = data_expect_path + self._metadata_expect_path = metadata_expect_path + self._support_dir_path = support_dir_path + + def collect(self): + # yield a single item + yield _YamlItem.from_parent(self, name=self._name, src_path=self._src_path, + data_expect_path=self._data_expect_path, + metadata_expect_path=self._metadata_expect_path, + support_dir_path=self._support_dir_path) + + +class _YamlItem(pytest.Item): + def __init__(self, parent, name, src_path, data_expect_path, metadata_expect_path, + support_dir_path): + super().__init__(parent=parent, name=name) + self._src_path = src_path + self._data_expect_path = data_expect_path + self._metadata_expect_path = metadata_expect_path + self._support_dir_path = support_dir_path + + def runtest(self): + # create a temporary directory + tmpdir = tempfile.TemporaryDirectory(prefix='pytest-barectf') # create barectf configuration - with open(yaml_cfg_path) as f: + with open(self.fspath) as f: cfg = barectf.configuration_from_file(f) # generate and write C code files @@ -96,7 +116,7 @@ def tracing_succeed_test(yaml_cfg_path, request, tmpdir): files += cg.generate_c_sources() for file in files: - with open(os.path.join(tmpdir, file.name), 'w') as f: + with open(os.path.join(tmpdir.name, file.name), 'w') as f: f.write(file.contents) # generate metadata stream, stripping the version and date @@ -128,36 +148,42 @@ def tracing_succeed_test(yaml_cfg_path, request, tmpdir): actual_metadata = '\n'.join(new_lines) # copy Makefile to build directory - support_dir = os.path.join(test_dir, 'support') - shutil.copy(os.path.join(support_dir, 'Makefile'), tmpdir) + shutil.copy(os.path.join(self._support_dir_path, 'Makefile'), tmpdir.name) # copy platform files to build directory - shutil.copy(os.path.join(support_dir, 'test-platform.c'), tmpdir) - shutil.copy(os.path.join(support_dir, 'test-platform.h'), tmpdir) + shutil.copy(os.path.join(self._support_dir_path, 'test-platform.c'), tmpdir.name) + shutil.copy(os.path.join(self._support_dir_path, 'test-platform.h'), tmpdir.name) # copy specific source code file to build directory - shutil.copy(src_path, os.path.join(tmpdir, 'test.c')) + shutil.copy(self._src_path, os.path.join(tmpdir.name, 'test.c')) # build the test - subprocess.check_output(['make'], cwd=tmpdir) + subprocess.check_output(['make'], cwd=tmpdir.name) # run the test (produce the data stream) - subprocess.check_output(['./test'], cwd=tmpdir) + subprocess.check_output(['./test'], cwd=tmpdir.name) # read actual stream - with open(os.path.join(tmpdir, 'stream'), 'rb') as f: + with open(os.path.join(tmpdir.name, 'stream'), 'rb') as f: actual_stream = f.read() # read data stream expectation file - with open(f'{expect_base_path}.data.expect', 'rb') as f: + with open(self._data_expect_path, 'rb') as f: expected_stream = f.read() # read metadata stream expectation file - with open(f'{expect_base_path}.metadata.expect', 'r') as f: + with open(self._metadata_expect_path, 'r') as f: expected_metadata = f.read() # validate streams assert actual_metadata == expected_metadata assert actual_stream == expected_stream - return func + # delete temporary directory + tmpdir.cleanup() + + def repr_failure(self, excinfo, style=None): + return f'`{self.fspath}` failed: {excinfo}.' + + def reportinfo(self): + return self.fspath, None, self.name diff --git a/tests/tracing/test_succeed_static_array.py b/tests/tracing/test_succeed_static_array.py deleted file mode 100644 index f815407..0000000 --- a/tests/tracing/test_succeed_static_array.py +++ /dev/null @@ -1,61 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2020 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -def test_nested_5_uint8(tracing_succeed_test): - tracing_succeed_test() - - -def test_of_double(tracing_succeed_test): - tracing_succeed_test() - - -def test_of_static_array_of_double(tracing_succeed_test): - tracing_succeed_test() - - -def test_of_static_array_of_str(tracing_succeed_test): - tracing_succeed_test() - - -def test_of_static_array_of_uint8(tracing_succeed_test): - tracing_succeed_test() - - -def test_of_str(tracing_succeed_test): - tracing_succeed_test() - - -def test_of_uint3(tracing_succeed_test): - tracing_succeed_test() - - -def test_of_uint3_middle(tracing_succeed_test): - tracing_succeed_test() - - -def test_of_uint8(tracing_succeed_test): - tracing_succeed_test() - - -def test_zero_len(tracing_succeed_test): - tracing_succeed_test() -- 2.34.1