tests/tracing: use pytest_collect_file() hook for YAML files
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Thu, 10 Sep 2020 18:41:12 +0000 (14:41 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Thu, 10 Sep 2020 18:41:12 +0000 (14:41 -0400)
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 <eeppeliteloop@gmail.com>
tests/tracing/conftest.py
tests/tracing/test_succeed_static_array.py [deleted file]

index 2c3366b01af64bb89c0d88e7142b6118ca525bb8..331b39b724b13ae02d64f26ce31c054bdaaa3a3e 100644 (file)
@@ -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 (file)
index f815407..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-# The MIT License (MIT)
-#
-# Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
-#
-# 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()
This page took 0.02582 seconds and 4 git commands to generate.