Coverage for src/debputy/linting/lint_report_junit.py: 25%

40 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2025-03-24 16:38 +0000

1import textwrap 

2from typing import Iterable, TYPE_CHECKING 

3 

4from debputy.util import _info 

5 

6if TYPE_CHECKING: 

7 from junit_xml import TestSuite, TestCase, to_xml_report_file 

8else: 

9 try: 

10 from junit_xml import TestSuite, TestCase, to_xml_report_file 

11 except ImportError: 

12 pass 

13 

14 

15from debputy.linting.lint_util import ( 

16 LintReport, 

17 LintDiagnosticResult, 

18 LintDiagnosticResultState, 

19 debputy_severity, 

20) 

21 

22 

23class JunitLintReport(LintReport): 

24 

25 def __init__(self, output_filename: str) -> None: 

26 super().__init__() 

27 self._output_filename = output_filename 

28 

29 def finish_report(self) -> None: 

30 # Nothing to do for now 

31 all_test_cases = list(self._as_test_cases()) 

32 test_suites = [ 

33 TestSuite( 

34 "debputy lint", 

35 test_cases=all_test_cases, 

36 timestamp=str(self.start_timestamp), 

37 ) 

38 ] 

39 with open(self._output_filename, "w", encoding="utf-8") as wfd: 

40 to_xml_report_file(wfd, test_suites, encoding="utf-8") 

41 _info(f"Wrote {self._output_filename}") 

42 

43 def _as_test_cases(self) -> Iterable["TestCase"]: 

44 for filename, duration in self.durations.items(): 

45 results = self.diagnostics_by_file.get(filename, []) 

46 yield self._as_test_case(filename, results, duration) 

47 

48 def _as_test_case( 

49 self, 

50 filename: str, 

51 diagnostic_results: Iterable[LintDiagnosticResult], 

52 duration: float, 

53 ) -> "TestCase": 

54 if not duration: 

55 duration = 0.000001 

56 case = TestCase( 

57 filename, 

58 # The JUnit schema has `classname` as mandatory 

59 classname=filename, 

60 allow_multiple_subelements=True, 

61 elapsed_sec=duration, 

62 ) 

63 for diagnostic_result in diagnostic_results: 

64 if diagnostic_result.result_state == LintDiagnosticResultState.FIXED: 

65 continue 

66 diagnostic = diagnostic_result.diagnostic 

67 severity = debputy_severity(diagnostic) 

68 if diagnostic_result.is_file_level_diagnostic: 

69 range_desc = "entire file" 

70 else: 

71 range_desc = str(diagnostic.range) 

72 code = f" [{diagnostic.code}]" if diagnostic.code else "" 

73 authority = diagnostic.source 

74 assert authority is not None 

75 output = textwrap.dedent( 

76 f"""\ 

77 \ 

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