This is an automated email from the ASF dual-hosted git repository. rmannibucau pushed a commit to branch rmannibucau/flush-output-toggle in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
commit 7be86d1ee9c7992dc7a48a218ac4318c320c2b1b Author: Romain Manni-Bucau <rmannibu...@gmail.com> AuthorDate: Wed Jul 8 13:02:23 2020 +0200 enable to flush regularly output of the forked process --- .../plugin/surefire/AbstractSurefireMojo.java | 15 +++++++- .../surefire/booterclient/BooterSerializer.java | 5 +++ ...ooterDeserializerProviderConfigurationTest.java | 2 +- ...BooterDeserializerStartupConfigurationTest.java | 2 +- .../AbstractNoninterruptibleWritableChannel.java | 6 +++ .../util/internal/WritableBufferedByteChannel.java | 2 + .../maven/surefire/booter/BooterConstants.java | 1 + .../maven/surefire/booter/BooterDeserializer.java | 4 +- .../apache/maven/surefire/booter/ForkedBooter.java | 45 ++++++++++++++++++++++ .../surefire/booter/ProviderConfiguration.java | 10 ++++- .../spi/LegacyMasterProcessChannelEncoder.java | 5 +++ 11 files changed, 92 insertions(+), 5 deletions(-) diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java index 38d34fb..0f92bbf 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java @@ -809,6 +809,18 @@ public abstract class AbstractSurefireMojo private Map<String, String> jdkToolchain; /** + * How often output is forced to be flushed. + * Useful when there is no output for N ms but some data are buffered. + * It will trigger a flush each configured interval to ensure + * data don't stay blocked in a buffer. + * Setting it to 0 disable that feature. + * + * @since 3.0.0-M6 + */ + @Parameter( defaultValue = "0" ) + private long outputFlushInterval; + + /** * */ @Component @@ -1850,7 +1862,8 @@ public abstract class AbstractSurefireMojo testSuiteDefinition, providerProperties, null, false, cli, getSkipAfterFailureCount(), Shutdown.parameterOf( getShutdown() ), - getForkedProcessExitTimeoutInSeconds() ); + getForkedProcessExitTimeoutInSeconds(), + outputFlushInterval ); } private static Map<String, String> toStringProperties( Properties properties ) diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java index c8aee3c..6e62079 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java @@ -53,6 +53,7 @@ import static org.apache.maven.surefire.booter.BooterConstants.FORKTESTSET_PREFE import static org.apache.maven.surefire.booter.BooterConstants.INCLUDES_PROPERTY_PREFIX; import static org.apache.maven.surefire.booter.BooterConstants.ISTRIMSTACKTRACE; import static org.apache.maven.surefire.booter.BooterConstants.MAIN_CLI_OPTIONS; +import static org.apache.maven.surefire.booter.BooterConstants.OUTPUT_FLUSH_INTERVAL_MS; import static org.apache.maven.surefire.booter.BooterConstants.PLUGIN_PID; import static org.apache.maven.surefire.booter.BooterConstants.PROCESS_CHECKER; import static org.apache.maven.surefire.booter.BooterConstants.PROVIDER_CONFIGURATION; @@ -181,6 +182,10 @@ class BooterSerializer properties.addList( mainCliOptions, MAIN_CLI_OPTIONS ); } properties.setNullableProperty( SYSTEM_EXIT_TIMEOUT, toString( providerConfiguration.getSystemExitTimeout() ) ); + properties.setNullableProperty( + OUTPUT_FLUSH_INTERVAL_MS, + providerConfiguration.getOutputFlushInterval() == null + ? "0" : toString( providerConfiguration.getOutputFlushInterval() ) ); File surefireTmpDir = forkConfiguration.getTempDirectory(); boolean debug = forkConfiguration.isDebug(); diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java index 7a08390..e62d1c3 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java @@ -279,7 +279,7 @@ public class BooterDeserializerProviderConfigurationTest RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, null ); return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, true, reporterConfiguration, new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new HashMap<String, String>(), TEST_TYPED, - readTestsFromInStream, cli, 0, Shutdown.DEFAULT, 0 ); + readTestsFromInStream, cli, 0, Shutdown.DEFAULT, 0, 0L ); } private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration ) diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java index 669e73c..cfa9f55 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java @@ -200,7 +200,7 @@ public class BooterDeserializerStartupConfigurationTest RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, null ); return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, true, reporterConfiguration, new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new HashMap<String, String>(), - BooterDeserializerProviderConfigurationTest.TEST_TYPED, true, cli, 0, Shutdown.DEFAULT, 0 ); + BooterDeserializerProviderConfigurationTest.TEST_TYPED, true, cli, 0, Shutdown.DEFAULT, 0, 0L ); } private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration ) diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/AbstractNoninterruptibleWritableChannel.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/AbstractNoninterruptibleWritableChannel.java index fe998f3..6e4a05d 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/AbstractNoninterruptibleWritableChannel.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/AbstractNoninterruptibleWritableChannel.java @@ -39,6 +39,12 @@ abstract class AbstractNoninterruptibleWritableChannel implements WritableBuffer protected abstract void flushImpl() throws IOException; @Override + public final void flush() throws IOException + { + flushImpl(); + } + + @Override public final int write( ByteBuffer src ) throws IOException { return write( src, true ); diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/WritableBufferedByteChannel.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/WritableBufferedByteChannel.java index 42c0d08..cc8409c 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/WritableBufferedByteChannel.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/WritableBufferedByteChannel.java @@ -33,4 +33,6 @@ import java.nio.channels.WritableByteChannel; public interface WritableBufferedByteChannel extends WritableByteChannel { void writeBuffered( ByteBuffer src ) throws IOException; + + void flush() throws IOException; } diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java index fa664be..5c5c34f 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java @@ -59,4 +59,5 @@ public final class BooterConstants public static final String PLUGIN_PID = "pluginPid"; public static final String PROCESS_CHECKER = "processChecker"; public static final String FORK_NODE_CONNECTION_STRING = "forkNodeConnectionString"; + public static final String OUTPUT_FLUSH_INTERVAL_MS = "outputFlushInterval"; } diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java index 4ec8cec..0df4ead 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java @@ -131,11 +131,13 @@ public class BooterDeserializer Integer systemExitTimeout = systemExitTimeoutAsString == null ? null : Integer.valueOf( systemExitTimeoutAsString ); + long outputFlushIntervalMs = properties.getLongProperty( OUTPUT_FLUSH_INTERVAL_MS ); + return new ProviderConfiguration( dirScannerParams, runOrderParameters, properties.getBooleanProperty( FAILIFNOTESTS ), reporterConfiguration, testNg, testSuiteDefinition, properties.getProperties(), typeEncodedTestForFork, preferTestsFromInStream, fromStrings( cli ), failFastCount, shutdown, - systemExitTimeout ); + systemExitTimeout, outputFlushIntervalMs ); } public StartupConfiguration getStartupConfiguration() diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java index d8e3bfe..cd27162 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java @@ -27,6 +27,9 @@ import org.apache.maven.surefire.api.booter.ForkingReporterFactory; import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder; import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder; import org.apache.maven.surefire.api.booter.Shutdown; +import org.apache.maven.surefire.api.util.internal.DaemonThreadFactory; +import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel; +import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder; import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelProcessorFactory; import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory; import org.apache.maven.surefire.api.provider.CommandListener; @@ -50,6 +53,7 @@ import java.lang.reflect.InvocationTargetException; import java.security.AccessControlException; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.Semaphore; @@ -100,6 +104,7 @@ public final class ForkedBooter private ForkingReporterFactory forkingReporterFactory; private StartupConfiguration startupConfiguration; private Object testSet; + private ScheduledExecutorService flusher; private void setupBooter( String tmpDir, String dumpFileName, String surefirePropsFileName, String effectiveSystemPropertiesFileName ) @@ -129,6 +134,34 @@ public final class ForkedBooter flushEventChannelOnExit(); + final Long outputFlushInterval = providerConfiguration.getOutputFlushInterval(); + if ( outputFlushInterval != null && outputFlushInterval > 0 + && LegacyMasterProcessChannelEncoder.class.isInstance( eventChannel ) ) + { + final LegacyMasterProcessChannelEncoder enc = LegacyMasterProcessChannelEncoder.class.cast( eventChannel ); + flusher = Executors.newSingleThreadScheduledExecutor( DaemonThreadFactory.newDaemonThreadFactory() ); + flusher.scheduleAtFixedRate( new Runnable() + { + @Override + public void run() + { + final WritableBufferedByteChannel c = WritableBufferedByteChannel.class.cast( enc.getOut() ); + try + { + if ( c.isOpen() ) + { + c.flush(); + } + + } + catch ( Exception e ) + { + // no-op, not important, we tried anyway + } + } + }, outputFlushInterval, outputFlushInterval, MILLISECONDS ); + } + forkingReporterFactory = createForkingReporterFactory(); ConsoleLogger logger = (ConsoleLogger) forkingReporterFactory.createReporter(); commandReader = new CommandReader( decoder, providerConfiguration.getShutdown(), logger ); @@ -237,6 +270,18 @@ public final class ForkedBooter e.printStackTrace(); } } + if ( flusher != null ) + { + flusher.shutdownNow(); + try + { // give a chance to finish sync, not that important if it fails + flusher.awaitTermination( 2, SECONDS ); + } + catch ( InterruptedException e ) + { + Thread.currentThread().interrupt(); + } + } } private PingScheduler listenToShutdownCommands( String ppid, ConsoleLogger logger ) diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java index 54b6187..77519c2 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java @@ -68,6 +68,8 @@ public class ProviderConfiguration private final Integer systemExitTimeout; + private final Long outputFlushInterval; + @SuppressWarnings( "checkstyle:parameternumber" ) public ProviderConfiguration( DirectoryScannerParameters directoryScannerParameters, RunOrderParameters runOrderParameters, boolean failIfNoTests, @@ -75,7 +77,7 @@ public class ProviderConfiguration TestRequest testSuiteDefinition, Map<String, String> providerProperties, TypeEncodedValue typeEncodedTestSet, boolean readTestsFromInStream, List<CommandLineOption> mainCliOptions, int skipAfterFailureCount, - Shutdown shutdown, Integer systemExitTimeout ) + Shutdown shutdown, Integer systemExitTimeout, Long outputFlushInterval ) { this.runOrderParameters = runOrderParameters; this.providerProperties = providerProperties; @@ -90,6 +92,12 @@ public class ProviderConfiguration this.skipAfterFailureCount = skipAfterFailureCount; this.shutdown = shutdown; this.systemExitTimeout = systemExitTimeout; + this.outputFlushInterval = outputFlushInterval; + } + + public Long getOutputFlushInterval() + { + return outputFlushInterval; } public ReporterConfiguration getReporterConfiguration() diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoder.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoder.java index 3cbc8eb..4c6d494 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoder.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoder.java @@ -275,6 +275,11 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn error( stackTraceWriter, trimStackTraces, BOOTERCODE_JVM_EXIT_ERROR, true ); } + public WritableBufferedByteChannel getOut() + { + return out; + } + private void error( StackTraceWriter stackTraceWriter, boolean trimStackTraces, ForkedProcessEventType event, @SuppressWarnings( "SameParameterValue" ) boolean sendImmediately ) {