Coverage for /builds/svesch/cikit/src/cikit/macros/pytest.py: 100%
25 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-09 14:17 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-09 14:17 +0000
1# SPDX-License-Identifier: GPL-3.0-or-later
2"""Pytest macro helpers for cikit.
4This module runs pytest test directories, writes JUnit XML reports, stores
5test result metrics in cikit data, creates test badges and generates small
6Sphinx RST result pages.
8Author:
9 Sven Schlender
10Date:
11 2026-05-28
12"""
14from pathlib import Path
15from tempfile import TemporaryDirectory
17from cikit.command import Command, run_command
18from cikit.errors import CiRecoverableError
20from .reports import process_test_result
23def build_coverage_command(coverage_file: Path, module: str = "pytest") -> Command:
24 """Build a coverage command for running a Python module.
26 Args:
27 coverage_file: Base path used for coverage data files.
28 module: Python module executed via ``python -m`` under coverage.
30 Returns:
31 Command configured for ``coverage run`` with parallel data files.
32 """
33 common_args = ["run", "--parallel-mode", "--branch", "--data-file", str(coverage_file), "-m",
34 module]
36 return Command("coverage", common_args=common_args, use_exec_tool=True)
39def pytest(env, test_path: Path, test_alias: str, *, coverage_dir: Path = None):
40 """Run one pytest test directory.
42 The macro creates a JUnit XML report and optionally executes pytest through
43 coverage. If ``coverage_dir`` is provided, parallel coverage data files are
44 written there for later processing by a coverage job.
46 Args:
47 env: Resolved CI run environment.
48 test_path: Test directory to execute.
49 test_alias: Logical name used for reports and cikit data keys.
50 coverage_dir: Optional directory for parallel coverage data files. If
51 provided, pytest is executed through coverage.
53 Raises:
54 CiRecoverableError: If the test directory is missing or pytest exits
55 with a non-zero return code.
56 """
57 test_path = env.resolve_path(test_path)
59 if not test_path.exists():
60 raise CiRecoverableError(f"Directory <{test_path}> does not exist, skipping.")
62 test_report_basename = f"{test_alias}-report.xml"
64 with TemporaryDirectory() as temp_dir:
65 test_report_file = Path(temp_dir) / test_report_basename
66 args = [str(test_path), "--junitxml", str(test_report_file)]
68 if coverage_dir is not None:
69 coverage_dir = env.resolve_path(coverage_dir)
70 coverage_dir.mkdir(parents=True, exist_ok=True)
71 cmd = build_coverage_command(coverage_dir / ".coverage", "pytest")
72 else:
73 cmd = Command("pytest", use_exec_tool=True)
75 cmd.add_subcommand("run", args)
76 argv = env.argv(cmd, "run")
77 proc_result = run_command(argv, cwd=env["PROJECT_ROOT"], check_exit_code=False)
79 process_test_result(env, test_alias, test_report_file, proc_result.returncode)