Skip to content

Commit d2d09d9

Browse files
mhdatieclaude
andcommitted
Add testcaseKeys() and tagRetriedTests(Set) to JUnitReport
testcaseKeys() returns the classname#name key for every testcase element, used by ResultCollector to identify which tests appear in a retry marker file. tagRetriedTests(Set) tags all <testcase> elements except the last for each retried key as skip, leaving the final attempt for tagFinalStatuses() to resolve naturally as pass or fail. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 54dbe6e commit d2d09d9

1 file changed

Lines changed: 27 additions & 26 deletions

File tree

.gitlab/collect-result/JUnitReport.java

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.LinkedHashMap;
77
import java.util.List;
88
import java.util.Map;
9+
import java.util.Set;
910
import java.util.regex.Pattern;
1011
import javax.xml.XMLConstants;
1112
import javax.xml.parsers.DocumentBuilderFactory;
@@ -114,32 +115,6 @@ void tagSyntheticFailures() {
114115
}
115116
}
116117

117-
/// Tags non-final attempts of a retried test so Test Optimization does not surface them as
118-
/// real failures. The Develocity testRetry plugin re-runs failed tests and emits one
119-
/// `<testcase>` per attempt sharing the same `(classname, name)`; CI ignores all but the
120-
/// final attempt, so this method does the same by marking earlier attempts as `skip`.
121-
///
122-
/// Must run before [#tagFinalStatuses] so the existing per-testcase tagger does not
123-
/// overwrite `skip` with `fail`.
124-
///
125-
/// See https://docs.gradle.com/develocity/gradle-plugin/current/#test_retry
126-
void tagRetriedTests() {
127-
var all = testcases();
128-
for (var i = 0; i < all.size(); i++) {
129-
var current = all.get(i);
130-
var classname = current.getAttribute("classname");
131-
var name = current.getAttribute("name");
132-
for (var j = i + 1; j < all.size(); j++) {
133-
var later = all.get(j);
134-
if (classname.equals(later.getAttribute("classname"))
135-
&& name.equals(later.getAttribute("name"))) {
136-
addFinalStatusProperty(current, "skip", MissingPropertiesPlacement.APPEND_TO_TESTCASE);
137-
break;
138-
}
139-
}
140-
}
141-
}
142-
143118
void tagFinalStatuses() {
144119
for (var testcase : testcases()) {
145120
if (hasFinalStatusProperty(testcase)) {
@@ -150,6 +125,32 @@ void tagFinalStatuses() {
150125
}
151126
}
152127

128+
Set<String> testcaseKeys() {
129+
var keys = new LinkedHashSet<String>();
130+
for (var testcase : testcases()) {
131+
keys.add(testcase.getAttribute("classname") + "#" + testcase.getAttribute("name"));
132+
}
133+
return keys;
134+
}
135+
136+
// Tags all <testcase> elements except the last for each retried key as skip.
137+
// Must be called before tagFinalStatuses() so hasFinalStatusProperty() skips tagged entries.
138+
void tagRetriedTests(Set<String> retriedTestKeys) {
139+
if (retriedTestKeys.isEmpty()) return;
140+
var testcasesByKey = new LinkedHashMap<String, List<Element>>();
141+
for (var testcase : testcases()) {
142+
var key = testcase.getAttribute("classname") + "#" + testcase.getAttribute("name");
143+
if (retriedTestKeys.contains(key)) {
144+
testcasesByKey.computeIfAbsent(key, k -> new ArrayList<>()).add(testcase);
145+
}
146+
}
147+
for (var attempts : testcasesByKey.values()) {
148+
for (var i = 0; i < attempts.size() - 1; i++) {
149+
addFinalStatusProperty(attempts.get(i), "skip", MissingPropertiesPlacement.FIRST_CHILD);
150+
}
151+
}
152+
}
153+
153154
void write(Path xmlFile) throws Exception {
154155
Files.createDirectories(xmlFile.getParent());
155156
var tmpFile = Files.createTempFile(xmlFile.getParent(), "collect-results-", ".xml");

0 commit comments

Comments
 (0)