Skip to content

Commit

Permalink
Issue: #3166: Introduce --redirect-stdout and --redirect-stderr CLI o…
Browse files Browse the repository at this point in the history
…ptions to redirect STDOUT and STDERR
  • Loading branch information
Oyster-zx committed Feb 12, 2025
1 parent 35d1d98 commit 216bfdb
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ repository on GitHub.
* The `@TempDir` now warns when deleting symlinks that target locations outside the
temporary directory to signal that the target file or directory is _not_ deleted, only
the link to it.

* New optional CLI options `--redirect-stdout` and `--redirect-stderr` to redirect stdout and stderr outputs to a file.

[[release-notes-5.12.0-RC2-junit-vintage]]
=== JUnit Vintage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,15 @@ $ java -jar junit-platform-console-standalone-{platform-version}.jar <OPTIONS> \
--config-resource=configuration.properties
----

You can redirect standard output and standard error using the --redirect-stdout and --redirect-stderr options:

[source,console,subs=attributes+]
----
$ java -jar junit-platform-console-standalone-{platform-version}.jar <OPTIONS> \
--redirect-stdout=foo.txt
--redirect-stderr=bar.txt
----

[[junit-platform-reporting-legacy-xml]]
==== Legacy XML format

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public class TestConsoleOutputOptions {
private boolean isSingleColorPalette;
private Details details = DEFAULT_DETAILS;
private Theme theme = DEFAULT_THEME;
private Path stdoutPath;
private Path stderrPath;

public boolean isAnsiColorOutputDisabled() {
return this.ansiColorOutputDisabled;
Expand Down Expand Up @@ -73,4 +75,24 @@ public void setTheme(Theme theme) {
this.theme = theme;
}

@API(status = INTERNAL, since = "5.12")
public Path getStdoutPath() {
return this.stdoutPath;
}

@API(status = INTERNAL, since = "5.12")
public void setStdoutPath(Path stdoutPath) {
this.stdoutPath = stdoutPath;
}

@API(status = INTERNAL, since = "5.12")
public Path getStderrPath() {
return this.stderrPath;
}

@API(status = INTERNAL, since = "5.12")
public void setStderrPath(Path stderrPath) {
this.stderrPath = stderrPath;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,19 @@ static class ConsoleOutputOptions {
@Option(names = "-details-theme", hidden = true)
private final Theme theme2 = DEFAULT_THEME;

@Option(names = "--redirect-stdout", paramLabel = "FILE", description = "Redirect tests stdout to a file.")
private Path stdout;

@Option(names = "--redirect-stderr", paramLabel = "FILE", description = "Redirect tests stderr to a file.")
private Path stderr;

private void applyTo(TestConsoleOutputOptions result) {
result.setColorPalettePath(choose(colorPalette, colorPalette2, null));
result.setSingleColorPalette(singleColorPalette || singleColorPalette2);
result.setDetails(choose(details, details2, DEFAULT_DETAILS));
result.setTheme(choose(theme, theme2, DEFAULT_THEME));
result.setStdoutPath(stdout);
result.setStderrPath(stderr);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@
import static org.junit.platform.console.tasks.DiscoveryRequestCreator.toDiscoveryRequestBuilder;
import static org.junit.platform.launcher.LauncherConstants.OUTPUT_DIR_PROPERTY_NAME;

import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.EnumSet;
import java.util.List;
Expand Down Expand Up @@ -101,6 +104,8 @@ private TestExecutionSummary executeTests(PrintWriter out, Optional<Path> report
Launcher launcher = launcherSupplier.get();
SummaryGeneratingListener summaryListener = registerListeners(out, reportsDir, launcher);

redirectStdStreams(outputOptions.getStdoutPath(), outputOptions.getStderrPath());

LauncherDiscoveryRequestBuilder discoveryRequestBuilder = toDiscoveryRequestBuilder(discoveryOptions);
reportsDir.ifPresent(dir -> discoveryRequestBuilder.configurationParameter(OUTPUT_DIR_PROPERTY_NAME,
dir.toAbsolutePath().toString()));
Expand Down Expand Up @@ -190,6 +195,47 @@ private void printSummary(TestExecutionSummary summary, PrintWriter out) {
summary.printTo(out);
}

@API(status = INTERNAL, since = "5.12")
private boolean isSameFile(Path path1, Path path2) {
if (path1 == null || path2 == null)
return false;
return (path1.normalize().toAbsolutePath().equals(path2.normalize().toAbsolutePath()));
}

private void redirectStdStreams(Path stdoutPath, Path stderrPath) {
if (isSameFile(stdoutPath, stderrPath)) {
try {
PrintStream commonStream = new PrintStream(Files.newOutputStream(stdoutPath), true);
System.setOut(commonStream);
System.setErr(commonStream);
}
catch (IOException e) {
throw new JUnitException("Error setting up stream for Stdout and Stderr at path: " + stdoutPath, e);
}
}
else {
if (stdoutPath != null) {
try {
PrintStream outStream = new PrintStream(Files.newOutputStream(stdoutPath), true);
System.setOut(outStream);
}
catch (IOException e) {
throw new JUnitException("Error setting up stream for Stdout at path: " + stdoutPath, e);
}
}

if (stderrPath != null) {
try {
PrintStream errStream = new PrintStream(Files.newOutputStream(stderrPath), true);
System.setErr(errStream);
}
catch (IOException e) {
throw new JUnitException("Error setting up stream for Stderr at path: " + stderrPath, e);
}
}
}
}

@FunctionalInterface
public interface Factory {
ConsoleTestExecutor create(TestDiscoveryOptions discoveryOptions, TestConsoleOutputOptions outputOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.platform.engine.discovery.ClassNameFilter.STANDARD_INCLUDE_PATTERN;
Expand Down Expand Up @@ -60,6 +61,8 @@ void parseNoArguments() {
// @formatter:off
assertAll(
() -> assertFalse(options.output.isAnsiColorOutputDisabled()),
() -> assertNull(options.output.getStdoutPath()),
() -> assertNull(options.output.getStderrPath()),
() -> assertEquals(TestConsoleOutputOptions.DEFAULT_DETAILS, options.output.getDetails()),
() -> assertFalse(options.discovery.isScanClasspath()),
() -> assertEquals(List.of(STANDARD_INCLUDE_PATTERN), options.discovery.getIncludedClassNamePatterns()),
Expand Down Expand Up @@ -632,6 +635,44 @@ void parseInvalidConfigurationParameters() {
assertOptionWithMissingRequiredArgumentThrowsException("-config", "--config");
}

@ParameterizedTest
@EnumSource
void parseValidStdoutRedirectionFile(ArgsType type) {
var file = Paths.get("foo.txt");
// @formatter:off
assertAll(
() -> assertNull(type.parseArgLine("").output.getStdoutPath()),
() -> assertEquals(file, type.parseArgLine("--redirect-stdout=foo.txt").output.getStdoutPath()),
() -> assertEquals(file, type.parseArgLine("--redirect-stdout foo.txt").output.getStdoutPath()),
() -> assertEquals(file, type.parseArgLine("--redirect-stdout bar.txt --redirect-stdout foo.txt").output.getStdoutPath())
);
// @formatter:on
}

@Test
void parseInvalidStdoutRedirectionFile() {
assertOptionWithMissingRequiredArgumentThrowsException("--redirect-stdout");
}

@ParameterizedTest
@EnumSource
void parseValidStderrRedirectionFile(ArgsType type) {
var file = Paths.get("foo.txt");
// @formatter:off
assertAll(
() -> assertNull(type.parseArgLine("").output.getStderrPath()),
() -> assertEquals(file, type.parseArgLine("--redirect-stderr=foo.txt").output.getStderrPath()),
() -> assertEquals(file, type.parseArgLine("--redirect-stderr foo.txt").output.getStderrPath()),
() -> assertEquals(file, type.parseArgLine("--redirect-stderr bar.txt --redirect-stderr foo.txt").output.getStderrPath())
);
// @formatter:on
}

@Test
void parseInvalidStderrRedirectionFile() {
assertOptionWithMissingRequiredArgumentThrowsException("--redirect-stderr");
}

@Test
void parseInvalidConfigurationParametersResource() {
assertOptionWithMissingRequiredArgumentThrowsException("--config-resource");
Expand Down

0 comments on commit 216bfdb

Please sign in to comment.