Skip to content

Commit 24e7820

Browse files
committed
Simplify tests, reduce comments
1 parent 589e6a7 commit 24e7820

4 files changed

Lines changed: 90 additions & 225 deletions

File tree

‎Lib/test/libregrtest/reporter.py‎

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,54 +5,43 @@
55
from _colorize import decolor # type: ignore[import-not-found]
66
from typing import TextIO
77

8+
from .findtests import findtestdir
89
from .result import TestResult
910
lazy from .results import TestResults
1011
from .runtests import RunTests
1112
from .utils import count
1213

1314

1415
class Reporter:
15-
"""Report test failures, for example to a CI service.
16-
17-
The default reporter does nothing.
18-
"""
16+
"""Report test failures, for example to a CI service. A no-op here."""
1917

2018
def run_started(self, runtests: RunTests) -> None:
21-
"""Start a run of tests.
22-
23-
Called in the main process at the start of each run (the first run
24-
and the re-run), and in the process running the tests before each
25-
test file.
26-
"""
19+
"""Called at the start of each run, and in the test process."""
2720

2821
def test_case_failed(self, test_case: str, traceback: str,
2922
stream: TextIO) -> None:
30-
"""Report a failed test case.
31-
32-
Called just after unittest printed the failure, or at the end of the
33-
test file in quiet mode.
34-
"""
23+
"""Called in the test process after a test case failed."""
3524

3625
def test_result(self, result: TestResult) -> None:
37-
"""Report the result of a test file, in the main process."""
26+
"""Called in the main process after a test file completed."""
3827

3928
def summary(self, results: TestResults, state: str) -> None:
40-
"""Report the results of all runs, in the main process."""
29+
"""Called in the main process after all runs."""
4130

4231
@staticmethod
4332
def parse_traceback(traceback: str) -> tuple[str | None, str | None, str]:
44-
"""Parse the traceback of a failure into (filename, line, exception).
45-
46-
The location is the last frame in a test file, or else the first frame
47-
(the test).
48-
"""
33+
"""Return (filename, line, exception) of a failure."""
4934
frames = re.findall(r'^ *File "(.*)", line (\d+)', traceback,
5035
re.MULTILINE)
5136
test_frames = [frame for frame in frames
5237
if pathlib.Path(frame[0]).name.startswith('test_')]
53-
filename, line = (test_frames[-1] if test_frames
54-
else frames[0] if frames
55-
else (None, None))
38+
# Prefer the last frame in a test file, the assertion
39+
if test_frames:
40+
filename, line = test_frames[-1]
41+
elif frames:
42+
filename, line = frames[0]
43+
else:
44+
filename = line = None
5645
exception = re.split(r'^ File .*\n(?: .*\n)*', traceback,
5746
flags=re.MULTILINE)[-1].strip()
5847
return filename, line, exception
@@ -65,10 +54,8 @@ def get_reporter() -> Reporter:
6554

6655

6756
class GitHubActionsReporter(Reporter):
68-
"""Annotate failures in the GitHub Actions job log and list them in the
69-
job summary."""
57+
"""Annotate failures in the job log and list them in the job summary."""
7058

71-
# Escape the data and the properties of workflow commands
7259
ESCAPE_DATA = str.maketrans({"%": "%25", "\r": "%0D", "\n": "%0A"})
7360
ESCAPE_PROPERTY = ESCAPE_DATA | str.maketrans({":": "%3A", ",": "%2C"})
7461

@@ -81,32 +68,39 @@ def run_started(self, runtests: RunTests) -> None:
8168

8269
def test_case_failed(self, test_case: str, traceback: str,
8370
stream: TextIO) -> None:
84-
# Only report the failures which decide the result
8571
if not self.runtests.final_run:
8672
return
87-
# The job log has the full traceback: only keep the exception
8873
filename, line, exception = self.parse_traceback(decolor(traceback))
8974
self.annotate(test_case, exception, filename, line, stream)
9075

9176
def test_result(self, result: TestResult) -> None:
92-
# Failed test cases are annotated by test_case_failed()
93-
if (not self.runtests.final_run
94-
or not result.is_failed(self.runtests.fail_env_changed)
95-
or result.failed_test_cases):
77+
if not self.runtests.final_run:
78+
return
79+
if not result.is_failed(self.runtests.fail_env_changed):
80+
return
81+
# Already annotated by test_case_failed()
82+
if result.failed_test_cases:
9683
return
84+
# "test.test_x.test_y" is test_x/test_y.py or test_x/test_y/__init__.py
85+
name = result.test_name.removeprefix("test.")
86+
path = pathlib.Path(findtestdir(self.runtests.test_dir),
87+
*name.split("."))
88+
filename = None
89+
for candidate in (path.with_suffix(".py"), path / "__init__.py"):
90+
if candidate.exists():
91+
filename = str(candidate)
9792
message = "\n".join([str(result), *(result.env_changed_reasons or ())])
98-
self.annotate(result.test_name, decolor(message),
99-
result.find_filename(self.runtests.test_dir))
93+
self.annotate(result.test_name, decolor(message), filename)
10094

10195
def annotate(self, title: str, message: str, filename: str | None,
10296
line: str | None = None, stream: TextIO | None = None) -> None:
10397
if filename:
104-
# The stdlib can be outside of the repository checkout
98+
# Make installed stdlib paths relative to the checkout
10599
path = pathlib.Path(filename)
106100
stdlib_dir = pathlib.Path(os.__file__).parent
107101
if path.is_relative_to(stdlib_dir):
108102
filename = ("Lib" / path.relative_to(stdlib_dir)).as_posix()
109-
# Without a line, the annotation is not shown on the file
103+
# GitHub needs a line to show the annotation on the file
110104
line = line or "1"
111105
props = {"file": filename, "line": line, "title": title}
112106
props_text = ",".join(f"{key}={value.translate(self.ESCAPE_PROPERTY)}"
@@ -115,7 +109,6 @@ def annotate(self, title: str, message: str, filename: str | None,
115109
file=stream, flush=True)
116110

117111
def summary(self, results: TestResults, state: str) -> None:
118-
# Test files which failed in the last run (the re-run, if any)
119112
failed = [result for result in results.rerun_results
120113
if result.is_failed(self.runtests.fail_env_changed)]
121114
if not failed:
@@ -131,12 +124,13 @@ def summary(self, results: TestResults, state: str) -> None:
131124
for reason in result.env_changed_reasons))
132125
for name, traceback in result.failed_test_cases:
133126
# Expand short tracebacks if there are only a few
134-
is_open = ncases <= 5 and traceback.count("\n") < 30
135-
blocks += [f"<details{' open' if is_open else ''}>"
136-
f"<summary>{html.escape(name)}</summary>",
127+
if ncases <= 5 and traceback.count("\n") < 30:
128+
details = "<details open>"
129+
else:
130+
details = "<details>"
131+
blocks += [f"{details}<summary>{html.escape(name)}</summary>",
137132
f"```pytb\n{decolor(traceback).rstrip()}\n```",
138133
"</details>"]
139-
# Markdown blocks are separated by an empty line
140134
with open(os.environ["GITHUB_STEP_SUMMARY"], "a",
141135
encoding="utf-8") as fp:
142136
fp.write("".join(f"{block}\n\n" for block in blocks))

‎Lib/test/libregrtest/result.py‎

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import dataclasses
22
import json
3-
import pathlib
43
from _colorize import get_colors # type: ignore[import-not-found]
54
from typing import Any
65

7-
from .findtests import findtestdir
86
from .utils import (
9-
StrJSON, StrPath, TestName, FilterTuple,
7+
StrJSON, TestName, FilterTuple,
108
format_duration, normalize_test_name, print_warning)
119

1210

@@ -105,9 +103,6 @@ class TestResult:
105103
# short descriptions of how the test altered the execution environment
106104
env_changed_reasons: list[str] | None = None
107105

108-
# test file, set once the test module is imported
109-
filename: str | None = None
110-
111106
def is_failed(self, fail_env_changed: bool) -> bool:
112107
if self.state == State.ENV_CHANGED:
113108
return fail_env_changed
@@ -187,19 +182,6 @@ def has_meaningful_duration(self):
187182
def failed_test_cases(self) -> list[tuple[str, str]]:
188183
return (self.errors or []) + (self.failures or [])
189184

190-
def find_filename(self, test_dir: StrPath | None) -> str | None:
191-
"""Get the filename of the test, or None if it is not found."""
192-
if self.filename:
193-
return self.filename
194-
# The worker crashed or timed out before importing the test:
195-
# "test.test_x.test_y" is test_x/test_y.py or test_x/test_y/__init__.py
196-
name = self.test_name.removeprefix("test.")
197-
path = pathlib.Path(findtestdir(test_dir), *name.split("."))
198-
for filename in (path.with_suffix(".py"), path / "__init__.py"):
199-
if filename.exists():
200-
return str(filename)
201-
return None
202-
203185
def set_env_changed(self, *reasons):
204186
if self.state is None or self.state == State.PASSED:
205187
self.state = State.ENV_CHANGED

‎Lib/test/libregrtest/single.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ def _load_run_test(result: TestResult, runtests: RunTests) -> None:
153153
test_name = result.test_name
154154
module_name = abs_module_name(test_name, runtests.test_dir)
155155
test_mod = importlib.import_module(module_name)
156-
result.filename = test_mod.__file__
157156

158157
if hasattr(test_mod, "test_main"):
159158
# https://github.com/python/cpython/issues/89392

0 commit comments

Comments
 (0)