Skip to content

Commit 28684ad

Browse files
author
Farid Zakaria
committed
Add support for @afterall in XML report
Writing a simple test like ``` package com.library; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; public class AlwaysFailTest { @test public void doNothingTest() { System.out.println("Hi there!"); } @afterall public static void alwaysFail() { throw new RuntimeException("Always failing."); } } ``` Produces an XML that seems to look like it succeeds. ```xml <?xml version="1.0" encoding="UTF-8"?> <testsuites> <testsuite name="com.library.AlwaysFailTest" timestamp="2024-10-10T21:19:11.522176Z" hostname="KHK9NLVQGN" tests="2" failures="0" errors="0" disabled="0" skipped="0" package=""> <properties/> <testcase name="doNothingTest" classname="com.library.AlwaysFailTest" time="0.03"> <system-out><![CDATA[Hi there! ]]></system-out> </testcase> </testsuite> </testsuites> ``` Bazel itself correctly identifies it fails. The Junit4 reporter also included with Bazel natively correclty reports the failure in the XML output. Augment the Junit listener to add support for static methods and add them to the JUnit output. Doing so produces the following XML. ```xml <?xml version="1.0" encoding="UTF-8"?> <testsuites> <testsuite name="com.library.AlwaysFailTest" timestamp="2024-10-10T21:49:02.096648Z" hostname="KHK9NLVQGN" tests="3" failures="1" errors="0" disabled="0" skipped="0" package=""> <properties/> <testcase name="com.library.AlwaysFailTest" classname="com.library.AlwaysFailTest" time="0.05"> <failure message="Always failing." type="java.lang.RuntimeException"><![CDATA[java.lang.RuntimeException: Always failing. at com.library.AlwaysFailTest.alwaysFail(AlwaysFailTest.java:20) ... at com.github.bazel_contrib.contrib_rules_jvm.junit5.JUnit5Runner.main(JUnit5Runner.java:39) ]]></failure> </testcase> <testcase name="doNothingTest" classname="com.library.AlwaysFailTest" time="0.03"> <system-out><![CDATA[Hi there! ]]></system-out> </testcase> </testsuite> </testsuites> ```
1 parent 43d61cb commit 28684ad

File tree

7 files changed

+232
-5
lines changed

7 files changed

+232
-5
lines changed

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ dev_maven.install(
228228
"org.junit.platform:junit-platform-suite:1.8.2",
229229
"org.junit.platform:junit-platform-suite-api:1.8.2",
230230
"org.junit.platform:junit-platform-suite-engine:1.8.2",
231+
"org.junit.platform:junit-platform-testkit:1.8.2",
231232
"org.junit.vintage:junit-vintage-engine:5.8.2",
232233
"org.mockito:mockito-core:4.8.1",
233234
],

WORKSPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ maven_install(
5454
"org.junit.platform:junit-platform-suite:1.8.2",
5555
"org.junit.platform:junit-platform-suite-api:1.8.2",
5656
"org.junit.platform:junit-platform-suite-engine:1.8.2",
57+
"org.junit.platform:junit-platform-testkit:1.8.2",
5758
"org.junit.vintage:junit-vintage-engine:5.8.2",
5859
"org.mockito:mockito-core:4.8.1",
5960
],

contrib_rules_jvm_tests_install.json

100644100755
Lines changed: 134 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL",
3-
"__INPUT_ARTIFACTS_HASH": -579211453,
4-
"__RESOLVED_ARTIFACTS_HASH": -1476897549,
3+
"__INPUT_ARTIFACTS_HASH": -110331756,
4+
"__RESOLVED_ARTIFACTS_HASH": 1220653889,
55
"artifacts": {
66
"junit:junit": {
77
"shasums": {
@@ -31,6 +31,13 @@
3131
},
3232
"version": "1.1.2"
3333
},
34+
"org.assertj:assertj-core": {
35+
"shasums": {
36+
"jar": "d749db58c2bd353f1c03541d747b753931d4b84da8e48993ef51efe8694b4ed7",
37+
"sources": "d0384d378df4391392bbdf06691aa48b3ac3bc5f37c223e6466a75a03d54fee4"
38+
},
39+
"version": "3.20.2"
40+
},
3441
"org.hamcrest:hamcrest-core": {
3542
"shasums": {
3643
"jar": "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9",
@@ -115,6 +122,13 @@
115122
},
116123
"version": "1.8.2"
117124
},
125+
"org.junit.platform:junit-platform-testkit": {
126+
"shasums": {
127+
"jar": "94d6fb9b1d4e7bb3d6a967b21f1cf4f7cf84455d602cccf614a186d936929b08",
128+
"sources": "485fac0cc55c14db0c10dff3e42351b10e0bfe5d337892efe8e5b5fcb672f752"
129+
},
130+
"version": "1.8.2"
131+
},
118132
"org.junit.vintage:junit-vintage-engine": {
119133
"shasums": {
120134
"jar": "ebd567b84e380d5373c47de3c9616d84f7bef91f9f8a8e7fc925be68240c1ba4",
@@ -198,6 +212,12 @@
198212
"org.junit.platform:junit-platform-suite-api",
199213
"org.junit.platform:junit-platform-suite-commons"
200214
],
215+
"org.junit.platform:junit-platform-testkit": [
216+
"org.apiguardian:apiguardian-api",
217+
"org.assertj:assertj-core",
218+
"org.junit.platform:junit-platform-launcher",
219+
"org.opentest4j:opentest4j"
220+
],
201221
"org.junit.vintage:junit-vintage-engine": [
202222
"junit:junit",
203223
"org.apiguardian:apiguardian-api",
@@ -294,6 +314,69 @@
294314
"org.apiguardian:apiguardian-api": [
295315
"org.apiguardian.api"
296316
],
317+
"org.assertj:assertj-core": [
318+
"org.assertj.core.annotations",
319+
"org.assertj.core.api",
320+
"org.assertj.core.api.exception",
321+
"org.assertj.core.api.filter",
322+
"org.assertj.core.api.iterable",
323+
"org.assertj.core.api.junit.jupiter",
324+
"org.assertj.core.api.recursive.comparison",
325+
"org.assertj.core.condition",
326+
"org.assertj.core.configuration",
327+
"org.assertj.core.data",
328+
"org.assertj.core.description",
329+
"org.assertj.core.error",
330+
"org.assertj.core.error.array2d",
331+
"org.assertj.core.error.future",
332+
"org.assertj.core.error.uri",
333+
"org.assertj.core.extractor",
334+
"org.assertj.core.groups",
335+
"org.assertj.core.internal",
336+
"org.assertj.core.internal.bytebuddy",
337+
"org.assertj.core.internal.bytebuddy.agent.builder",
338+
"org.assertj.core.internal.bytebuddy.asm",
339+
"org.assertj.core.internal.bytebuddy.build",
340+
"org.assertj.core.internal.bytebuddy.description",
341+
"org.assertj.core.internal.bytebuddy.description.annotation",
342+
"org.assertj.core.internal.bytebuddy.description.enumeration",
343+
"org.assertj.core.internal.bytebuddy.description.field",
344+
"org.assertj.core.internal.bytebuddy.description.method",
345+
"org.assertj.core.internal.bytebuddy.description.modifier",
346+
"org.assertj.core.internal.bytebuddy.description.type",
347+
"org.assertj.core.internal.bytebuddy.dynamic",
348+
"org.assertj.core.internal.bytebuddy.dynamic.loading",
349+
"org.assertj.core.internal.bytebuddy.dynamic.scaffold",
350+
"org.assertj.core.internal.bytebuddy.dynamic.scaffold.inline",
351+
"org.assertj.core.internal.bytebuddy.dynamic.scaffold.subclass",
352+
"org.assertj.core.internal.bytebuddy.implementation",
353+
"org.assertj.core.internal.bytebuddy.implementation.attribute",
354+
"org.assertj.core.internal.bytebuddy.implementation.auxiliary",
355+
"org.assertj.core.internal.bytebuddy.implementation.bind",
356+
"org.assertj.core.internal.bytebuddy.implementation.bind.annotation",
357+
"org.assertj.core.internal.bytebuddy.implementation.bytecode",
358+
"org.assertj.core.internal.bytebuddy.implementation.bytecode.assign",
359+
"org.assertj.core.internal.bytebuddy.implementation.bytecode.assign.primitive",
360+
"org.assertj.core.internal.bytebuddy.implementation.bytecode.assign.reference",
361+
"org.assertj.core.internal.bytebuddy.implementation.bytecode.collection",
362+
"org.assertj.core.internal.bytebuddy.implementation.bytecode.constant",
363+
"org.assertj.core.internal.bytebuddy.implementation.bytecode.member",
364+
"org.assertj.core.internal.bytebuddy.jar.asm",
365+
"org.assertj.core.internal.bytebuddy.jar.asm.commons",
366+
"org.assertj.core.internal.bytebuddy.jar.asm.signature",
367+
"org.assertj.core.internal.bytebuddy.matcher",
368+
"org.assertj.core.internal.bytebuddy.pool",
369+
"org.assertj.core.internal.bytebuddy.utility",
370+
"org.assertj.core.internal.bytebuddy.utility.privilege",
371+
"org.assertj.core.internal.bytebuddy.utility.visitor",
372+
"org.assertj.core.matcher",
373+
"org.assertj.core.presentation",
374+
"org.assertj.core.util",
375+
"org.assertj.core.util.diff",
376+
"org.assertj.core.util.diff.myers",
377+
"org.assertj.core.util.introspection",
378+
"org.assertj.core.util.xml"
379+
],
297380
"org.hamcrest:hamcrest-core": [
298381
"org.hamcrest",
299382
"org.hamcrest.core",
@@ -380,6 +463,9 @@
380463
"org.junit.platform:junit-platform-suite-engine": [
381464
"org.junit.platform.suite.engine"
382465
],
466+
"org.junit.platform:junit-platform-testkit": [
467+
"org.junit.platform.testkit.engine"
468+
],
383469
"org.junit.vintage:junit-vintage-engine": [
384470
"org.junit.vintage.engine",
385471
"org.junit.vintage.engine.descriptor",
@@ -479,6 +565,8 @@
479565
"net.bytebuddy:byte-buddy:jar:sources",
480566
"org.apiguardian:apiguardian-api",
481567
"org.apiguardian:apiguardian-api:jar:sources",
568+
"org.assertj:assertj-core",
569+
"org.assertj:assertj-core:jar:sources",
482570
"org.hamcrest:hamcrest-core",
483571
"org.hamcrest:hamcrest-core:jar:sources",
484572
"org.junit.jupiter:junit-jupiter-api",
@@ -503,6 +591,8 @@
503591
"org.junit.platform:junit-platform-suite-engine",
504592
"org.junit.platform:junit-platform-suite-engine:jar:sources",
505593
"org.junit.platform:junit-platform-suite:jar:sources",
594+
"org.junit.platform:junit-platform-testkit",
595+
"org.junit.platform:junit-platform-testkit:jar:sources",
506596
"org.junit.vintage:junit-vintage-engine",
507597
"org.junit.vintage:junit-vintage-engine:jar:sources",
508598
"org.mockito:mockito-core",
@@ -513,5 +603,47 @@
513603
"org.opentest4j:opentest4j:jar:sources"
514604
]
515605
},
606+
"services": {
607+
"org.junit.jupiter:junit-jupiter-engine": {
608+
"org.junit.platform.engine.TestEngine": [
609+
"org.junit.jupiter.engine.JupiterTestEngine"
610+
]
611+
},
612+
"org.junit.jupiter:junit-jupiter-engine:jar:sources": {
613+
"org.junit.platform.engine.TestEngine": [
614+
"org.junit.jupiter.engine.JupiterTestEngine"
615+
]
616+
},
617+
"org.junit.platform:junit-platform-launcher": {
618+
"org.junit.platform.launcher.TestExecutionListener": [
619+
"org.junit.platform.launcher.listeners.UniqueIdTrackingListener"
620+
]
621+
},
622+
"org.junit.platform:junit-platform-launcher:jar:sources": {
623+
"org.junit.platform.launcher.TestExecutionListener": [
624+
"org.junit.platform.launcher.listeners.UniqueIdTrackingListener"
625+
]
626+
},
627+
"org.junit.platform:junit-platform-suite-engine": {
628+
"org.junit.platform.engine.TestEngine": [
629+
"org.junit.platform.suite.engine.SuiteTestEngine"
630+
]
631+
},
632+
"org.junit.platform:junit-platform-suite-engine:jar:sources": {
633+
"org.junit.platform.engine.TestEngine": [
634+
"org.junit.platform.suite.engine.SuiteTestEngine"
635+
]
636+
},
637+
"org.junit.vintage:junit-vintage-engine": {
638+
"org.junit.platform.engine.TestEngine": [
639+
"org.junit.vintage.engine.VintageTestEngine"
640+
]
641+
},
642+
"org.junit.vintage:junit-vintage-engine:jar:sources": {
643+
"org.junit.platform.engine.TestEngine": [
644+
"org.junit.vintage.engine.VintageTestEngine"
645+
]
646+
}
647+
},
516648
"version": "2"
517649
}

java/src/com/github/bazel_contrib/contrib_rules_jvm/junit5/BazelJUnitOutputListener.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ private Map<TestData, List<TestData>> matchTestCasesToSuites_locked(
109109
// results
110110
List<UniqueId.Segment> segments = testCase.getId().getUniqueIdObject().getSegments();
111111

112-
if (segments.size() == 3 || segments.size() == 5) {
112+
if (segments.size() == 2) {
113+
parent = results.get(testCase.getId().getUniqueIdObject());
114+
} else if (segments.size() == 3 || segments.size() == 5) {
113115
// get class / test data up one level
114116
parent =
115117
testCase
@@ -161,7 +163,7 @@ private List<TestData> findTestCases_locked() {
161163
// are identified by the fact that they have no child test cases in the
162164
// test plan, or they are marked as tests.
163165
TestIdentifier id = result.getId();
164-
return id.isTest() || testPlan.getChildren(id).isEmpty();
166+
return id.getSource() != null || id.isTest() || testPlan.getChildren(id).isEmpty();
165167
})
166168
.collect(Collectors.toList());
167169
}

java/src/com/github/bazel_contrib/contrib_rules_jvm/junit5/TestData.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,25 @@ public boolean isDynamic() {
146146
public Instant getStarted() {
147147
return this.started;
148148
}
149+
150+
@Override
151+
public String toString() {
152+
return "TestData{"
153+
+ "id="
154+
+ id
155+
+ ", reportEntries="
156+
+ reportEntries
157+
+ ", started="
158+
+ started
159+
+ ", finished="
160+
+ finished
161+
+ ", reason='"
162+
+ reason
163+
+ '\''
164+
+ ", result="
165+
+ result
166+
+ ", dynamic="
167+
+ dynamic
168+
+ '}';
169+
}
149170
}

java/test/com/github/bazel_contrib/contrib_rules_jvm/junit5/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ java_test_suite(
3838
artifact("org.junit.jupiter:junit-jupiter-api", "contrib_rules_jvm_tests"),
3939
artifact("org.junit.jupiter:junit-jupiter-params", "contrib_rules_jvm_tests"),
4040
artifact("org.junit.platform:junit-platform-engine", "contrib_rules_jvm_tests"),
41+
artifact("org.junit.platform:junit-platform-testkit", "contrib_rules_jvm_tests"),
4142
artifact("org.mockito:mockito-core", "contrib_rules_jvm_tests"),
4243
artifact("org.opentest4j:opentest4j", "contrib_rules_jvm_tests"),
4344
] + junit5_vintage_deps("contrib_rules_jvm_tests"),
Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,39 @@
55
import static org.junit.jupiter.api.Assertions.assertNotNull;
66
import static org.junit.jupiter.api.Assertions.assertTrue;
77
import static org.junit.jupiter.api.Assertions.fail;
8+
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
89
import static org.junit.platform.launcher.LauncherConstants.STDERR_REPORT_ENTRY_KEY;
910
import static org.junit.platform.launcher.LauncherConstants.STDOUT_REPORT_ENTRY_KEY;
1011

12+
import java.io.File;
1113
import java.io.IOException;
1214
import java.io.Reader;
1315
import java.io.StringReader;
1416
import java.io.StringWriter;
1517
import java.io.Writer;
18+
import java.nio.file.Files;
1619
import java.util.Collection;
1720
import java.util.List;
21+
import java.util.concurrent.atomic.AtomicBoolean;
1822
import javax.xml.parsers.DocumentBuilder;
1923
import javax.xml.parsers.DocumentBuilderFactory;
2024
import javax.xml.parsers.ParserConfigurationException;
2125
import javax.xml.stream.XMLOutputFactory;
2226
import javax.xml.stream.XMLStreamException;
2327
import javax.xml.stream.XMLStreamWriter;
28+
import org.junit.jupiter.api.AfterAll;
2429
import org.junit.jupiter.api.Test;
2530
import org.junit.platform.engine.TestDescriptor;
2631
import org.junit.platform.engine.TestExecutionResult;
2732
import org.junit.platform.engine.UniqueId;
2833
import org.junit.platform.engine.reporting.ReportEntry;
34+
import org.junit.platform.launcher.Launcher;
2935
import org.junit.platform.launcher.TestIdentifier;
3036
import org.junit.platform.launcher.TestPlan;
37+
import org.junit.platform.launcher.core.LauncherConfig;
38+
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
39+
import org.junit.platform.launcher.core.LauncherFactory;
40+
import org.junit.platform.testkit.engine.EngineTestKit;
3141
import org.mockito.Mockito;
3242
import org.opentest4j.TestAbortedException;
3343
import org.w3c.dom.Document;
@@ -37,11 +47,70 @@
3747
import org.xml.sax.InputSource;
3848
import org.xml.sax.SAXException;
3949

40-
public class BazelJUnitOuputListenerTest {
50+
public class BazelJUnitOutputListenerTest {
4151

4252
private TestDescriptor testDescriptor = new StubbedTestDescriptor(createId("descriptors"));
4353
private TestIdentifier identifier = TestIdentifier.from(testDescriptor);
4454

55+
/** This latch is used in TestAfterAllFails for testAfterAllFailuresAreReported */
56+
private static final AtomicBoolean causeFailure = new AtomicBoolean(false);
57+
58+
static final class TestAfterAllFails {
59+
@AfterAll
60+
static void afterAll() {
61+
if (causeFailure.get()) {
62+
throw new RuntimeException("I always fail.");
63+
}
64+
}
65+
66+
@Test
67+
void test() {}
68+
}
69+
70+
@Test
71+
public void testAfterAllFailuresAreReported() throws Exception {
72+
causeFailure.set(true);
73+
74+
// First let's do a sanity test that we have the expected failures for the @AfterAll
75+
EngineTestKit.engine("junit-jupiter")
76+
.selectors(selectClass(TestAfterAllFails.class))
77+
.execute()
78+
.containerEvents()
79+
.assertStatistics(stats -> stats.skipped(0).started(2).succeeded(1).aborted(0).failed(1));
80+
81+
// Now let's run the same test. Unfortunately we cannot use EngineTestKit since it has no way
82+
// to register a listener.
83+
File xmlFile = File.createTempFile("junit-report", "xml");
84+
BazelJUnitOutputListener listener = new BazelJUnitOutputListener(xmlFile.toPath());
85+
LauncherConfig config = LauncherConfig.builder().addTestExecutionListeners(listener).build();
86+
87+
Launcher launcher = LauncherFactory.create(config);
88+
89+
LauncherDiscoveryRequestBuilder request =
90+
LauncherDiscoveryRequestBuilder.request().selectors(selectClass(TestAfterAllFails.class));
91+
92+
TestPlan plan = launcher.discover(request.build());
93+
94+
launcher.execute(request.build());
95+
listener.close();
96+
97+
// now write an assertion to validate the XML file has an error
98+
String[] expectedStrings = {
99+
"<failure message=\"I always fail.\" type=\"java.lang.RuntimeException\">", "failures=\"1\"",
100+
};
101+
102+
// Useful for debugging the expected output
103+
// System.out.println(Files.readString(xmlFile.toPath()));
104+
105+
for (String expected : expectedStrings) {
106+
assertTrue(
107+
Files.lines(xmlFile.toPath()).anyMatch(line -> line.contains(expected)),
108+
"Expected to find " + expected + " in " + xmlFile);
109+
}
110+
111+
causeFailure.set(false);
112+
}
113+
45114
@Test
46115
public void testResultCanBeDisabled() {
47116
// Note: we do not call `markFinished` so the TestResult is null. This is what happens when

0 commit comments

Comments
 (0)