# Copyright 2021-2025 XMOS LIMITED. # This Software is subject to the terms of the XMOS Public Licence: Version 1. import pytest import subprocess from pathlib import Path import re import os from audio_dsp.design import pipeline from stages.dummy import Dummy def gen_dummy_pipeline(): p = pipeline.Pipeline(1, identifier="dummy") d = p.stage(Dummy, p.i, label="dummy") p.set_outputs(d) pipeline.generate_dsp_main(p, out_dir=Path(__file__).parent / "build/dummy_pipeline") def pytest_configure(): worker_id = os.environ.get("PYTEST_XDIST_WORKER") if worker_id is None: # dont run on the worker threads gen_dummy_pipeline() if not Path("bin").exists(): subprocess.run(["cmake", "-B", "build"], check=True) subprocess.run(["cmake", "--build", "build"], check=True) ## Begin pytest magic to convert xe files into tests def pytest_collect_file(parent, path): """Custom collection function to inform pytest that xe files contain tests.""" if path.ext == ".xe": return UnityTestSource.from_parent(parent, path=Path(path)) class UnityTestSource(pytest.File): """ Each xe file contains 1 pytest test. """ def collect(self): yield UnityTestExecutable.from_parent(self, xe=self.path, name=self.path.stem) class UnityTestExecutable(pytest.Item): """ Run the xe file in xsim, this is the work of the test. """ def __init__(self, xe, **kwargs): super().__init__(**kwargs) self.xe=xe self.fail_reason=[] def runtest(self): """ fancy test output processing. """ proc = subprocess.run(["xsim", self.xe], text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) self.add_report_section("call", "stdout", proc.stdout) unity_result_pattern=r"^(?P[^\n:]+):(?P\d+):(?P[^:]+):(?PPASS|FAIL)(: (?P.*))?$" unlikely_repl = "unlikely_repl" result = [i for i in re.finditer(unity_result_pattern, proc.stdout, re.MULTILINE)] all_out = [i for i in re.sub(unity_result_pattern, unlikely_repl, proc.stdout, flags=re.MULTILINE).split(unlikely_repl)] for match, output in zip(result, all_out): file, line, test_name, status, message = match.group("path", "line", "name", "status", "message") fail_reason = f"{test_name}:{line}:{message}" self.add_report_section("call", f"{status} {test_name}", output + "\n" + fail_reason) if status == "FAIL": self.fail_reason.append(fail_reason) if proc.returncode: raise UnityTestException def repr_failure(self, excinfo): if isinstance(excinfo.value, UnityTestException): return "Failure summary:\n\t" + "\n\t".join(self.fail_reason) return super().repr_failure(excinfo) def reportinfo(self): return self.path, 0, self.xe.stem class UnityTestException(Exception): pass ## End pytest magic