Coverage for src/debputy/linting/lint_report_junit.py: 27%
41 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-10-12 15:06 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-10-12 15:06 +0000
1import textwrap
2from typing import TYPE_CHECKING
3from collections.abc import Iterable
5from debputy.util import _info
7if TYPE_CHECKING:
8 from junit_xml import TestSuite, TestCase, to_xml_report_file
9else:
10 try:
11 from junit_xml import TestSuite, TestCase, to_xml_report_file
12 except ImportError:
13 pass
16from debputy.linting.lint_util import (
17 LintReport,
18 LintDiagnosticResult,
19 LintDiagnosticResultState,
20 debputy_severity,
21)
24class JunitLintReport(LintReport):
26 def __init__(self, output_filename: str) -> None:
27 super().__init__()
28 self._output_filename = output_filename
30 def finish_report(self) -> None:
31 # Nothing to do for now
32 all_test_cases = list(self._as_test_cases())
33 test_suites = [
34 TestSuite(
35 "debputy lint",
36 test_cases=all_test_cases,
37 timestamp=str(self.start_timestamp),
38 )
39 ]
40 with open(self._output_filename, "w", encoding="utf-8") as wfd:
41 to_xml_report_file(wfd, test_suites, encoding="utf-8")
42 _info(f"Wrote {self._output_filename}")
44 def _as_test_cases(self) -> Iterable["TestCase"]:
45 for filename, duration in self.durations.items():
46 results = self.diagnostics_by_file.get(filename, [])
47 yield self._as_test_case(filename, results, duration)
49 def _as_test_case(
50 self,
51 filename: str,
52 diagnostic_results: Iterable[LintDiagnosticResult],
53 duration: float,
54 ) -> "TestCase":
55 if not duration:
56 duration = 0.000001
57 case = TestCase(
58 filename,
59 # The JUnit schema has `classname` as mandatory
60 classname=filename,
61 allow_multiple_subelements=True,
62 elapsed_sec=duration,
63 )
64 for diagnostic_result in diagnostic_results:
65 if diagnostic_result.result_state == LintDiagnosticResultState.FIXED:
66 continue
67 diagnostic = diagnostic_result.diagnostic
68 severity = debputy_severity(diagnostic)
69 if diagnostic_result.is_file_level_diagnostic:
70 range_desc = "entire file"
71 else:
72 range_desc = str(diagnostic.range)
73 code = f" [{diagnostic.code}]" if diagnostic.code else ""
74 authority = diagnostic.source
75 assert authority is not None
76 output = textwrap.dedent(
77 f"""\
78 {filename}{code} ({severity}) {range_desc} [{authority}]: {diagnostic.message}
79 """
80 )
81 case.add_failure_info(
82 message=f"[{severity}]: {diagnostic.message}",
83 output=output,
84 )
85 return case