Coverage for /home/crpier/Projects/snektest/snektest/presenter/errors.py: 40%

70 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-07 00:30 +0300

1from __future__ import annotations 

2 

3from dataclasses import dataclass 

4from typing import cast 

5 

6from rich.console import Console 

7 

8from snektest.models import ( 

9 AssertionFailure, 

10 ErrorResult, 

11 FailedResult, 

12 TeardownFailure, 

13 TestResult, 

14) 

15from snektest.presenter.diff import render_assertion_failure 

16from snektest.presenter.traceback import render_traceback 

17 

18 

19@dataclass(frozen=True) 

20class FailureGroups: 

21 failures: list[TestResult] 

22 errors: list[TestResult] 

23 fixture_teardown_failures: list[TestResult] 

24 

25 

26def _collect_failure_groups(test_results: list[TestResult]) -> FailureGroups: 

27 failures = [ 

28 result for result in test_results if isinstance(result.result, FailedResult) 

29 ] 

30 errors = [ 

31 result for result in test_results if isinstance(result.result, ErrorResult) 

32 ] 

33 fixture_teardown_failures = [ 

34 result for result in test_results if result.fixture_teardown_failures 

35 ] 

36 return FailureGroups( 

37 failures=failures, 

38 errors=errors, 

39 fixture_teardown_failures=fixture_teardown_failures, 

40 ) 

41 

42 

43def _print_optional_output(console: Console, *, title: str, output: str | None) -> None: 

44 if not output: 

45 return 

46 console.print() 

47 console.print(title, style="yellow", markup=False) 

48 console.print(output, markup=False, highlight=False) 

49 

50 

51def _print_result_details( 

52 console: Console, 

53 *, 

54 result: TestResult, 

55 outcome: FailedResult | ErrorResult, 

56) -> None: 

57 exc_type = outcome.exc_type 

58 exc_value = outcome.exc_value 

59 traceback = outcome.traceback 

60 

61 if isinstance(exc_value, AssertionFailure): 

62 render_traceback( 

63 console, 

64 exc_type, 

65 exc_value, 

66 traceback, 

67 show_exception_line=False, 

68 ) 

69 render_assertion_failure(console, exc_value) 

70 else: 

71 render_traceback(console, exc_type, exc_value, traceback) 

72 

73 _print_optional_output( 

74 console, 

75 title="Captured output:", 

76 output=result.captured_output.getvalue(), 

77 ) 

78 _print_optional_output( 

79 console, 

80 title="Captured output from fixture teardowns:", 

81 output=result.fixture_teardown_output, 

82 ) 

83 

84 

85def _print_test_failures(console: Console, failures: list[TestResult]) -> None: 

86 for result in failures: 

87 console.rule(f"{result.name}", style="bold red") 

88 failing_result = cast("FailedResult", result.result) 

89 

90 _print_result_details( 

91 console, 

92 result=result, 

93 outcome=failing_result, 

94 ) 

95 

96 

97def _print_test_errors(console: Console, errors: list[TestResult]) -> None: 

98 for result in errors: 

99 console.rule(f"{result.name}", style="bold dark_orange") 

100 error_result = cast("ErrorResult", result.result) 

101 

102 _print_result_details( 

103 console, 

104 result=result, 

105 outcome=error_result, 

106 ) 

107 

108 

109def _print_fixture_teardown_failures( 

110 console: Console, fixture_teardown_failures: list[TestResult] 

111) -> None: 

112 for result in fixture_teardown_failures: 

113 for teardown_failure in result.fixture_teardown_failures: 

114 console.rule( 

115 f"{result.name} - Fixture teardown: {teardown_failure.fixture_name}", 

116 style="bold red", 

117 ) 

118 render_traceback( 

119 console, 

120 teardown_failure.exc_type, 

121 teardown_failure.exc_value, 

122 teardown_failure.traceback, 

123 ) 

124 

125 _print_optional_output( 

126 console, 

127 title="Captured output from fixture teardowns:", 

128 output=result.fixture_teardown_output, 

129 ) 

130 

131 

132def _print_session_teardown_failures( 

133 console: Console, session_teardown_failures: list[TeardownFailure] 

134) -> None: 

135 for teardown_failure in session_teardown_failures: 

136 console.rule( 

137 f"Session fixture teardown: {teardown_failure.fixture_name}", 

138 style="bold red", 

139 ) 

140 render_traceback( 

141 console, 

142 teardown_failure.exc_type, 

143 teardown_failure.exc_value, 

144 teardown_failure.traceback, 

145 ) 

146 

147 

148def print_failures( 

149 console: Console, 

150 test_results: list[TestResult], 

151 session_teardown_failures: list[TeardownFailure] | None = None, 

152 session_teardown_output: str | None = None, 

153) -> None: 

154 """Print all test failures, fixture teardown failures, and session teardown failures.""" 

155 if session_teardown_failures is None: 

156 session_teardown_failures = [] 

157 

158 groups = _collect_failure_groups(test_results) 

159 

160 if ( 

161 not groups.failures 

162 and not groups.errors 

163 and not groups.fixture_teardown_failures 

164 and not session_teardown_failures 

165 ): 

166 return 

167 

168 console.print() 

169 console.rule("FAILURES", style="bold orange3", characters="=") 

170 console.print() 

171 

172 _print_test_failures(console, groups.failures) 

173 _print_test_errors(console, groups.errors) 

174 _print_fixture_teardown_failures(console, groups.fixture_teardown_failures) 

175 _print_session_teardown_failures(console, session_teardown_failures) 

176 

177 if session_teardown_output and (groups.failures or groups.errors): 

178 console.print() 

179 console.rule( 

180 "Output from session fixture teardowns", 

181 style="bold yellow", 

182 ) 

183 console.print(session_teardown_output, markup=False, highlight=False)