This is an automated email from the ASF dual-hosted git repository. tibordigana pushed a commit to branch comm in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
commit 67bc6e2e995816c9ec6f8822f78807f6eb470889 Author: tibordigana <tibor.dig...@gmail.com> AuthorDate: Tue Jan 12 08:51:50 2021 +0100 fixed decoder and tests + logger of binary stream --- .../maven/plugin/surefire/SurefireHelper.java | 2 + .../plugin/surefire/booterclient/ForkStarter.java | 13 ++ .../output/InPluginProcessDumpSingleton.java | 6 + .../booterclient/ForkingRunListenerTest.java | 12 ++ .../TestLessInputStreamBuilderTest.java | 3 +- .../TestProvidingInputStreamTest.java | 3 +- .../booterclient/output/ForkClientTest.java | 12 ++ .../maven/plugin/surefire/extensions/E2ETest.java | 12 ++ .../extensions/EventConsumerThreadTest.java | 12 ++ .../extensions/ForkedProcessEventNotifierTest.java | 12 ++ .../maven/surefire/extensions/ForkChannelTest.java | 12 ++ .../maven/surefire/stream/EventDecoderTest.java | 12 ++ .../surefire/api/booter/DumpErrorSingleton.java | 9 + .../maven/surefire/api/fork/ForkNodeArguments.java | 4 + .../surefire/api/stream/AbstractStreamDecoder.java | 5 + .../api/stream/AbstractStreamDecoderTest.java | 12 ++ .../apache/maven/surefire/booter/ForkedBooter.java | 7 +- .../maven/surefire/booter/ForkedNodeArg.java | 16 +- .../surefire/booter/stream/CommandDecoder.java | 65 ++++++ .../maven/surefire/booter/CommandReaderTest.java | 4 +- .../surefire/booter/ForkedBooterMockTest.java | 4 +- .../booter/spi/CommandChannelDecoderTest.java | 237 +++++++++++++++++++-- .../resources/binary-commands/75171711-encoder.bin | Bin 0 -> 851 bytes 23 files changed, 451 insertions(+), 23 deletions(-) diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireHelper.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireHelper.java index d0cac4d..9149946 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireHelper.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireHelper.java @@ -64,6 +64,8 @@ public final class SurefireHelper public static final String DUMP_FILENAME = DUMP_FILE_DATE + DUMP_FILE_EXT; + public static final String EVENTS_BINARY_DUMP_FILENAME_FORMATTER = DUMP_FILE_DATE + "-jvmRun%d-events.bin"; + /** * The maximum path that does not require long path prefix on Windows.<br> * See {@code sun/nio/fs/WindowsPath} in diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java index 3ebc935..55647a1 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java @@ -932,5 +932,18 @@ public class ForkStarter { return log; } + + @Override + public File getEventStreamBinaryFile() + { + return InPluginProcessDumpSingleton.getSingleton() + .getEventStreamBinaryFile( dumpLogDir, forkChannelId ); + } + + @Override + public File getCommandStreamBinaryFile() + { + return null; + } } } diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/InPluginProcessDumpSingleton.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/InPluginProcessDumpSingleton.java index 372c046..aa4fa3c 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/InPluginProcessDumpSingleton.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/InPluginProcessDumpSingleton.java @@ -28,6 +28,7 @@ import static org.apache.maven.plugin.surefire.SurefireHelper.DUMP_FILENAME; import static org.apache.maven.plugin.surefire.SurefireHelper.DUMP_FILENAME_FORMATTER; import static org.apache.maven.plugin.surefire.SurefireHelper.DUMPSTREAM_FILENAME; import static org.apache.maven.plugin.surefire.SurefireHelper.DUMPSTREAM_FILENAME_FORMATTER; +import static org.apache.maven.plugin.surefire.SurefireHelper.EVENTS_BINARY_DUMP_FILENAME_FORMATTER; /** * Reports errors to dump file. @@ -82,6 +83,11 @@ public final class InPluginProcessDumpSingleton DumpFileUtils.dumpException( t, msg == null ? "null" : msg, dump ); } + public File getEventStreamBinaryFile( File reportsDirectory, int jvmRun ) + { + return new File( reportsDirectory, format( EVENTS_BINARY_DUMP_FILENAME_FORMATTER, jvmRun ) ); + } + private File newDumpStreamFile( File reportsDirectory ) { return new File( reportsDirectory, DUMPSTREAM_FILENAME ); diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java index e5520f7..48eb35f 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java @@ -375,6 +375,18 @@ public class ForkingRunListenerTest { return !dumpStreamText.isEmpty() || !logWarningAtEnd.isEmpty(); } + + @Override + public File getEventStreamBinaryFile() + { + return null; + } + + @Override + public File getCommandStreamBinaryFile() + { + return null; + } } private static class EH implements EventHandler<Event> diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java index 41b3db3..87bad45 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java @@ -192,7 +192,8 @@ public class TestLessInputStreamBuilderTest throw new IOException(); } }; - MasterProcessChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), new ForkedNodeArg( 1 ) ); + MasterProcessChannelDecoder decoder = + new CommandChannelDecoder( newChannel( is ), new ForkedNodeArg( 1, false ) ); builder.getImmediateCommands().shutdown( KILL ); builder.getImmediateCommands().noop(); Command bye = decoder.decode(); diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java index bafcea5..29a5745 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java @@ -189,7 +189,8 @@ public class TestProvidingInputStreamTest throw new IOException(); } }; - MasterProcessChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), new ForkedNodeArg( 1 ) ); + MasterProcessChannelDecoder decoder = + new CommandChannelDecoder( newChannel( is ), new ForkedNodeArg( 1, false ) ); pluginIs.acknowledgeByeEventReceived(); pluginIs.noop(); Command bye = decoder.decode(); diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java index 8cda588..531bafb 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java @@ -1884,6 +1884,18 @@ public class ForkClientTest { return !dumpStreamText.isEmpty() || !logWarningAtEnd.isEmpty(); } + + @Override + public File getEventStreamBinaryFile() + { + return null; + } + + @Override + public File getCommandStreamBinaryFile() + { + return null; + } } /** diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java index 37bc259..87bcca7 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java @@ -333,5 +333,17 @@ public class E2ETest { return logger; } + + @Override + public File getEventStreamBinaryFile() + { + return null; + } + + @Override + public File getCommandStreamBinaryFile() + { + return null; + } } } diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThreadTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThreadTest.java index 4bd5c13..9357080 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThreadTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThreadTest.java @@ -227,5 +227,17 @@ public class EventConsumerThreadTest { return null; } + + @Override + public File getEventStreamBinaryFile() + { + return null; + } + + @Override + public File getCommandStreamBinaryFile() + { + return null; + } } } diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/ForkedProcessEventNotifierTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/ForkedProcessEventNotifierTest.java index ee0007c..558c021 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/ForkedProcessEventNotifierTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/ForkedProcessEventNotifierTest.java @@ -1188,6 +1188,18 @@ public class ForkedProcessEventNotifierTest { return !dumpStreamText.isEmpty() || !logWarningAtEnd.isEmpty(); } + + @Override + public File getEventStreamBinaryFile() + { + return null; + } + + @Override + public File getCommandStreamBinaryFile() + { + return null; + } } /** diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java index 34d3325..428ab10 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java @@ -68,6 +68,18 @@ public class ForkChannelTest final String sessionId = UUID.randomUUID().toString(); ForkNodeArguments forkNodeArguments = new ForkNodeArguments() { + @Override + public File getEventStreamBinaryFile() + { + return null; + } + + @Override + public File getCommandStreamBinaryFile() + { + return null; + } + @Nonnull @Override public String getSessionId() diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java index 31fb51e..ccfd3f0 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java @@ -768,6 +768,18 @@ public class EventDecoderTest { return null; } + + @Override + public File getEventStreamBinaryFile() + { + return null; + } + + @Override + public File getCommandStreamBinaryFile() + { + return null; + } } } diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/DumpErrorSingleton.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/DumpErrorSingleton.java index 6cc7db8..381f852 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/DumpErrorSingleton.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/DumpErrorSingleton.java @@ -40,6 +40,7 @@ public final class DumpErrorSingleton private File dumpFile; private File dumpStreamFile; + private File binaryDumpStreamFile; private DumpErrorSingleton() { @@ -54,6 +55,9 @@ public final class DumpErrorSingleton { dumpFile = createDumpFile( reportsDir, dumpFileName ); dumpStreamFile = createDumpStreamFile( reportsDir, dumpFileName ); + String fileNameWithoutExtension = + dumpFileName.contains( "." ) ? dumpFileName.substring( 0, dumpFileName.lastIndexOf( '.' ) ) : dumpFileName; + binaryDumpStreamFile = createDumpStreamFile( reportsDir, fileNameWithoutExtension + "-commands.bin" ); } public synchronized File dumpException( Throwable t, String msg ) @@ -92,6 +96,11 @@ public final class DumpErrorSingleton return dumpStreamFile; } + public File getCommandStreamBinaryFile() + { + return binaryDumpStreamFile; + } + private File createDumpFile( File reportsDir, String dumpFileName ) { return newDumpFile( reportsDir, dumpFileName + DUMP_FILE_EXT ); diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java index 0d8aa75..7d20e0e 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java @@ -54,4 +54,8 @@ public interface ForkNodeArguments @Nonnull ConsoleLogger getConsoleLogger(); + + File getEventStreamBinaryFile(); + + File getCommandStreamBinaryFile(); } diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java index d0daa25..e989d12 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java @@ -106,6 +106,10 @@ public abstract class AbstractStreamDecoder<M, MT extends Enum<MT>, ST extends E return arguments; } + protected void debugStream( byte[] array, int position, int remaining ) + { + } + protected MT readMessageType( @Nonnull Memento memento ) throws IOException, MalformedFrameException { byte[] header = getEncodedMagicNumber(); @@ -468,6 +472,7 @@ public abstract class AbstractStreamDecoder<M, MT extends Enum<MT>, ST extends E } else { + debugStream( buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining() ); return readBytes >= recommendedCount ? OVERFLOW : UNDERFLOW; } } diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java index fb61e4c..3f4c0bd 100644 --- a/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java +++ b/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java @@ -633,6 +633,18 @@ public class AbstractStreamDecoderTest { return null; } + + @Override + public File getEventStreamBinaryFile() + { + return null; + } + + @Override + public File getCommandStreamBinaryFile() + { + return null; + } } private static class Mock extends AbstractStreamDecoder<Event, ForkedProcessEventType, SegmentType> 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 f6a19e8..f00cb6a 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 @@ -62,6 +62,7 @@ import static java.lang.Thread.currentThread; import static java.util.ServiceLoader.load; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.apache.maven.surefire.api.cli.CommandLineOption.LOGGING_LEVEL_DEBUG; import static org.apache.maven.surefire.api.util.ReflectionUtils.instantiateOneArg; import static org.apache.maven.surefire.api.util.internal.DaemonThreadFactory.newDaemonThreadFactory; import static org.apache.maven.surefire.api.util.internal.StringUtils.NL; @@ -128,7 +129,9 @@ public final class ForkedBooter String channelConfig = booterDeserializer.getConnectionString(); channelProcessorFactory = lookupDecoderFactory( channelConfig ); channelProcessorFactory.connect( channelConfig ); - ForkNodeArguments args = new ForkedNodeArg( forkNumber ); + boolean isDebugging = isDebugging(); + boolean debug = isDebugging || providerConfiguration.getMainCliOptions().contains( LOGGING_LEVEL_DEBUG ); + ForkNodeArguments args = new ForkedNodeArg( forkNumber, debug ); eventChannel = channelProcessorFactory.createEncoder( args ); MasterProcessChannelDecoder decoder = channelProcessorFactory.createDecoder( args ); @@ -138,7 +141,7 @@ public final class ForkedBooter ConsoleLogger logger = (ConsoleLogger) forkingReporterFactory.createReporter(); commandReader = new CommandReader( decoder, providerConfiguration.getShutdown(), logger ); - pingScheduler = isDebugging() ? null : listenToShutdownCommands( booterDeserializer.getPluginPid(), logger ); + pingScheduler = isDebugging ? null : listenToShutdownCommands( booterDeserializer.getPluginPid(), logger ); systemExitTimeoutInSeconds = providerConfiguration.systemExitTimeout( DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS ); diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedNodeArg.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedNodeArg.java index cc375b5..4bed5ca 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedNodeArg.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedNodeArg.java @@ -34,11 +34,13 @@ public final class ForkedNodeArg implements ForkNodeArguments { private final int forkChannelId; private final ConsoleLogger logger; + private final boolean isDebug; - public ForkedNodeArg( int forkChannelId ) + public ForkedNodeArg( int forkChannelId, boolean isDebug ) { this.forkChannelId = forkChannelId; logger = new NullConsoleLogger(); + this.isDebug = isDebug; } @Nonnull @@ -80,4 +82,16 @@ public final class ForkedNodeArg implements ForkNodeArguments { return logger; } + + @Override + public File getEventStreamBinaryFile() + { + return null; + } + + @Override + public File getCommandStreamBinaryFile() + { + return isDebug ? DumpErrorSingleton.getSingleton().getCommandStreamBinaryFile() : null; + } } diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/CommandDecoder.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/CommandDecoder.java index b7fc186..873136f 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/CommandDecoder.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/CommandDecoder.java @@ -29,8 +29,14 @@ import org.apache.maven.surefire.api.stream.MalformedChannelException; import org.apache.maven.surefire.api.stream.SegmentType; import javax.annotation.Nonnull; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.channels.ReadableByteChannel; +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; import static org.apache.maven.surefire.api.booter.Command.BYE_ACK; import static org.apache.maven.surefire.api.booter.Command.NOOP; @@ -43,6 +49,7 @@ import static org.apache.maven.surefire.api.booter.MasterProcessCommand.COMMAND_ import static org.apache.maven.surefire.api.report.RunMode.RUN_MODES; import static org.apache.maven.surefire.api.stream.SegmentType.DATA_STRING; import static org.apache.maven.surefire.api.stream.SegmentType.END_OF_FRAME; +import static org.apache.maven.surefire.api.stream.SegmentType.RUN_MODE; import static org.apache.maven.surefire.api.stream.SegmentType.STRING_ENCODING; /** @@ -56,6 +63,13 @@ public class CommandDecoder extends AbstractStreamDecoder<Command, MasterProcess END_OF_FRAME }; + private static final SegmentType[] COMMAND_WITH_RUNNABLE_STRING = new SegmentType[] { + RUN_MODE, + STRING_ENCODING, + DATA_STRING, + END_OF_FRAME + }; + private static final SegmentType[] COMMAND_WITH_ONE_STRING = new SegmentType[] { STRING_ENCODING, DATA_STRING, @@ -63,12 +77,14 @@ public class CommandDecoder extends AbstractStreamDecoder<Command, MasterProcess }; private final ForkNodeArguments arguments; + private final OutputStream debugSink; public CommandDecoder( @Nonnull ReadableByteChannel channel, @Nonnull ForkNodeArguments arguments ) { super( channel, arguments, COMMAND_TYPES ); this.arguments = arguments; + debugSink = newDebugSink(); } @Override @@ -160,6 +176,7 @@ public class CommandDecoder extends AbstractStreamDecoder<Command, MasterProcess case TEST_SET_FINISHED: return COMMAND_WITHOUT_DATA; case RUN_CLASS: + return COMMAND_WITH_RUNNABLE_STRING; case SHUTDOWN: return COMMAND_WITH_ONE_STRING; default: @@ -196,4 +213,52 @@ public class CommandDecoder extends AbstractStreamDecoder<Command, MasterProcess throw new IllegalArgumentException( "Missing a branch for the event type " + commandType ); } } + + @Override + protected void debugStream( byte[] array, int position, int remaining ) + { + if ( debugSink == null ) + { + return; + } + + try + { + debugSink.write( array, position, remaining ); + debugSink.flush(); + } + catch ( IOException e ) + { + // logger file was deleted + // System.out is already used by the stream in this decoder + } + } + + private OutputStream newDebugSink() + { + final File sink = arguments.getCommandStreamBinaryFile(); + if ( sink == null ) + { + return null; + } + + try + { + final OutputStream os = new FileOutputStream( sink, true ); + Runtime.getRuntime().addShutdownHook( new Thread( new FutureTask<>( new Callable<Void>() + { + @Override + public Void call() throws Exception + { + os.close(); + return null; + } + } ) ) ); + return os; + } + catch ( FileNotFoundException e ) + { + return null; + } + } } diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java index 12df54d..2cb7267 100644 --- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java +++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java @@ -96,7 +96,7 @@ public class CommandReaderTest InputStream realInputStream = new SystemInputStream(); addTestToPipeline( getClass().getName() ); ConsoleLogger logger = new NullConsoleLogger(); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); MasterProcessChannelDecoder decoder = new CommandChannelDecoder( newChannel( realInputStream ), args ); reader = new CommandReader( decoder, Shutdown.DEFAULT, logger ); @@ -259,6 +259,8 @@ public class CommandReaderTest .append( ":maven-surefire-command:" ) .append( (char) 13 ) .append( ":run-testclass:" ) + .append( (char) 10 ) + .append( ":normal-run:" ) .append( (char) 5 ) .append( ":UTF-8:" ) .append( (char) ( clsLength >> 24 ) ) diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java index cb6c057..0f3d8db 100644 --- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java +++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java @@ -294,7 +294,7 @@ public class ForkedBooterMockTest factory.connect( "pipe://3" ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); MasterProcessChannelDecoder decoder = factory.createDecoder( args ); assertThat( decoder ).isInstanceOf( CommandChannelDecoder.class ); MasterProcessChannelEncoder encoder = factory.createEncoder( args ); @@ -414,7 +414,7 @@ public class ForkedBooterMockTest } ); factory.connect( "tcp://localhost:" + serverPort ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); MasterProcessChannelDecoder decoder = factory.createDecoder( args ); assertThat( decoder ) .isInstanceOf( CommandChannelDecoder.class ); diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java index 33de107..f31d9d8 100644 --- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java +++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java @@ -19,6 +19,7 @@ package org.apache.maven.surefire.booter.spi; * under the License. */ +import org.apache.maven.plugin.surefire.log.api.ConsoleLogger; import org.apache.maven.surefire.api.booter.Command; import org.apache.maven.surefire.api.booter.DumpErrorSingleton; import org.apache.maven.surefire.api.booter.Shutdown; @@ -29,11 +30,13 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import javax.annotation.Nonnull; import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.concurrent.ConcurrentLinkedQueue; import static java.nio.channels.Channels.newChannel; import static java.nio.charset.StandardCharsets.UTF_8; @@ -54,6 +57,7 @@ import static org.junit.Assert.fail; /** * Tests for {@link CommandChannelDecoder}. */ +@SuppressWarnings( "checkstyle:magicnumber" ) public class CommandChannelDecoderTest { @Rule @@ -78,6 +82,8 @@ public class CommandChannelDecoderTest .append( ":maven-surefire-command:" ) .append( (char) 13 ) .append( ":run-testclass:" ) + .append( (char) 10 ) + .append( ":normal-run:" ) .append( (char) 5 ) .append( ":UTF-8:" ) .append( (char) 0 ) @@ -91,7 +97,7 @@ public class CommandChannelDecoderTest .getBytes( UTF_8 ); InputStream is = new ByteArrayInputStream( encoded ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); Command command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( RUN_CLASS ); @@ -107,7 +113,7 @@ public class CommandChannelDecoderTest byte[] encoded = ":maven-surefire-command:\u0010:testset-finished:".getBytes(); ByteArrayInputStream is = new ByteArrayInputStream( encoded ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( TEST_SET_FINISHED ); @@ -123,7 +129,7 @@ public class CommandChannelDecoderTest byte[] encoded = ":maven-surefire-command:\u0014:skip-since-next-test:".getBytes(); ByteArrayInputStream is = new ByteArrayInputStream( encoded ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( SKIP_SINCE_NEXT_TEST ); @@ -139,7 +145,7 @@ public class CommandChannelDecoderTest + shutdownType.getParam() + ":" ).getBytes(); ByteArrayInputStream is = new ByteArrayInputStream( encoded ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); Command command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( SHUTDOWN ); @@ -155,7 +161,7 @@ public class CommandChannelDecoderTest + shutdownType.getParam() + ":" ).getBytes(); ByteArrayInputStream is = new ByteArrayInputStream( encoded ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); Command command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( SHUTDOWN ); @@ -171,7 +177,7 @@ public class CommandChannelDecoderTest + shutdownType.getParam() + ":" ).getBytes(); ByteArrayInputStream is = new ByteArrayInputStream( encoded ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); Command command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( SHUTDOWN ); @@ -186,7 +192,7 @@ public class CommandChannelDecoderTest byte[] encoded = ":maven-surefire-command:\u0004:noop:".getBytes(); ByteArrayInputStream is = new ByteArrayInputStream( encoded ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); Command command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( NOOP ); @@ -202,7 +208,7 @@ public class CommandChannelDecoderTest byte[] streamContent = ( "<something>" + new String( encoded ) + "<damaged>" ).getBytes(); ByteArrayInputStream is = new ByteArrayInputStream( streamContent ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); Command command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( BYE_ACK ); @@ -218,7 +224,7 @@ public class CommandChannelDecoderTest byte[] streamContent = ( ":<damaged>:" + new String( encoded ) ).getBytes(); ByteArrayInputStream is = new ByteArrayInputStream( streamContent ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); Command command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( BYE_ACK ); @@ -233,7 +239,7 @@ public class CommandChannelDecoderTest byte[] encoded = ":maven-surefire-command:\u0007:bye-ack:".getBytes(); ByteArrayInputStream is = new ByteArrayInputStream( encoded ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); Command command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( BYE_ACK ); @@ -246,7 +252,7 @@ public class CommandChannelDecoderTest String cmd = ":maven-surefire-command:\u0007:bye-ack:\r\n:maven-surefire-command:\u0007:bye-ack:"; InputStream is = new ByteArrayInputStream( cmd.getBytes() ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); Command command = decoder.decode(); @@ -266,7 +272,7 @@ public class CommandChannelDecoderTest ByteArrayInputStream is = new ByteArrayInputStream( ":maven-surefire-command:".getBytes() ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); decoder.decode(); fail(); @@ -278,7 +284,7 @@ public class CommandChannelDecoderTest ByteArrayInputStream is = new ByteArrayInputStream( new byte[] {':', '\r'} ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); decoder.decode(); fail(); @@ -290,7 +296,7 @@ public class CommandChannelDecoderTest String cmd = ":maven-surefire-command:\u0007:bye-ack ::maven-surefire-command:"; InputStream is = new ByteArrayInputStream( cmd.getBytes() ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); decoder.decode(); @@ -302,11 +308,212 @@ public class CommandChannelDecoderTest String cmd = ":maven-surefire-command:\0007:bye-ack\r\n::maven-surefire-command:\u0004:noop:"; InputStream is = new ByteArrayInputStream( cmd.getBytes() ); DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName ); - ForkNodeArguments args = new ForkedNodeArg( 1 ); + ForkNodeArguments args = new ForkedNodeArg( 1, false ); CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args ); Command command = decoder.decode(); assertThat( command.getCommandType() ).isSameAs( NOOP ); assertNull( command.getData() ); } + + @Test + public void testBinaryCommandStream() throws Exception + { + InputStream commands = getClass().getResourceAsStream( "/binary-commands/75171711-encoder.bin" ); + ConsoleLoggerMock logger = new ConsoleLoggerMock( true, true, true, true ); + ForkNodeArguments args = new ForkNodeArgumentsMock( logger, new File( "" ) ); + CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( commands ), args ); + + Command command = decoder.decode(); + assertThat( command ).isNotNull(); + assertThat( command.getCommandType() ).isEqualTo( NOOP ); + assertThat( command.getData() ).isNull(); + + command = decoder.decode(); + assertThat( command ).isNotNull(); + assertThat( command.getCommandType() ).isEqualTo( RUN_CLASS ); + assertThat( command.getData() ).isEqualTo( "pkg.ATest" ); + + for ( int i = 0; i < 24; i++ ) + { + command = decoder.decode(); + assertThat( command ).isNotNull(); + assertThat( command.getCommandType() ).isEqualTo( NOOP ); + assertThat( command.getData() ).isNull(); + } + } + + /** + * Threadsafe impl. Mockito and Powermock are not thread-safe. + */ + private static class ForkNodeArgumentsMock implements ForkNodeArguments + { + private final ConcurrentLinkedQueue<String> dumpStreamText = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue<String> logWarningAtEnd = new ConcurrentLinkedQueue<>(); + private final ConsoleLogger logger; + private final File dumpStreamTextFile; + + ForkNodeArgumentsMock( ConsoleLogger logger, File dumpStreamTextFile ) + { + this.logger = logger; + this.dumpStreamTextFile = dumpStreamTextFile; + } + + @Nonnull + @Override + public String getSessionId() + { + throw new UnsupportedOperationException(); + } + + @Override + public int getForkChannelId() + { + return 0; + } + + @Nonnull + @Override + public File dumpStreamText( @Nonnull String text ) + { + dumpStreamText.add( text ); + return dumpStreamTextFile; + } + + @Nonnull + @Override + public File dumpStreamException( @Nonnull Throwable t ) + { + throw new UnsupportedOperationException(); + } + + @Override + public void logWarningAtEnd( @Nonnull String text ) + { + logWarningAtEnd.add( text ); + } + + @Nonnull + @Override + public ConsoleLogger getConsoleLogger() + { + return logger; + } + + boolean isCalled() + { + return !dumpStreamText.isEmpty() || !logWarningAtEnd.isEmpty(); + } + + @Override + public File getEventStreamBinaryFile() + { + return null; + } + + @Override + public File getCommandStreamBinaryFile() + { + return null; + } + } + + /** + * Threadsafe impl. Mockito and Powermock are not thread-safe. + */ + private static class ConsoleLoggerMock implements ConsoleLogger + { + final ConcurrentLinkedQueue<String> debug = new ConcurrentLinkedQueue<>(); + final ConcurrentLinkedQueue<String> info = new ConcurrentLinkedQueue<>(); + final ConcurrentLinkedQueue<String> error = new ConcurrentLinkedQueue<>(); + final boolean isDebug; + final boolean isInfo; + final boolean isWarning; + final boolean isError; + boolean called; + boolean isDebugEnabledCalled; + boolean isInfoEnabledCalled; + + ConsoleLoggerMock( boolean isDebug, boolean isInfo, boolean isWarning, boolean isError ) + { + this.isDebug = isDebug; + this.isInfo = isInfo; + this.isWarning = isWarning; + this.isError = isError; + } + + @Override + public boolean isDebugEnabled() + { + isDebugEnabledCalled = true; + called = true; + return isDebug; + } + + @Override + public void debug( String message ) + { + debug.add( message ); + called = true; + } + + @Override + public boolean isInfoEnabled() + { + isInfoEnabledCalled = true; + called = true; + return isInfo; + } + + @Override + public void info( String message ) + { + info.add( message ); + called = true; + } + + @Override + public boolean isWarnEnabled() + { + called = true; + return isWarning; + } + + @Override + public void warning( String message ) + { + called = true; + } + + @Override + public boolean isErrorEnabled() + { + called = true; + return isError; + } + + @Override + public void error( String message ) + { + error.add( message ); + called = true; + } + + @Override + public void error( String message, Throwable t ) + { + called = true; + } + + @Override + public void error( Throwable t ) + { + called = true; + } + + boolean isCalled() + { + return called; + } + } } diff --git a/surefire-booter/src/test/resources/binary-commands/75171711-encoder.bin b/surefire-booter/src/test/resources/binary-commands/75171711-encoder.bin new file mode 100644 index 0000000..bbc337b Binary files /dev/null and b/surefire-booter/src/test/resources/binary-commands/75171711-encoder.bin differ