Skip to content

Commit b45dc07

Browse files
authored
Merge branch 'main' into cosmic-minty-quokka
2 parents 5545c35 + fb82a00 commit b45dc07

14 files changed

Lines changed: 1049 additions & 93 deletions

File tree

‎.github/scripts/flake_report.py‎

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ def attempt_results(root_dir):
7171
# proposing for quarantine.
7272
continue
7373
test_id = "{}.{}".format(classname, name)
74-
if case.find("skipped") is None:
74+
# gtest writes status="notrun" (e.g. DISABLED_ tests, never run)
75+
# with no result element; counting those as observed would claim a
76+
# test ran and passed when it never started. Gradle's XML leaves
77+
# the status attribute empty and marks skips with <skipped>, so
78+
# this only bites gtest reports.
79+
if case.find("skipped") is None and case.get("status") != "notrun":
7580
observed.add(test_id)
7681
problem = case.find("failure")
7782
if problem is None:
@@ -134,6 +139,14 @@ def cmd_count(args):
134139
# review on this PR against commit 5f01c39c, before the fix here).
135140
_WORKER_DIED_RE = re.compile(r"finished with non-zero exit value")
136141

142+
# Marker printed by .gitlab/scripts/run-gtest-quarantine.sh when a gtest
143+
# binary exited non-zero without writing its JUnit XML -- a sanitizer abort
144+
# (ASan exit 1, TSan exit 66) kills the binary before gtest writes anything,
145+
# so the abort reaches the classifier only through this log line.
146+
_ABORTED_WITHOUT_RESULTS_RE = re.compile(
147+
r"ABORTED: \S+ exited \d+ without producing test results"
148+
)
149+
137150

138151
def non_test_task_failures(log_path, test_task_pattern):
139152
"""Gradle task names blamed for a failure, other than the test task itself.
@@ -162,14 +175,17 @@ def cut_short_marker(log_path, musl=False):
162175
caller happened not to pass.
163176
164177
`musl=True` drops _WORKER_DIED_RE from consideration: see its docstring
165-
for why that line is ambiguous there. The crash banner alone still
166-
catches a genuine musl crash.
178+
for why that line is ambiguous there. The crash banner and the gtest
179+
abort marker alone still catch a genuine cut-short run.
167180
"""
168181
if not log_path or not os.path.isfile(log_path):
169182
return None
170-
pattern = _CRASH_RE if musl else re.compile(
171-
"{}|{}".format(_CRASH_RE.pattern, _WORKER_DIED_RE.pattern)
172-
)
183+
if musl:
184+
pattern = re.compile("{}|{}".format(_CRASH_RE.pattern, _ABORTED_WITHOUT_RESULTS_RE.pattern))
185+
else:
186+
pattern = re.compile("{}|{}|{}".format(
187+
_CRASH_RE.pattern, _WORKER_DIED_RE.pattern, _ABORTED_WITHOUT_RESULTS_RE.pattern
188+
))
173189
with open(log_path, errors="replace") as handle:
174190
for line in handle:
175191
m = pattern.search(line)

‎.github/scripts/quarantine.py‎

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,17 @@
8686
for suffix in _SYNTHETIC_SUITE_SUFFIXES
8787
)
8888

89+
# The GitLab sanitizer gtest jobs run native binaries that never link a JDK,
90+
# so their cells carry no jdk axis and no slow-suite suffix -- three tokens
91+
# only (the quarantine.txt gtest section documents the shape). Without these
92+
# in the universe, cells_overlap() fails closed for every gtest glob and two
93+
# genuinely disjoint entries (asan vs tsan) read as duplicates.
94+
SYNTHETIC_CELLS += tuple(
95+
"glibc-{}-{}".format(config, arch)
96+
for config in _SYNTHETIC_CONFIGS
97+
for arch in KNOWN_ARCHES
98+
)
99+
89100

90101
def _matches_any_synthetic_cell(globs):
91102
return any(any(fnmatch.fnmatch(cell, g) for g in globs) for cell in SYNTHETIC_CELLS)
@@ -184,9 +195,31 @@ def covers(entry, test_id):
184195
# the merge gate for the entire repository from one validate-clean
185196
# line.
186197
prefix = pattern[:-1]
187-
if not test_id.startswith(prefix):
198+
if test_id.startswith(prefix):
199+
# The remainder may carry a trailing "/0"-style suffix: Java
200+
# writes none, but gtest value-parameterized cases are named
201+
# "Case/0", and the Java rule's only real constraint is that the
202+
# remainder holds no further class/case separator dot.
203+
return "." not in test_id[len(prefix):]
204+
# GoogleTest bakes its suite decorations into the XML classname, not
205+
# into the entry: value-parameterized suites are "<inst>/<suite>"
206+
# (the "/0" lands on the case name), typed suites "<suite>/<typeidx>",
207+
# and combinations "<inst>/<suite>/<typeidx>". Reduce the classname to
208+
# its bare suite -- drop a trailing all-digits segment, then take the
209+
# last "/" segment -- and compare that. Java classnames never contain
210+
# "/", so this branch is unreachable for a Java id.
211+
cls, _, _ = test_id.rpartition(".")
212+
if not cls or "/" not in cls:
188213
return False
189-
return "." not in test_id[len(prefix):]
214+
segments = cls.split("/")
215+
if len(segments) > 1 and segments[-1].isdigit():
216+
segments.pop()
217+
if segments[-1] + "." == prefix:
218+
return True
219+
# A decorated entry ("MyInst/WobblySuite.*") also covers the typed
220+
# variants of its own instantiation ("MyInst/WobblySuite/0.case"):
221+
# the full undecorated classname must equal the prefix's suite too.
222+
return "/".join(segments) + "." == prefix
190223
return test_id == pattern
191224

192225

@@ -317,6 +350,12 @@ def complain(line, message):
317350
"normalises the report's id to that shape -- drop the '()'"
318351
).format(entry["test"]))
319352

353+
# A decorated gtest suite name ("<inst>/<Suite>.*") in a class-wide
354+
# entry is valid and narrower than the bare-suite form: covers()
355+
# matches it directly against the decorated classname, muting only
356+
# that instantiation. No complaint - just do not claim it matches
357+
# nothing.
358+
320359
if _INVOCATION_INDEX_RE.match(entry["test"].rsplit(".", 1)[-1]):
321360
complain(line, (
322361
"test '{}' names an invocation index. @ParameterizedTest and "

0 commit comments

Comments
 (0)