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

1# SPDX-License-Identifier: GPL-3.0-or-later 

2"""Pytest macro helpers for cikit. 

3 

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. 

7 

8Author: 

9 Sven Schlender 

10Date: 

11 2026-05-28 

12""" 

13 

14from pathlib import Path 

15from tempfile import TemporaryDirectory 

16 

17from cikit.command import Command, run_command 

18from cikit.errors import CiRecoverableError 

19 

20from .reports import process_test_result 

21 

22 

23def build_coverage_command(coverage_file: Path, module: str = "pytest") -> Command: 

24 """Build a coverage command for running a Python module. 

25 

26 Args: 

27 coverage_file: Base path used for coverage data files. 

28 module: Python module executed via ``python -m`` under coverage. 

29 

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] 

35 

36 return Command("coverage", common_args=common_args, use_exec_tool=True) 

37 

38 

39def pytest(env, test_path: Path, test_alias: str, *, coverage_dir: Path = None): 

40 """Run one pytest test directory. 

41 

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. 

45 

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. 

52 

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) 

58 

59 if not test_path.exists(): 

60 raise CiRecoverableError(f"Directory <{test_path}> does not exist, skipping.") 

61 

62 test_report_basename = f"{test_alias}-report.xml" 

63 

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)] 

67 

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) 

74 

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) 

78 

79 process_test_result(env, test_alias, test_report_file, proc_result.returncode)