Skip to content

Commit 530b3fa

Browse files
[𝘀𝗽𝗿] initial version
Created using spr 1.3.6
2 parents 536e414 + 27c5ba3 commit 530b3fa

File tree

2 files changed

+330
-22
lines changed

2 files changed

+330
-22
lines changed

.ci/generate_test_report_lib.py

Lines changed: 118 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,57 @@
1212
"https://github.com/llvm/llvm-project/issues and add the "
1313
"`infrastructure` label."
1414
)
15+
# The maximum number of lines to pull from a ninja failure.
16+
NINJA_LOG_SIZE_THRESHOLD = 500
17+
18+
19+
def _parse_ninja_log(ninja_log: list[str]) -> list[tuple[str, str]]:
20+
"""Parses an individual ninja log."""
21+
failures = []
22+
index = 0
23+
while index < len(ninja_log):
24+
while index < len(ninja_log) and not ninja_log[index].startswith("FAILED:"):
25+
index += 1
26+
if index == len(ninja_log):
27+
# We hit the end of the log without finding a build failure, go to
28+
# the next log.
29+
return failures
30+
failing_action = ninja_log[index - 1].split("] ")[1]
31+
failure_log = []
32+
while (
33+
index < len(ninja_log)
34+
and not ninja_log[index].startswith("[")
35+
and not ninja_log[index].startswith(
36+
"ninja: build stopped: subcommand failed"
37+
)
38+
and len(failure_log) < NINJA_LOG_SIZE_THRESHOLD
39+
):
40+
failure_log.append(ninja_log[index])
41+
index += 1
42+
failures.append((failing_action, "\n".join(failure_log)))
43+
return failures
44+
45+
46+
def find_failure_in_ninja_logs(ninja_logs: list[list[str]]) -> list[tuple[str, str]]:
47+
"""Extracts failure messages from ninja output.
48+
49+
This patch takes stdout/stderr from ninja in the form of a list of files
50+
represented as a list of lines. This function then returns tuples containing
51+
the name of the target and the error message.
52+
53+
Args:
54+
ninja_logs: A list of files in the form of a list of lines representing the log
55+
files captured from ninja.
56+
57+
Returns:
58+
A list of tuples. The first string is the name of the target that failed. The
59+
second string is the error message.
60+
"""
61+
failures = []
62+
for ninja_log in ninja_logs:
63+
log_failures = _parse_ninja_log(ninja_log)
64+
failures.extend(log_failures)
65+
return failures
1566

1667

1768
# Set size_limit to limit the byte size of the report. The default is 1MB as this
@@ -24,6 +75,7 @@ def generate_report(
2475
title,
2576
return_code,
2677
junit_objects,
78+
ninja_logs: list[list[str]],
2779
size_limit=1024 * 1024,
2880
list_failures=True,
2981
):
@@ -61,15 +113,46 @@ def generate_report(
61113
]
62114
)
63115
else:
64-
report.extend(
65-
[
66-
"The build failed before running any tests.",
67-
"",
68-
SEE_BUILD_FILE_STR,
69-
"",
70-
UNRELATED_FAILURES_STR,
71-
]
72-
)
116+
ninja_failures = find_failure_in_ninja_logs(ninja_logs)
117+
if not ninja_failures:
118+
report.extend(
119+
[
120+
"The build failed before running any tests. Detailed "
121+
"information about the build failure could not be "
122+
"automatically obtained.",
123+
"",
124+
SEE_BUILD_FILE_STR,
125+
"",
126+
UNRELATED_FAILURES_STR,
127+
]
128+
)
129+
else:
130+
report.extend(
131+
[
132+
"The build failed before running any tests. Click on the "
133+
"failure below to see the details.",
134+
"",
135+
]
136+
)
137+
for build_failure in ninja_failures:
138+
failed_action, failure_message = build_failure
139+
report.extend(
140+
[
141+
"<details>",
142+
f"<summary>{failed_action}</summary>",
143+
"",
144+
"```",
145+
failure_message,
146+
"```",
147+
"</details>",
148+
]
149+
)
150+
report.extend(
151+
[
152+
"",
153+
UNRELATED_FAILURES_STR,
154+
]
155+
)
73156
return "\n".join(report)
74157

75158
tests_passed = tests_run - tests_skipped - tests_failed
@@ -114,14 +197,32 @@ def plural(num_tests):
114197
elif return_code != 0:
115198
# No tests failed but the build was in a failed state. Bring this to the user's
116199
# attention.
117-
report.extend(
118-
[
119-
"",
120-
"All tests passed but another part of the build **failed**.",
121-
"",
122-
SEE_BUILD_FILE_STR,
123-
]
124-
)
200+
ninja_failures = find_failure_in_ninja_logs(ninja_logs)
201+
if not ninja_failures:
202+
report.extend(
203+
[
204+
"",
205+
"All tests passed but another part of the build **failed**. "
206+
"Detailed information about the build failure could not be "
207+
"automatically obtained.",
208+
"",
209+
SEE_BUILD_FILE_STR,
210+
]
211+
)
212+
else:
213+
for build_failure in ninja_failures:
214+
failed_action, failure_message = build_failure
215+
report.extend(
216+
[
217+
"<details>",
218+
f"<summary>{failed_action}</summary>",
219+
"",
220+
"```",
221+
failure_message,
222+
"```",
223+
"</details>",
224+
]
225+
)
125226

126227
if failures or return_code != 0:
127228
report.extend(["", UNRELATED_FAILURES_STR])

0 commit comments

Comments
 (0)