Skip to content

Commit 491ff05

Browse files
authored
Parse flakyFailure and rerunFailure from surefire report files (#765)
1 parent 32557b4 commit 491ff05

File tree

9 files changed

+812
-2
lines changed

9 files changed

+812
-2
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,12 @@
198198
<artifactId>mockito-core</artifactId>
199199
<scope>test</scope>
200200
</dependency>
201+
<dependency>
202+
<groupId>org.xmlunit</groupId>
203+
<artifactId>xmlunit-core</artifactId>
204+
<version>2.11.0</version>
205+
<scope>test</scope>
206+
</dependency>
201207
</dependencies>
202208

203209
<repositories>

src/main/java/hudson/tasks/junit/CaseResult.java

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ public class CaseResult extends TestResult implements Comparable<CaseResult> {
8484
private String errorStackTrace;
8585
private String errorDetails;
8686
private Map<String, String> properties;
87+
private List<Failure> flakyFailures;
88+
private List<Failure> rerunFailures;
8789

8890
@SuppressFBWarnings(value = "SE_TRANSIENT_FIELD_NOT_RESTORED", justification = "Specific method to restore it")
8991
private transient SuiteResult parent;
@@ -247,6 +249,8 @@ public CaseResult(
247249
}
248250
this.properties = properties;
249251
this.keepTestNames = keepTestNames;
252+
this.flakyFailures = parseFlakyFailures(testCase);
253+
this.rerunFailures = parseRerunFailures(testCase);
250254
}
251255

252256
public CaseResult(CaseResult src) {
@@ -263,12 +267,46 @@ public CaseResult(CaseResult src) {
263267
this.stderr = src.stderr;
264268
this.properties = new HashMap<>();
265269
this.properties.putAll(src.properties);
270+
this.flakyFailures = src.flakyFailures;
271+
this.rerunFailures = src.rerunFailures;
266272
}
267273

268274
public static float clampDuration(float d) {
269275
return Math.min(365.0f * 24 * 60 * 60, Math.max(0.0f, d));
270276
}
271277

278+
private static List<Failure> parseFlakyFailures(Element testCase) {
279+
List<Failure> failures = new ArrayList<>();
280+
List<Element> flakyFailuresElements = testCase.elements("flakyFailure");
281+
if (flakyFailuresElements != null) {
282+
for (Element flakyFailuresElement : flakyFailuresElements) {
283+
String message = flakyFailuresElement.attributeValue("message");
284+
String type = flakyFailuresElement.attributeValue("type");
285+
String stackTrace = flakyFailuresElement.elementText("stackTrace");
286+
String stdout = flakyFailuresElement.elementText("system-out");
287+
String stderr = flakyFailuresElement.elementText("system-err");
288+
failures.add(new Failure(message, type, stackTrace, stdout, stderr));
289+
}
290+
}
291+
return failures;
292+
}
293+
294+
private static List<Failure> parseRerunFailures(Element testCase) {
295+
List<Failure> rerunFailures = new ArrayList<>();
296+
List<Element> rerunFailureElements = testCase.elements("rerunFailure");
297+
if (rerunFailureElements != null) {
298+
for (Element rerunFailureElement : rerunFailureElements) {
299+
String message = rerunFailureElement.attributeValue("message");
300+
String type = rerunFailureElement.attributeValue("type");
301+
String stackTrace = rerunFailureElement.elementText("stackTrace");
302+
String stdout = rerunFailureElement.elementText("system-out");
303+
String stderr = rerunFailureElement.elementText("system-err");
304+
rerunFailures.add(new Failure(message, type, stackTrace, stdout, stderr));
305+
}
306+
}
307+
return rerunFailures;
308+
}
309+
272310
static CaseResult parse(SuiteResult parent, final XMLStreamReader reader, String context, String ver)
273311
throws XMLStreamException {
274312
CaseResult r = new CaseResult(parent, null, null, null);
@@ -320,6 +358,12 @@ static CaseResult parse(SuiteResult parent, final XMLStreamReader reader, String
320358
r.properties = new HashMap<>();
321359
parseProperties(r.properties, reader, context, ver);
322360
break;
361+
case "flakyFailures":
362+
r.flakyFailures = parseFailures(reader, context, "flakyFailures");
363+
break;
364+
case "rerunFailures":
365+
r.rerunFailures = parseFailures(reader, context, "rerunFailures");
366+
break;
323367
default:
324368
LOGGER.finest(() -> "Unknown field in " + context + ": " + elementName);
325369
}
@@ -328,6 +372,63 @@ static CaseResult parse(SuiteResult parent, final XMLStreamReader reader, String
328372
return r;
329373
}
330374

375+
static List<Failure> parseFailures(final XMLStreamReader reader, String context, String endElement)
376+
throws XMLStreamException {
377+
List<Failure> failures = new ArrayList<>();
378+
while (reader.hasNext()) {
379+
final int event = reader.next();
380+
if (event == XMLStreamReader.END_ELEMENT && reader.getLocalName().equals(endElement)) {
381+
break;
382+
}
383+
if (event == XMLStreamReader.START_ELEMENT) {
384+
final String elementName = reader.getLocalName();
385+
if (elementName.equals("failure")) {
386+
failures.add(parseFailure(reader, context));
387+
} else {
388+
LOGGER.finest(() -> "Unknown field in " + context + ": " + elementName);
389+
}
390+
}
391+
}
392+
return failures;
393+
}
394+
395+
public static Failure parseFailure(XMLStreamReader reader, String context) throws XMLStreamException {
396+
String message = null;
397+
String type = null;
398+
String stackTrace = null;
399+
String stdout = null;
400+
String stderr = null;
401+
while (reader.hasNext()) {
402+
int event = reader.next();
403+
if (event == XMLStreamReader.END_ELEMENT && "failure".equals(reader.getLocalName())) {
404+
break;
405+
}
406+
if (event == XMLStreamReader.START_ELEMENT) {
407+
String elementName = reader.getLocalName();
408+
switch (elementName) {
409+
case "message":
410+
message = reader.getElementText();
411+
break;
412+
case "type":
413+
type = reader.getElementText();
414+
break;
415+
case "stackTrace":
416+
stackTrace = reader.getElementText();
417+
break;
418+
case "stdout":
419+
stdout = reader.getElementText();
420+
break;
421+
case "stderr":
422+
stderr = reader.getElementText();
423+
break;
424+
default:
425+
LOGGER.finest(() -> "Unknown field in " + context + ": " + elementName);
426+
}
427+
}
428+
}
429+
return new Failure(message, type, stackTrace, stdout, stderr);
430+
}
431+
331432
static void parseProperties(Map<String, String> r, final XMLStreamReader reader, String context, String ver)
332433
throws XMLStreamException {
333434
while (reader.hasNext()) {
@@ -1040,6 +1141,18 @@ void replaceParent(SuiteResult parent) {
10401141
this.parent = parent;
10411142
}
10421143

1144+
@Exported
1145+
@Override
1146+
public List<Failure> getFlakyFailures() {
1147+
return flakyFailures == null ? Collections.emptyList() : Collections.unmodifiableList(flakyFailures);
1148+
}
1149+
1150+
@Exported
1151+
@Override
1152+
public List<Failure> getRerunFailures() {
1153+
return rerunFailures == null ? Collections.emptyList() : Collections.unmodifiableList(rerunFailures);
1154+
}
1155+
10431156
/**
10441157
* Constants that represent the status of this test.
10451158
*/
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package hudson.tasks.junit;
2+
3+
import java.io.Serializable;
4+
import org.kohsuke.stapler.export.ExportedBean;
5+
6+
@ExportedBean
7+
public record Failure(String message, String type, String stackTrace, String stdout, String stderr)
8+
implements Serializable {}

src/main/java/hudson/tasks/junit/TestResultAction.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ public Widget getWidget() {
427427
XSTREAM.alias("result", TestResult.class);
428428
XSTREAM.alias("suite", SuiteResult.class);
429429
XSTREAM.alias("case", CaseResult.class);
430+
XSTREAM.alias("failure", Failure.class);
430431
XSTREAM.registerConverter(new HeapSpaceStringConverter(), 100);
431432
}
432433
}

src/main/java/hudson/tasks/test/TestResult.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@
2626
import hudson.model.Job;
2727
import hudson.model.Result;
2828
import hudson.model.Run;
29+
import hudson.tasks.junit.Failure;
2930
import hudson.tasks.junit.TestAction;
3031
import java.util.Collection;
3132
import java.util.Collections;
33+
import java.util.List;
3234
import java.util.Map;
3335
import java.util.logging.Level;
3436
import java.util.logging.Logger;
@@ -263,6 +265,14 @@ public Map<String, String> getProperties() {
263265
return Collections.emptyMap();
264266
}
265267

268+
public List<Failure> getFlakyFailures() {
269+
return Collections.emptyList();
270+
}
271+
272+
public List<Failure> getRerunFailures() {
273+
return Collections.emptyList();
274+
}
275+
266276
/**
267277
* @return true if the test was not skipped and did not fail, false otherwise.
268278
*/

0 commit comments

Comments
 (0)