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

Reply via email to