66import java .util .LinkedHashMap ;
77import java .util .List ;
88import java .util .Map ;
9+ import java .util .Set ;
910import java .util .regex .Pattern ;
1011import javax .xml .XMLConstants ;
1112import 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