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
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-07 00:30 +0300
1from __future__ import annotations
3from dataclasses import dataclass
4from typing import cast
6from rich.console import Console
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
19@dataclass(frozen=True)
20class FailureGroups:
21 failures: list[TestResult]
22 errors: list[TestResult]
23 fixture_teardown_failures: list[TestResult]
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 )
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)
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
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)
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 )
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)
90 _print_result_details(
91 console,
92 result=result,
93 outcome=failing_result,
94 )
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)
102 _print_result_details(
103 console,
104 result=result,
105 outcome=error_result,
106 )
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 )
125 _print_optional_output(
126 console,
127 title="Captured output from fixture teardowns:",
128 output=result.fixture_teardown_output,
129 )
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 )
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 = []
158 groups = _collect_failure_groups(test_results)
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
168 console.print()
169 console.rule("FAILURES", style="bold orange3", characters="=")
170 console.print()
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)
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)