Skip to content

Commit

Permalink
Add new merged standard streams interceptor.
Browse files Browse the repository at this point in the history
Add method registerMergedStandardStreams in StreamInterceptor to merge
stdout and stderr so both can be intercepted in an stdout interceptor.
This will keep the relative order for both outputs, which makes it
easier to correlate error messages with the corresponding output.

Add new configuration parameter junit.platform.output.capture.merge to
merge stdout and stderr and publish it as STDOUT_REPORT_ENTRY_KEY to
all registered TestExecutionListener instances.

Issue: junit-team#3166
  • Loading branch information
mobounya committed Jan 13, 2024
1 parent 2a291a1 commit 77d248d
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 6 deletions.
Expand Up @@ -60,6 +60,21 @@ public class LauncherConstants {
*/
public static final String CAPTURE_STDERR_PROPERTY_NAME = "junit.platform.output.capture.stderr";

/**
* Property name used to enable merging and capturing output to {@link System#err} and {@link System#out}:
* {@value}
*
* <p>If enabled, the JUnit Platform merges stdout and stderr and publishes
* it as a {@link ReportEntry} using the
* {@value #STDOUT_REPORT_ENTRY_KEY} key immediately before reporting the
* test identifier as finished.
*
* @see #STDOUT_REPORT_ENTRY_KEY
* @see ReportEntry
* @see TestExecutionListener#reportingEntryPublished(TestIdentifier, ReportEntry)
*/
public static final String CAPTURE_MERGED_STANDARD_STREAMS_PROPERTY_NAME = "junit.platform.output.capture.merge";

/**
* Property name used to configure the maximum number of bytes for buffering
* to use per thread and output type if output capturing is enabled:
Expand Down
Expand Up @@ -12,6 +12,7 @@

import static org.junit.platform.launcher.LauncherConstants.CAPTURE_MAX_BUFFER_DEFAULT;
import static org.junit.platform.launcher.LauncherConstants.CAPTURE_MAX_BUFFER_PROPERTY_NAME;
import static org.junit.platform.launcher.LauncherConstants.CAPTURE_MERGED_STANDARD_STREAMS_PROPERTY_NAME;
import static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDERR_PROPERTY_NAME;
import static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDOUT_PROPERTY_NAME;
import static org.junit.platform.launcher.LauncherConstants.STDERR_REPORT_ENTRY_KEY;
Expand Down Expand Up @@ -43,17 +44,28 @@ static Optional<StreamInterceptingTestExecutionListener> create(ConfigurationPar

boolean captureStdout = configurationParameters.getBoolean(CAPTURE_STDOUT_PROPERTY_NAME).orElse(false);
boolean captureStderr = configurationParameters.getBoolean(CAPTURE_STDERR_PROPERTY_NAME).orElse(false);
if (!captureStdout && !captureStderr) {
boolean captureMergeStandardStreams = configurationParameters.getBoolean(
CAPTURE_MERGED_STANDARD_STREAMS_PROPERTY_NAME).orElse(false);

if (!captureStdout && !captureStderr && !captureMergeStandardStreams) {
return Optional.empty();
}

int maxSize = configurationParameters.get(CAPTURE_MAX_BUFFER_PROPERTY_NAME, Integer::valueOf) //
.orElse(CAPTURE_MAX_BUFFER_DEFAULT);

Optional<StreamInterceptor> stdoutInterceptor = captureStdout ? StreamInterceptor.registerStdout(maxSize)
: Optional.empty();
Optional<StreamInterceptor> stderrInterceptor = captureStderr ? StreamInterceptor.registerStderr(maxSize)
: Optional.empty();
Optional<StreamInterceptor> stdoutInterceptor = Optional.empty();
Optional<StreamInterceptor> stderrInterceptor = Optional.empty();

if (captureMergeStandardStreams) {
stdoutInterceptor = StreamInterceptor.registerMergedStandardStreams(maxSize);
captureStderr = false;
captureStdout = true;
}
else {
stdoutInterceptor = captureStdout ? StreamInterceptor.registerStdout(maxSize) : Optional.empty();
stderrInterceptor = captureStderr ? StreamInterceptor.registerStderr(maxSize) : Optional.empty();
}

if ((!stdoutInterceptor.isPresent() && captureStdout) || (!stderrInterceptor.isPresent() && captureStderr)) {
stdoutInterceptor.ifPresent(StreamInterceptor::unregister);
Expand Down
Expand Up @@ -37,6 +37,12 @@ static Optional<StreamInterceptor> registerStderr(int maxNumberOfBytesPerThread)
return register(System.err, System::setErr, maxNumberOfBytesPerThread);
}

static Optional<StreamInterceptor> registerMergedStandardStreams(int maxNumberOfBytesPerThread) {
Optional<StreamInterceptor> interceptor = registerStdout(maxNumberOfBytesPerThread);
interceptor.ifPresent((System::setErr));
return interceptor;
}

static Optional<StreamInterceptor> register(PrintStream originalStream, Consumer<PrintStream> streamSetter,
int maxNumberOfBytesPerThread) {
if (originalStream instanceof StreamInterceptor) {
Expand Down
Expand Up @@ -15,6 +15,7 @@
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.junit.platform.engine.TestExecutionResult.successful;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;
import static org.junit.platform.launcher.LauncherConstants.CAPTURE_MERGED_STANDARD_STREAMS_PROPERTY_NAME;
import static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDERR_PROPERTY_NAME;
import static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDOUT_PROPERTY_NAME;
import static org.junit.platform.launcher.LauncherConstants.STDERR_REPORT_ENTRY_KEY;
Expand Down Expand Up @@ -119,7 +120,8 @@ void doesNotInterceptStreamWhenAlreadyBeingIntercepted(String configParam,
private static Stream<Arguments> systemStreams() {
return Stream.of(//
streamType(CAPTURE_STDOUT_PROPERTY_NAME, () -> System.out, STDOUT_REPORT_ENTRY_KEY), //
streamType(CAPTURE_STDERR_PROPERTY_NAME, () -> System.err, STDERR_REPORT_ENTRY_KEY));
streamType(CAPTURE_STDERR_PROPERTY_NAME, () -> System.err, STDERR_REPORT_ENTRY_KEY), //
streamType(CAPTURE_MERGED_STANDARD_STREAMS_PROPERTY_NAME, () -> System.out, STDOUT_REPORT_ENTRY_KEY));
}

private static Arguments streamType(String configParam, Supplier<PrintStream> printStreamSupplier,
Expand Down

0 comments on commit 77d248d

Please sign in to comment.