diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc index e3802369cccc..f5b6fda0ff31 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc @@ -18,6 +18,8 @@ JUnit repository on GitHub. * Fix support for disabling ANSI colors on the console when the `NO_COLOR` environment variable is available. +* Enable auto-flushing of output in the `ConsoleLauncher` to fix issues with buffering, + in particular when using the `--details=testfeed` option. [[release-notes-5.12.0-M1-junit-platform-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild/exec/RunConsoleLauncher.kt b/gradle/plugins/common/src/main/kotlin/junitbuild/exec/RunConsoleLauncher.kt index a92f31195ad2..896e76090c6f 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild/exec/RunConsoleLauncher.kt +++ b/gradle/plugins/common/src/main/kotlin/junitbuild/exec/RunConsoleLauncher.kt @@ -6,7 +6,13 @@ import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property -import org.gradle.api.tasks.* +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.options.Option import org.gradle.jvm.toolchain.JavaLauncher import org.gradle.jvm.toolchain.JavaToolchainService @@ -16,7 +22,6 @@ import org.gradle.process.CommandLineArgumentProvider import org.gradle.process.ExecOperations import trackOperationSystemAsInput import java.io.ByteArrayOutputStream -import java.util.* import javax.inject.Inject @CacheableTask @@ -97,4 +102,13 @@ abstract class RunConsoleLauncher @Inject constructor(private val execOperations debugging.set(enabled) } + @Suppress("unused") + @Option( + option = "show-output", + description = "Show output" + ) + fun setShowOutput(showOutput: Boolean) { + hideOutput.set(!showOutput) + } + } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java b/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java index 6ffd9ebefa5d..54345f061e2e 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java @@ -30,36 +30,17 @@ public class ConsoleLauncher { public static void main(String... args) { - PrintWriter out = new PrintWriter(System.out); - PrintWriter err = new PrintWriter(System.err); - CommandResult result = run(out, err, args); + CommandResult result = newCommandFacade().run(args); System.exit(result.getExitCode()); } @API(status = INTERNAL, since = "1.0") public static CommandResult run(PrintWriter out, PrintWriter err, String... args) { - ConsoleLauncher consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, out, err); - return consoleLauncher.run(args); + return newCommandFacade().run(args, out, err); } - private final ConsoleTestExecutor.Factory consoleTestExecutorFactory; - private final PrintWriter out; - private final PrintWriter err; - - ConsoleLauncher(ConsoleTestExecutor.Factory consoleTestExecutorFactory, PrintWriter out, PrintWriter err) { - this.consoleTestExecutorFactory = consoleTestExecutorFactory; - this.out = out; - this.err = err; - } - - CommandResult run(String... args) { - try { - return new CommandFacade(consoleTestExecutorFactory).run(out, err, args); - } - finally { - out.flush(); - err.flush(); - } + private static CommandFacade newCommandFacade() { + return new CommandFacade(ConsoleTestExecutor::new); } } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java index a591a59abb70..b1690e279c96 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java @@ -33,10 +33,24 @@ public CommandFacade(ConsoleTestExecutor.Factory consoleTestExecutorFactory) { this.consoleTestExecutorFactory = consoleTestExecutorFactory; } - public CommandResult run(PrintWriter out, PrintWriter err, String[] args) { + public CommandResult run(String[] args) { + return run(args, Optional.empty()); + } + + public CommandResult run(String[] args, PrintWriter out, PrintWriter err) { + try { + return run(args, Optional.of(new OutputStreamConfig(out, err))); + } + finally { + out.flush(); + err.flush(); + } + } + + private CommandResult run(String[] args, Optional outputStreamConfig) { Optional version = ManifestVersionProvider.getImplementationVersion(); System.setProperty("junit.docs.version", version.map(it -> it.endsWith("-SNAPSHOT") ? "snapshot" : it).orElse("current")); - return new MainCommand(consoleTestExecutorFactory).run(out, err, args); + return new MainCommand(consoleTestExecutorFactory).run(args, outputStreamConfig); } } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java index d4a86e807785..941a4976ea3e 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java @@ -103,10 +103,11 @@ private Object runCommand(String subcommand, Optional triggeringOption) List args = new ArrayList<>(commandLine.getParseResult().expandedArgs()); triggeringOption.ifPresent(args::remove); - CommandResult result = runCommand(commandLine.getOut(), // - commandLine.getErr(), // + CommandResult result = runCommand( // + new CommandLine(command), // args.toArray(new String[0]), // - command); + Optional.of(new OutputStreamConfig(commandLine)) // + ); this.commandResult = result; printDeprecationWarning(subcommand, triggeringOption, commandLine); @@ -130,24 +131,19 @@ private static void printDeprecationWarning(String subcommand, Optional err.flush(); } - CommandResult run(PrintWriter out, PrintWriter err, String[] args) { + CommandResult run(String[] args, Optional outputStreamConfig) { CommandLine commandLine = new CommandLine(this) // .addSubcommand(new DiscoverTestsCommand(consoleTestExecutorFactory)) // .addSubcommand(new ExecuteTestsCommand(consoleTestExecutorFactory)) // .addSubcommand(new ListTestEnginesCommand()); - return runCommand(out, err, args, commandLine); + return runCommand(commandLine, args, outputStreamConfig); } - private static CommandResult runCommand(PrintWriter out, PrintWriter err, String[] args, Object command) { - return runCommand(out, err, args, new CommandLine(command)); - } - - private static CommandResult runCommand(PrintWriter out, PrintWriter err, String[] args, - CommandLine commandLine) { - int exitCode = BaseCommand.initialize(commandLine) // - .setOut(out) // - .setErr(err) // - .execute(args); + private static CommandResult runCommand(CommandLine commandLine, String[] args, + Optional outputStreamConfig) { + BaseCommand.initialize(commandLine); + outputStreamConfig.ifPresent(it -> it.applyTo(commandLine)); + int exitCode = commandLine.execute(args); return CommandResult.create(exitCode, getLikelyExecutedCommand(commandLine).getExecutionResult()); } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/OutputStreamConfig.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/OutputStreamConfig.java new file mode 100644 index 000000000000..dd72ed9e9f0b --- /dev/null +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/OutputStreamConfig.java @@ -0,0 +1,34 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.console.options; + +import java.io.PrintWriter; + +import picocli.CommandLine; + +class OutputStreamConfig { + + private final PrintWriter out; + private final PrintWriter err; + + OutputStreamConfig(CommandLine commandLine) { + this(commandLine.getOut(), commandLine.getErr()); + } + + OutputStreamConfig(PrintWriter out, PrintWriter err) { + this.out = out; + this.err = err; + } + + void applyTo(CommandLine commandLine) { + commandLine.setOut(out).setErr(err); + } +} diff --git a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java index ef30a6d3db59..f4fb02f4a935 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java @@ -22,7 +22,6 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.platform.console.tasks.ConsoleTestExecutor; /** * @since 1.0 @@ -36,8 +35,7 @@ class ConsoleLauncherTests { @EmptySource @MethodSource("commandsWithEmptyOptionExitCodes") void displayHelp(String command) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - var exitCode = consoleLauncher.run(command, "--help").getExitCode(); + var exitCode = ConsoleLauncher.run(printSink, printSink, command, "--help").getExitCode(); assertEquals(0, exitCode); assertThat(output()).contains("--help"); @@ -47,8 +45,7 @@ void displayHelp(String command) { @EmptySource @MethodSource("commandsWithEmptyOptionExitCodes") void displayVersion(String command) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - var exitCode = consoleLauncher.run(command, "--version").getExitCode(); + var exitCode = ConsoleLauncher.run(printSink, printSink, command, "--version").getExitCode(); assertEquals(0, exitCode); assertThat(output()).contains("JUnit Platform Console Launcher"); @@ -57,8 +54,7 @@ void displayVersion(String command) { @ParameterizedTest(name = "{0}") @MethodSource("commandsWithEmptyOptionExitCodes") void displayBanner(String command) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - consoleLauncher.run(command); + ConsoleLauncher.run(printSink, printSink, command); assertThat(output()).contains("Thanks for using JUnit!"); } @@ -66,8 +62,7 @@ void displayBanner(String command) { @ParameterizedTest(name = "{0}") @MethodSource("commandsWithEmptyOptionExitCodes") void disableBanner(String command, int expectedExitCode) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - var exitCode = consoleLauncher.run(command, "--disable-banner").getExitCode(); + var exitCode = ConsoleLauncher.run(printSink, printSink, command, "--disable-banner").getExitCode(); assertEquals(expectedExitCode, exitCode); assertThat(output()).doesNotContain("Thanks for using JUnit!"); @@ -76,8 +71,7 @@ void disableBanner(String command, int expectedExitCode) { @ParameterizedTest(name = "{0}") @MethodSource("commandsWithEmptyOptionExitCodes") void executeWithUnknownCommandLineOption(String command) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - var exitCode = consoleLauncher.run(command, "--all").getExitCode(); + var exitCode = ConsoleLauncher.run(printSink, printSink, command, "--all").getExitCode(); assertEquals(-1, exitCode); assertThat(output()).contains("Unknown option: '--all'").contains("Usage:"); @@ -90,8 +84,7 @@ private String output() { @ParameterizedTest(name = "{0}") @MethodSource("commandsWithEmptyOptionExitCodes") void executeWithoutCommandLineOptions(String command, int expectedExitCode) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - var actualExitCode = consoleLauncher.run(command).getExitCode(); + var actualExitCode = ConsoleLauncher.run(printSink, printSink, command).getExitCode(); assertEquals(expectedExitCode, actualExitCode); } diff --git a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java index 7ae6ba8fd921..0d2c240d8ea3 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java +++ b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java @@ -17,6 +17,7 @@ import java.io.StringWriter; import java.util.Optional; +import org.junit.platform.console.options.CommandFacade; import org.junit.platform.console.tasks.ConsoleTestExecutor; /** @@ -26,16 +27,14 @@ class ConsoleLauncherWrapper { private final StringWriter out = new StringWriter(); private final StringWriter err = new StringWriter(); - private final ConsoleLauncher consoleLauncher; + private final ConsoleTestExecutor.Factory consoleTestExecutorFactory; ConsoleLauncherWrapper() { this(ConsoleTestExecutor::new); } private ConsoleLauncherWrapper(ConsoleTestExecutor.Factory consoleTestExecutorFactory) { - var outWriter = new PrintWriter(out, false); - var errWriter = new PrintWriter(err, false); - this.consoleLauncher = new ConsoleLauncher(consoleTestExecutorFactory, outWriter, errWriter); + this.consoleTestExecutorFactory = consoleTestExecutorFactory; } public ConsoleLauncherWrapperResult execute(String... args) { @@ -47,7 +46,9 @@ public ConsoleLauncherWrapperResult execute(int expectedExitCode, String... args } public ConsoleLauncherWrapperResult execute(Optional expectedCode, String... args) { - var result = consoleLauncher.run(args); + var outWriter = new PrintWriter(out, false); + var errWriter = new PrintWriter(err, false); + var result = new CommandFacade(consoleTestExecutorFactory).run(args, outWriter, errWriter); var code = result.getExitCode(); var outText = out.toString(); var errText = err.toString();