Skip to content

Commit 779ee68

Browse files
authored
Report captured output using OTR's new dedicated XML element (#4199)
If output capturing is enabled, captured output written to `System.out` and `System.err` is now included using OTR's new dedicated `output` element rather than as regular report entries.
1 parent dcb4f49 commit 779ee68

File tree

7 files changed

+77
-3
lines changed

7 files changed

+77
-3
lines changed

documentation/documentation.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ tasks {
158158
args.addAll("execute")
159159
args.addAll("--scan-classpath")
160160
args.addAll("--config=junit.platform.reporting.open.xml.enabled=true")
161+
args.addAll("--config=junit.platform.output.capture.stdout=true")
162+
args.addAll("--config=junit.platform.output.capture.stderr=true")
161163
outputs.dir(consoleLauncherTestReportsDir)
162164
argumentProviders.add(CommandLineArgumentProvider {
163165
listOf(

documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,17 @@ JUnit repository on GitHub.
4141
allow test engines to publish/attach files to containers and tests by calling
4242
`EngineExecutionListener.fileEntryPublished(...)`. Registered `TestExecutionListeners`
4343
can then access these files by overriding the `fileEntryPublished(...)` method.
44-
* The following improvements have been made to the open-test-reporting XML output:
44+
* The following improvements have been made to the
45+
<<../user-guide/index.adoc#junit-platform-reporting-open-test-reporting, Open Test Reporting>>
46+
XML output:
4547
- Information about the Git repository, the current branch, the commit hash, and the
4648
current worktree status are now included in the XML report, if applicable.
4749
- A section containing JUnit-specific metadata about each test/container to the HTML
4850
report is now written by open-test-reporting when added to the classpath/module path
4951
- Information about published files is now included as attachments.
52+
- If <<../user-guide/index.adoc#running-tests-capturing-output, output capturing>> is
53+
enabled, the captured output written to `System.out` and `System.err` is now included
54+
in the XML report.
5055
* Introduced contracts for Kotlin-specific assertion methods.
5156

5257

documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-reporting.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ The listener is auto-registered and can be configured via the following
4444
If enabled, the listener creates an XML report file named `open-test-report.xml` in the
4545
configured <<junit-platform-reporting-output-directory, output directory>>.
4646

47+
If <<running-tests-capturing-output, output capturing>> is enabled, the captured output
48+
written to `System.out` and `System.err` will be included in the report as well.
49+
4750
TIP: The {OpenTestReportingCliTool} can be used to convert from the event-based format to
4851
the hierarchical format which is more human-readable.
4952

gradle/plugins/common/src/main/kotlin/junitbuild.testing-conventions.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ tasks.withType<Test>().configureEach {
129129
"-Djunit.platform.reporting.output.dir=${reports.junitXml.outputLocation.get().asFile.absolutePath}/junit-{uniqueNumber}",
130130
)
131131
}
132+
systemProperty("junit.platform.output.capture.stdout", "true")
133+
systemProperty("junit.platform.output.capture.stderr", "true")
132134

133135
jvmArgumentProviders += objects.newInstance(JavaAgentArgumentProvider::class).apply {
134136
classpath.from(javaAgentClasspath)

junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListener.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
1414
import static org.junit.platform.commons.util.StringUtils.isNotBlank;
15+
import static org.junit.platform.launcher.LauncherConstants.STDERR_REPORT_ENTRY_KEY;
16+
import static org.junit.platform.launcher.LauncherConstants.STDOUT_REPORT_ENTRY_KEY;
1517
import static org.junit.platform.reporting.open.xml.JUnitFactory.legacyReportingName;
1618
import static org.junit.platform.reporting.open.xml.JUnitFactory.type;
1719
import static org.junit.platform.reporting.open.xml.JUnitFactory.uniqueId;
@@ -25,6 +27,7 @@
2527
import static org.opentest4j.reporting.events.core.CoreFactory.infrastructure;
2628
import static org.opentest4j.reporting.events.core.CoreFactory.metadata;
2729
import static org.opentest4j.reporting.events.core.CoreFactory.operatingSystem;
30+
import static org.opentest4j.reporting.events.core.CoreFactory.output;
2831
import static org.opentest4j.reporting.events.core.CoreFactory.reason;
2932
import static org.opentest4j.reporting.events.core.CoreFactory.result;
3033
import static org.opentest4j.reporting.events.core.CoreFactory.sources;
@@ -60,6 +63,7 @@
6063
import java.nio.file.Path;
6164
import java.nio.file.Paths;
6265
import java.time.Instant;
66+
import java.time.LocalDateTime;
6367
import java.util.Map;
6468
import java.util.Optional;
6569
import java.util.concurrent.ConcurrentHashMap;
@@ -90,6 +94,7 @@
9094
import org.junit.platform.launcher.TestPlan;
9195
import org.opentest4j.reporting.events.api.DocumentWriter;
9296
import org.opentest4j.reporting.events.api.NamespaceRegistry;
97+
import org.opentest4j.reporting.events.core.Attachments;
9398
import org.opentest4j.reporting.events.core.Infrastructure;
9499
import org.opentest4j.reporting.events.core.Result;
95100
import org.opentest4j.reporting.events.core.Sources;
@@ -345,8 +350,26 @@ public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry e
345350
String id = inProgressIds.get(testIdentifier.getUniqueIdObject());
346351
eventsFileWriter.append(reported(id, Instant.now()), //
347352
reported -> reported.append(attachments(), //
348-
attachments -> attachments.append(data(entry.getTimestamp()), //
349-
data -> entry.getKeyValuePairs().forEach(data::addEntry))));
353+
attachments -> {
354+
Map<String, String> keyValuePairs = entry.getKeyValuePairs();
355+
if (keyValuePairs.containsKey(STDOUT_REPORT_ENTRY_KEY)
356+
|| keyValuePairs.containsKey(STDERR_REPORT_ENTRY_KEY)) {
357+
attachOutput(attachments, entry.getTimestamp(), keyValuePairs.get(STDOUT_REPORT_ENTRY_KEY),
358+
"stdout");
359+
attachOutput(attachments, entry.getTimestamp(), keyValuePairs.get(STDERR_REPORT_ENTRY_KEY),
360+
"stderr");
361+
}
362+
else {
363+
attachments.append(data(entry.getTimestamp()), //
364+
data -> keyValuePairs.forEach(data::addEntry));
365+
}
366+
}));
367+
}
368+
369+
private static void attachOutput(Attachments attachments, LocalDateTime timestamp, String content, String source) {
370+
if (content != null) {
371+
attachments.append(output(timestamp), output -> output.withSource(source).withContent(content));
372+
}
350373
}
351374

352375
@Override

platform-tests/src/test/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListenerTests.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,23 @@
1616
import static org.junit.jupiter.api.Assertions.fail;
1717
import static org.junit.jupiter.api.Assumptions.assumeTrue;
1818
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;
19+
import static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDERR_PROPERTY_NAME;
20+
import static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDOUT_PROPERTY_NAME;
1921
import static org.junit.platform.launcher.LauncherConstants.OUTPUT_DIR_PROPERTY_NAME;
2022
import static org.junit.platform.launcher.LauncherConstants.OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER;
2123
import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
2224
import static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncher;
2325
import static org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener.ENABLED_PROPERTY_NAME;
2426
import static org.junit.platform.reporting.testutil.FileUtils.findPath;
2527

28+
import java.io.PrintStream;
2629
import java.net.URISyntaxException;
2730
import java.nio.file.Files;
2831
import java.nio.file.Path;
2932
import java.util.Map;
3033

34+
import org.junit.jupiter.api.AfterEach;
35+
import org.junit.jupiter.api.BeforeEach;
3136
import org.junit.jupiter.api.Test;
3237
import org.junit.jupiter.api.io.TempDir;
3338
import org.junit.jupiter.params.ParameterizedTest;
@@ -51,12 +56,32 @@
5156
*/
5257
public class OpenTestReportGeneratingListenerTests {
5358

59+
private PrintStream originalOut;
60+
private PrintStream originalErr;
61+
62+
@BeforeEach
63+
void wrapSystemPrintStreams() {
64+
// Work around nesting check in org.junit.platform.launcher.core.StreamInterceptor
65+
originalOut = System.out;
66+
System.setOut(new PrintStream(System.out));
67+
originalErr = System.err;
68+
System.setErr(new PrintStream(System.err));
69+
}
70+
71+
@AfterEach
72+
void restoreSystemPrintStreams() {
73+
System.setOut(originalOut);
74+
System.setErr(originalErr);
75+
}
76+
5477
@Test
5578
void writesValidXmlReport(@TempDir Path tempDirectory) throws Exception {
5679
var engine = new DemoHierarchicalTestEngine("dummy");
5780
engine.addTest("failingTest", "display<-->Name 😎", (context, descriptor) -> {
5881
var listener = context.request.getEngineExecutionListener();
5982
listener.reportingEntryPublished(descriptor, ReportEntry.from("key", "value"));
83+
System.out.println("Hello, stdout!");
84+
System.err.println("Hello, stderr!");
6085
fail("failure message");
6186
});
6287

@@ -105,6 +130,12 @@ void writesValidXmlReport(@TempDir Path tempDirectory) throws Exception {
105130
</data>
106131
</attachments>
107132
</e:reported>
133+
<e:reported id="2" time="${xmlunit.isDateTime}">
134+
<attachments>
135+
<output time="${xmlunit.isDateTime}" source="stdout"><![CDATA[Hello, stdout!]]></output>
136+
<output time="${xmlunit.isDateTime}" source="stderr"><![CDATA[Hello, stderr!]]></output>
137+
</attachments>
138+
</e:reported>
108139
<e:finished id="2" time="${xmlunit.isDateTime}">
109140
<result status="FAILED">
110141
<java:throwable assertionError="true" type="org.opentest4j.AssertionFailedError">
@@ -200,6 +231,8 @@ private void executeTests(Path tempDirectory, TestEngine engine, Path outputDir)
200231
var build = request() //
201232
.selectors(selectUniqueId(UniqueId.forEngine(engine.getId()))) //
202233
.configurationParameter(ENABLED_PROPERTY_NAME, String.valueOf(true)) //
234+
.configurationParameter(CAPTURE_STDOUT_PROPERTY_NAME, String.valueOf(true)) //
235+
.configurationParameter(CAPTURE_STDERR_PROPERTY_NAME, String.valueOf(true)) //
203236
.configurationParameter(OUTPUT_DIR_PROPERTY_NAME, outputDir.toString()) //
204237
.build();
205238
createLauncher(engine).execute(build, new OpenTestReportGeneratingListener(tempDirectory));

platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ tasks.test {
165165
dir("${rootDir}/documentation/src/test").withPathSensitivity(RELATIVE)
166166
}
167167

168+
// Disable capturing output since parallel execution is enabled and output of
169+
// external processes happens on non-test threads which can't reliably be
170+
// attributed to the test that started the process.
171+
systemProperty("junit.platform.output.capture.stdout", "false")
172+
systemProperty("junit.platform.output.capture.stderr", "false")
173+
168174
develocity {
169175
testDistribution {
170176
requirements.add("jdk=8")

0 commit comments

Comments
 (0)