Coverage for src / ocarina / opinionated / plugins / reports / pretty_print_results.py: 0.00%

68 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-06-04 15:56 +0200

1# ruff: noqa: T201 

2"""Displays the full test results. 

3 

4This module provides a pretty printer to display, in a structured and readable 

5way, the results of a complete test suite. It supports hierarchical display 

6(campaigns > suites > cases) with optional colorization. 

7 

8Architecture: 

9 - Recursive traversal of the results structure 

10 - Dispatch pattern to map outcomes → textual statuses 

11 - Conditional colorization via ANSI codes 

12 - Statistics accumulation for final summary 

13 

14Output format: 

15 Campaign 

16 • Suite 

17 > Test case 1 

18 » PASSED 

19 > Test case 2 

20 » FAILED 

21 → Error message 

22 ⫸ At step 3 

23 > Test case 3 

24 » SKIPPED 

25 

26 Test results: 

27 1 FAILED | 1 PASSED | 1 SKIPPED 

28 

29Features: 

30 - ANSI color support (green/red) 

31 - Error message display with context (step number) 

32 - Smart spacing between groups 

33 

34Typical usage: 

35 >>> results = run_all_tests() 

36 >>> pretty_print_results(results, with_colors=True) 

37 

38""" 

39 

40from typing import TYPE_CHECKING, Final 

41 

42from ocarina.aggregates.tests_layers import ( 

43 is_test_result_fail, 

44 is_test_result_ok, 

45 is_test_result_skipped, 

46) 

47from ocarina.railway.result import Fail, Ok 

48 

49if TYPE_CHECKING: 

50 from ocarina.custom_types.oc_test_layers import ( 

51 TestCycleResults, 

52 ) 

53 

54_FAIL: Final[str] = "FAILED" 

55_SUCCESS: Final[str] = "PASSED" 

56_SKIPPED: Final[str] = "SKIPPED" 

57_FINAL_SUMUP_HEADLINE: Final[str] = "Test results:" 

58 

59_STATUS_DISPATCH = { 

60 Fail: _FAIL, 

61 Ok: _SUCCESS, 

62 type(None): _SKIPPED, 

63} 

64 

65_GREEN = "\033[32m" 

66_RED = "\033[31m" 

67_COLOR_RESET = "\033[0m" 

68 

69 

70def _color_text(text: str, color_code: str) -> str: 

71 return f"{color_code}{text}{_COLOR_RESET}" 

72 

73 

74def _muted_color_text(text: str, color_code: str) -> str: # noqa: ARG001 

75 return text 

76 

77 

78def _upcase(word: str) -> str: 

79 return word.upper() 

80 

81 

82def pretty_print_results( # noqa: PLR0912 

83 results: TestCycleResults, *, with_colors: bool = False 

84) -> None: 

85 """Pretty printer.""" 

86 _color = _color_text if with_colors else _muted_color_text 

87 

88 count_ok = 0 

89 count_fail = 0 

90 count_ignored = 0 

91 

92 campaigns_output: list[list[str]] = [] 

93 

94 for campaign_name, campaigns in results.items(): 

95 campaign_lines: list[str] = [] 

96 

97 for suite_name, tests in campaigns.items(): 

98 if not tests: 

99 continue 

100 

101 suite_lines: list[str] = [f"{suite_name}"] 

102 

103 for test_name, (outcome, counter, _) in tests.items(): 

104 status = _STATUS_DISPATCH.get(type(outcome), "???") 

105 suite_lines.append(f"{test_name}") # noqa: RUF001 

106 

107 if is_test_result_fail(outcome): 

108 count_fail += 1 

109 suite_lines.append(_color(f" » {status}", _RED)) 

110 error_msg = str(outcome.error).rstrip("\n") if outcome.error else "" 

111 if error_msg: 

112 suite_lines.append(_color(f"{error_msg}", _RED)) 

113 suite_lines.append(_color(f" ⫸ At step {counter}", _RED)) 

114 else: 

115 suite_lines.append(_color(f" → At step {counter}", _RED)) 

116 elif is_test_result_ok(outcome): 

117 count_ok += 1 

118 suite_lines.append(_color(f" » {status}", _GREEN)) 

119 elif is_test_result_skipped(outcome): 

120 count_ignored += 1 

121 suite_lines.append(f" » {status}") 

122 else: 

123 suite_lines.append(f" » {status}") 

124 

125 campaign_lines.extend(suite_lines) 

126 

127 if campaign_lines: 

128 campaigns_output.append([campaign_name, *campaign_lines]) 

129 

130 if len(campaigns_output) > 1: 

131 print() 

132 

133 for i, campaign_lines in enumerate(campaigns_output): 

134 for line in campaign_lines: 

135 print(line) 

136 if i < len(campaigns_output) - 1: 

137 print() 

138 

139 summary_parts = [] 

140 if count_fail > 0: 

141 summary_parts.append(_color(f"{count_fail} {_upcase(_FAIL)}", _RED)) 

142 if count_ok > 0: 

143 summary_parts.append(_color(f"{count_ok} {_upcase(_SUCCESS)}", _GREEN)) 

144 if count_ignored > 0: 

145 summary_parts.append(f"{count_ignored} {_upcase(_SKIPPED)}") 

146 

147 if summary_parts: 

148 print() 

149 print(_FINAL_SUMUP_HEADLINE) 

150 print(" | ".join(summary_parts))