You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2021/01/16 02:08:14 UTC
[maven-surefire] 01/01: [SUREFIRE-1847] Remove Base64 in the
Encoder/Decoder and gain the performance for the communication flow: Plugin
to Fork
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 bd04a9ead77bb962c716ad7bb224b4eec2888169
Author: tibordigana <ti...@gmail.com>
AuthorDate: Wed Nov 25 08:59:01 2020 +0100
[SUREFIRE-1847] Remove Base64 in the Encoder/Decoder and gain the performance for the communication flow: Plugin to Fork
---
.../maven/plugin/surefire/SurefireHelper.java | 2 +
.../maven/plugin/surefire/SurefireProperties.java | 5 +
.../surefire/booterclient/BooterSerializer.java | 2 +
.../plugin/surefire/booterclient/ForkStarter.java | 22 +-
.../output/InPluginProcessDumpSingleton.java | 7 +
.../surefire/extensions/EventConsumerThread.java | 926 +----------------
.../surefire/extensions/LegacyForkChannel.java | 2 +-
.../surefire/extensions/LegacyForkNodeFactory.java | 2 +-
.../plugin/surefire/extensions/StreamFeeder.java | 123 +--
.../surefire/extensions/SurefireForkChannel.java | 2 +-
.../extensions/SurefireForkNodeFactory.java | 2 +-
.../maven/surefire/stream/CommandEncoder.java | 148 +++
.../apache/maven/surefire/stream/EventDecoder.java | 468 +++++++++
.../booterclient/ForkingRunListenerTest.java | 22 +-
.../plugin/surefire/booterclient/MainClass.java | 2 +-
.../TestLessInputStreamBuilderTest.java | 35 +-
.../TestProvidingInputStreamTest.java | 46 +-
.../booterclient/output/ForkClientTest.java | 19 +-
.../maven/plugin/surefire/extensions/E2ETest.java | 16 +-
.../extensions/EventConsumerThreadTest.java | 1060 +-------------------
.../extensions/ForkedProcessEventNotifierTest.java | 223 +---
.../surefire/extensions/StreamFeederTest.java | 39 +-
.../org/apache/maven/surefire/JUnit4SuiteTest.java | 2 +
.../maven/surefire/extensions/ForkChannelTest.java | 15 +-
.../maven/surefire/stream/EventDecoderTest.java | 785 +++++++++++++++
maven-surefire-plugin/src/site/fml/faq.fml | 4 +-
.../maven/surefire/api/booter/Constants.java | 6 +-
.../surefire/api/booter/DumpErrorSingleton.java | 27 +-
.../surefire/api/booter/ForkingRunListener.java | 2 +-
.../api/booter/MasterProcessChannelEncoder.java | 6 +-
.../surefire/api/booter/MasterProcessCommand.java | 54 +-
.../surefire/api/fork}/ForkNodeArguments.java | 6 +-
.../apache/maven/surefire/api/report/RunMode.java | 19 +
.../surefire/api/stream/AbstractStreamDecoder.java | 735 ++++++++++++++
.../surefire/api/stream/AbstractStreamEncoder.java | 191 ++++
.../MalformedChannelException.java} | 13 +-
.../Constants.java => stream/SegmentType.java} | 21 +-
.../surefire/api/util/internal/DumpFileUtils.java | 1 +
.../java/org/apache/maven/JUnit4SuiteTest.java | 6 +-
.../api/stream/AbstractStreamDecoderTest.java | 692 +++++++++++++
.../api/stream/AbstractStreamEncoderTest.java | 331 ++++++
.../maven/surefire/booter/BooterConstants.java | 2 +-
.../maven/surefire/booter/BooterDeserializer.java | 5 +
.../maven/surefire/booter/CommandReader.java | 4 +-
.../apache/maven/surefire/booter/ForkedBooter.java | 29 +-
.../maven/surefire/booter/ForkedNodeArg.java | 97 ++
.../maven/surefire/booter/PropertiesWrapper.java | 2 +-
.../surefire/booter/spi/CommandChannelDecoder.java | 86 ++
...hannelEncoder.java => EventChannelEncoder.java} | 268 ++---
.../spi/LegacyMasterProcessChannelDecoder.java | 190 ----
...LegacyMasterProcessChannelProcessorFactory.java | 12 +-
...refireMasterProcessChannelProcessorFactory.java | 14 +-
.../surefire/booter/stream/CommandDecoder.java | 276 +++++
.../maven/surefire/booter/stream/EventEncoder.java | 78 ++
.../maven/surefire/booter/CommandReaderTest.java | 44 +-
.../surefire/booter/ForkedBooterMockTest.java | 33 +-
.../maven/surefire/booter/JUnit4SuiteTest.java | 8 +-
.../booter/spi/CommandChannelDecoderTest.java | 519 ++++++++++
...coderTest.java => EventChannelEncoderTest.java} | 227 +----
.../spi/LegacyMasterProcessChannelDecoderTest.java | 243 -----
.../resources/binary-commands/75171711-encoder.bin | Bin 0 -> 851 bytes
.../maven/surefire/extensions/ForkChannel.java | 1 +
.../maven/surefire/extensions/ForkNodeFactory.java | 2 +
surefire-extensions-spi/pom.xml | 5 +
.../spi/MasterProcessChannelProcessorFactory.java | 10 +-
.../its/JUnitPlatformStreamCorruptionIT.java | 2 +-
.../plugin/surefire/log/api/PrintStreamLogger.java | 2 +-
67 files changed, 5061 insertions(+), 3187 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/SurefireProperties.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java
index a880c0d..cd2f91f 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java
@@ -201,6 +201,11 @@ public class SurefireProperties
}
}
+ public void setProperty( String key, int value )
+ {
+ setProperty( key, String.valueOf( value ) );
+ }
+
public void setProperty( String key, Long value )
{
if ( value != null )
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 1e00f16..1335a82 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
@@ -50,6 +50,7 @@ import static org.apache.maven.surefire.booter.BooterConstants.FAIL_FAST_COUNT;
import static org.apache.maven.surefire.booter.BooterConstants.FAILIFNOTESTS;
import static org.apache.maven.surefire.booter.BooterConstants.FORKTESTSET;
import static org.apache.maven.surefire.booter.BooterConstants.FORKTESTSET_PREFER_TESTS_FROM_IN_STREAM;
+import static org.apache.maven.surefire.booter.BooterConstants.FORK_NUMBER;
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;
@@ -168,6 +169,7 @@ class BooterSerializer
ReporterConfiguration reporterConfiguration = providerConfiguration.getReporterConfiguration();
boolean rep = reporterConfiguration.isTrimStackTrace();
File reportsDirectory = replaceForkThreadsInPath( reporterConfiguration.getReportsDirectory(), forkNumber );
+ properties.setProperty( FORK_NUMBER, forkNumber );
properties.setProperty( ISTRIMSTACKTRACE, rep );
properties.setProperty( REPORTSDIRECTORY, reportsDirectory );
ClassLoaderConfiguration classLoaderConfig = startupConfiguration.getClassLoaderConfiguration();
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 1ac3917..200d2a8 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
@@ -45,7 +45,7 @@ import org.apache.maven.surefire.extensions.CloseableDaemonThread;
import org.apache.maven.surefire.extensions.EventHandler;
import org.apache.maven.surefire.extensions.ForkChannel;
import org.apache.maven.surefire.extensions.ForkNodeFactory;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CommandlineExecutor;
import org.apache.maven.surefire.extensions.util.CommandlineStreams;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
@@ -574,7 +574,8 @@ public class ForkStarter
File dumpLogDir = replaceForkThreadsInPath( startupReportConfiguration.getReportsDirectory(), forkNumber );
try
{
- ForkNodeArguments forkNodeArguments = new ForkedNodeArg( forkNumber, dumpLogDir, randomUUID().toString() );
+ ForkNodeArguments forkNodeArguments =
+ new ForkedNodeArg( forkConfiguration.isDebug(), forkNumber, dumpLogDir, randomUUID().toString() );
forkChannel = forkNodeFactory.createForkChannel( forkNodeArguments );
closer.addCloseable( forkChannel );
tempDir = forkConfiguration.getTempDirectory().getCanonicalPath();
@@ -884,9 +885,11 @@ public class ForkStarter
private final int forkChannelId;
private final File dumpLogDir;
private final String sessionId;
+ private final boolean debug;
- ForkedNodeArg( int forkChannelId, File dumpLogDir, String sessionId )
+ ForkedNodeArg( boolean debug, int forkChannelId, File dumpLogDir, String sessionId )
{
+ this.debug = debug;
this.forkChannelId = forkChannelId;
this.dumpLogDir = dumpLogDir;
this.sessionId = sessionId;
@@ -932,5 +935,18 @@ public class ForkStarter
{
return log;
}
+
+ @Override
+ public File getEventStreamBinaryFile()
+ {
+ return debug ? InPluginProcessDumpSingleton.getSingleton()
+ .getEventStreamBinaryFile( dumpLogDir, forkChannelId ) : null;
+ }
+
+ @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..05c4235 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,12 @@ public final class InPluginProcessDumpSingleton
DumpFileUtils.dumpException( t, msg == null ? "null" : msg, dump );
}
+ public File getEventStreamBinaryFile( File reportsDirectory, int jvmRun )
+ {
+ reportsDirectory.mkdirs();
+ 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/main/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThread.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThread.java
index 8b855d8..1371dbb 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThread.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThread.java
@@ -19,142 +19,28 @@ package org.apache.maven.plugin.surefire.extensions;
* under the License.
*/
-import org.apache.maven.plugin.surefire.booterclient.output.DeserializedStacktraceWriter;
-import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
-import org.apache.maven.surefire.api.booter.ForkedProcessEventType;
-import org.apache.maven.surefire.api.event.ConsoleDebugEvent;
-import org.apache.maven.surefire.api.event.ConsoleErrorEvent;
-import org.apache.maven.surefire.api.event.ConsoleInfoEvent;
-import org.apache.maven.surefire.api.event.ConsoleWarningEvent;
-import org.apache.maven.surefire.api.event.ControlByeEvent;
-import org.apache.maven.surefire.api.event.ControlNextTestEvent;
-import org.apache.maven.surefire.api.event.ControlStopOnNextTestEvent;
import org.apache.maven.surefire.api.event.Event;
-import org.apache.maven.surefire.api.event.JvmExitErrorEvent;
-import org.apache.maven.surefire.api.event.StandardStreamErrEvent;
-import org.apache.maven.surefire.api.event.StandardStreamErrWithNewLineEvent;
-import org.apache.maven.surefire.api.event.StandardStreamOutEvent;
-import org.apache.maven.surefire.api.event.StandardStreamOutWithNewLineEvent;
-import org.apache.maven.surefire.api.event.SystemPropertyEvent;
-import org.apache.maven.surefire.api.event.TestAssumptionFailureEvent;
-import org.apache.maven.surefire.api.event.TestErrorEvent;
-import org.apache.maven.surefire.api.event.TestFailedEvent;
-import org.apache.maven.surefire.api.event.TestSkippedEvent;
-import org.apache.maven.surefire.api.event.TestStartingEvent;
-import org.apache.maven.surefire.api.event.TestSucceededEvent;
-import org.apache.maven.surefire.api.event.TestsetCompletedEvent;
-import org.apache.maven.surefire.api.event.TestsetStartingEvent;
-import org.apache.maven.surefire.api.report.RunMode;
-import org.apache.maven.surefire.api.report.StackTraceWriter;
-import org.apache.maven.surefire.api.report.TestSetReportEntry;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.Memento;
import org.apache.maven.surefire.extensions.CloseableDaemonThread;
import org.apache.maven.surefire.extensions.EventHandler;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
+import org.apache.maven.surefire.stream.EventDecoder;
-import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.io.EOFException;
-import java.io.File;
import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
import java.nio.channels.ReadableByteChannel;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CoderResult;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static java.lang.Math.max;
-import static java.lang.Math.min;
-import static java.nio.charset.CodingErrorAction.REPLACE;
-import static java.nio.charset.StandardCharsets.US_ASCII;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.StreamReadStatus.OVERFLOW;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.StreamReadStatus.UNDERFLOW;
-import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING;
-import static org.apache.maven.surefire.api.booter.Constants.MAGIC_NUMBER_BYTES;
-import static org.apache.maven.surefire.api.report.CategorizedReportEntry.reportEntry;
/**
*
*/
public class EventConsumerThread extends CloseableDaemonThread
{
- private static final String[] JVM_ERROR_PATTERNS =
- {
- "could not create the java virtual machine",
- "error occurred during initialization", // of VM, of boot layer
- "error:", // general errors
- "could not reserve enough space", "could not allocate", "unable to allocate", // memory errors
- "java.lang.module.findexception" // JPMS errors
- };
-
- private static final String PRINTABLE_JVM_NATIVE_STREAM = "Listening for transport dt_socket at address:";
-
- private static final SegmentType[] EVENT_WITHOUT_DATA = new SegmentType[] {
- SegmentType.END_OF_FRAME
- };
-
- private static final SegmentType[] EVENT_WITH_ERROR_TRACE = new SegmentType[] {
- SegmentType.STRING_ENCODING,
- SegmentType.DATA_STRING,
- SegmentType.DATA_STRING,
- SegmentType.DATA_STRING,
- SegmentType.END_OF_FRAME
- };
-
- private static final SegmentType[] EVENT_WITH_ONE_STRING = new SegmentType[] {
- SegmentType.STRING_ENCODING,
- SegmentType.DATA_STRING,
- SegmentType.END_OF_FRAME
- };
-
- private static final SegmentType[] EVENT_WITH_RUNMODE_AND_ONE_STRING = new SegmentType[] {
- SegmentType.RUN_MODE,
- SegmentType.STRING_ENCODING,
- SegmentType.DATA_STRING,
- SegmentType.END_OF_FRAME
- };
-
- private static final SegmentType[] EVENT_WITH_RUNMODE_AND_TWO_STRINGS = new SegmentType[] {
- SegmentType.RUN_MODE,
- SegmentType.STRING_ENCODING,
- SegmentType.DATA_STRING,
- SegmentType.DATA_STRING,
- SegmentType.END_OF_FRAME
- };
-
- private static final SegmentType[] EVENT_TEST_CONTROL = new SegmentType[] {
- SegmentType.RUN_MODE,
- SegmentType.STRING_ENCODING,
- SegmentType.DATA_STRING,
- SegmentType.DATA_STRING,
- SegmentType.DATA_STRING,
- SegmentType.DATA_STRING,
- SegmentType.DATA_STRING,
- SegmentType.DATA_STRING,
- SegmentType.DATA_INT,
- SegmentType.DATA_STRING,
- SegmentType.DATA_STRING,
- SegmentType.DATA_STRING,
- SegmentType.END_OF_FRAME
- };
-
- private static final int BUFFER_SIZE = 1024;
- private static final byte[] DEFAULT_STREAM_ENCODING_BYTES = DEFAULT_STREAM_ENCODING.name().getBytes( US_ASCII );
- private static final int DELIMITER_LENGTH = 1;
- private static final int BYTE_LENGTH = 1;
- private static final int INT_LENGTH = 4;
- private static final int NO_POSITION = -1;
-
private final ReadableByteChannel channel;
private final EventHandler<Event> eventHandler;
private final CountdownCloseable countdownCloseable;
+ private final EventDecoder decoder;
private final ForkNodeArguments arguments;
private volatile boolean disabled;
@@ -165,6 +51,7 @@ public class EventConsumerThread extends CloseableDaemonThread
@Nonnull ForkNodeArguments arguments )
{
super( threadName );
+ decoder = new EventDecoder( channel, arguments );
this.channel = channel;
this.eventHandler = eventHandler;
this.countdownCloseable = countdownCloseable;
@@ -175,809 +62,42 @@ public class EventConsumerThread extends CloseableDaemonThread
public void run()
{
try ( ReadableByteChannel stream = channel;
- CountdownCloseable c = countdownCloseable; )
- {
- decode();
- }
- catch ( IOException e )
- {
- // not needed
- }
- }
-
- @Override
- public void disable()
- {
- disabled = true;
- }
-
- @Override
- public void close() throws IOException
- {
- channel.close();
- }
-
- @SuppressWarnings( "checkstyle:innerassignment" )
- private void decode() throws IOException
- {
- Map<Segment, ForkedProcessEventType> eventTypes = mapEventTypes();
- Map<Segment, RunMode> runModes = mapRunModes();
- Memento memento = new Memento();
- memento.bb.limit( 0 );
-
- do
- {
- try
- {
- ForkedProcessEventType eventType = readEventType( eventTypes, memento );
- if ( eventType == null )
- {
- throw new MalformedFrameException( memento.line.positionByteBuffer, memento.bb.position() );
- }
- RunMode runMode = null;
- for ( SegmentType segmentType : nextSegmentType( eventType ) )
- {
- if ( segmentType == null )
- {
- break;
- }
-
- switch ( segmentType )
- {
- case RUN_MODE:
- runMode = runModes.get( readSegment( memento ) );
- break;
- case STRING_ENCODING:
- memento.setCharset( readCharset( memento ) );
- break;
- case DATA_STRING:
- memento.data.add( readString( memento ) );
- break;
- case DATA_INT:
- memento.data.add( readInteger( memento ) );
- break;
- case END_OF_FRAME:
- memento.line.positionByteBuffer = memento.bb.position();
- if ( !disabled )
- {
- eventHandler.handleEvent( toEvent( eventType, runMode, memento.data ) );
- }
- break;
- default:
- memento.line.positionByteBuffer = NO_POSITION;
- arguments.dumpStreamText( "Unknown enum ("
- + ForkedProcessEventType.class.getSimpleName()
- + ") "
- + segmentType );
- }
- }
- }
- catch ( MalformedFrameException e )
- {
- if ( e.hasValidPositions() )
- {
- int length = e.readTo - e.readFrom;
- memento.line.write( memento.bb, e.readFrom, length );
- }
- }
- catch ( RuntimeException e )
- {
- arguments.dumpStreamException( e );
- }
- catch ( IOException e )
- {
- printRemainingStream( memento );
- throw e;
- }
- finally
- {
- memento.reset();
- }
- }
- while ( true );
- }
-
- protected ForkedProcessEventType readEventType( Map<Segment, ForkedProcessEventType> eventTypes, Memento memento )
- throws IOException, MalformedFrameException
- {
- int readCount = DELIMITER_LENGTH + MAGIC_NUMBER_BYTES.length + DELIMITER_LENGTH
- + BYTE_LENGTH + DELIMITER_LENGTH;
- read( memento, readCount );
- checkHeader( memento );
- return eventTypes.get( readSegment( memento ) );
- }
-
- protected String readString( Memento memento ) throws IOException, MalformedFrameException
- {
- memento.cb.clear();
- int readCount = readInt( memento );
- read( memento, readCount + DELIMITER_LENGTH );
-
- final String string;
- if ( readCount == 0 )
- {
- string = "";
- }
- else if ( readCount == 1 )
- {
- read( memento, 1 );
- byte oneChar = memento.bb.get();
- string = oneChar == 0 ? null : String.valueOf( (char) oneChar );
- }
- else
- {
- string = readString( memento, readCount );
- }
-
- checkDelimiter( memento );
- return string;
- }
-
- @Nonnull
- @SuppressWarnings( "checkstyle:magicnumber" )
- protected Segment readSegment( Memento memento ) throws IOException, MalformedFrameException
- {
- int readCount = readByte( memento ) & 0xff;
- read( memento, readCount + DELIMITER_LENGTH );
- ByteBuffer bb = memento.bb;
- Segment segment = new Segment( bb.array(), bb.arrayOffset() + bb.position(), readCount );
- bb.position( bb.position() + readCount );
- checkDelimiter( memento );
- return segment;
- }
-
- @Nonnull
- @SuppressWarnings( "checkstyle:magicnumber" )
- protected Charset readCharset( Memento memento ) throws IOException, MalformedFrameException
- {
- int length = readByte( memento ) & 0xff;
- read( memento, length + DELIMITER_LENGTH );
- ByteBuffer bb = memento.bb;
- byte[] array = bb.array();
- int offset = bb.arrayOffset() + bb.position();
- bb.position( bb.position() + length );
- boolean isDefaultEncoding = false;
- if ( length == DEFAULT_STREAM_ENCODING_BYTES.length )
- {
- isDefaultEncoding = true;
- for ( int i = 0; i < length; i++ )
- {
- isDefaultEncoding &= DEFAULT_STREAM_ENCODING_BYTES[i] == array[offset + i];
- }
- }
-
- try
- {
- Charset charset =
- isDefaultEncoding
- ? DEFAULT_STREAM_ENCODING
- : Charset.forName( new String( array, offset, length, US_ASCII ) );
-
- checkDelimiter( memento );
- return charset;
- }
- catch ( IllegalArgumentException e )
- {
- throw new MalformedFrameException( memento.line.positionByteBuffer, bb.position() );
- }
- }
-
- private static void checkHeader( Memento memento ) throws MalformedFrameException
- {
- ByteBuffer bb = memento.bb;
-
- checkDelimiter( memento );
-
- int shift = 0;
- try
- {
- for ( int start = bb.arrayOffset() + bb.position(), length = MAGIC_NUMBER_BYTES.length;
- shift < length; shift++ )
- {
- if ( bb.array()[shift + start] != MAGIC_NUMBER_BYTES[shift] )
- {
- throw new MalformedFrameException( memento.line.positionByteBuffer, bb.position() + shift );
- }
- }
- }
- finally
- {
- bb.position( bb.position() + shift );
- }
-
- checkDelimiter( memento );
- }
-
- @SuppressWarnings( "checkstyle:magicnumber" )
- private static void checkDelimiter( Memento memento ) throws MalformedFrameException
- {
- ByteBuffer bb = memento.bb;
- if ( ( 0xff & bb.get() ) != ':' )
- {
- throw new MalformedFrameException( memento.line.positionByteBuffer, bb.position() );
- }
- }
-
- static SegmentType[] nextSegmentType( ForkedProcessEventType eventType )
- {
- switch ( eventType )
- {
- case BOOTERCODE_BYE:
- case BOOTERCODE_STOP_ON_NEXT_TEST:
- case BOOTERCODE_NEXT_TEST:
- return EVENT_WITHOUT_DATA;
- case BOOTERCODE_CONSOLE_ERROR:
- case BOOTERCODE_JVM_EXIT_ERROR:
- return EVENT_WITH_ERROR_TRACE;
- case BOOTERCODE_CONSOLE_INFO:
- case BOOTERCODE_CONSOLE_DEBUG:
- case BOOTERCODE_CONSOLE_WARNING:
- return EVENT_WITH_ONE_STRING;
- case BOOTERCODE_STDOUT:
- case BOOTERCODE_STDOUT_NEW_LINE:
- case BOOTERCODE_STDERR:
- case BOOTERCODE_STDERR_NEW_LINE:
- return EVENT_WITH_RUNMODE_AND_ONE_STRING;
- case BOOTERCODE_SYSPROPS:
- return EVENT_WITH_RUNMODE_AND_TWO_STRINGS;
- case BOOTERCODE_TESTSET_STARTING:
- case BOOTERCODE_TESTSET_COMPLETED:
- case BOOTERCODE_TEST_STARTING:
- case BOOTERCODE_TEST_SUCCEEDED:
- case BOOTERCODE_TEST_FAILED:
- case BOOTERCODE_TEST_SKIPPED:
- case BOOTERCODE_TEST_ERROR:
- case BOOTERCODE_TEST_ASSUMPTIONFAILURE:
- return EVENT_TEST_CONTROL;
- default:
- throw new IllegalArgumentException( "Unknown enum " + eventType );
- }
- }
-
- protected StreamReadStatus read( Memento memento, int recommendedCount ) throws IOException
- {
- ByteBuffer buffer = memento.bb;
- if ( buffer.remaining() >= recommendedCount && buffer.position() != 0 )
- {
- return OVERFLOW;
- }
- else
- {
- if ( buffer.position() != 0 && recommendedCount > buffer.capacity() - buffer.position() )
- {
- buffer.compact().flip();
- memento.line.positionByteBuffer = 0;
- }
- int mark = buffer.position();
- buffer.position( buffer.limit() );
- buffer.limit( buffer.capacity() );
- boolean isEnd = false;
- while ( !isEnd && buffer.position() - mark < recommendedCount && buffer.position() != buffer.limit() )
- {
- isEnd = channel.read( buffer ) == -1;
- }
-
- buffer.limit( buffer.position() );
- buffer.position( mark );
- int readBytes = buffer.remaining();
-
- if ( isEnd && readBytes < recommendedCount )
- {
- throw new EOFException();
- }
- else
- {
- return readBytes >= recommendedCount ? OVERFLOW : UNDERFLOW;
- }
- }
- }
-
- static Event toEvent( ForkedProcessEventType eventType, RunMode runMode, List<Object> args )
- {
- switch ( eventType )
- {
- case BOOTERCODE_BYE:
- return new ControlByeEvent();
- case BOOTERCODE_STOP_ON_NEXT_TEST:
- return new ControlStopOnNextTestEvent();
- case BOOTERCODE_NEXT_TEST:
- return new ControlNextTestEvent();
- case BOOTERCODE_CONSOLE_ERROR:
- return new ConsoleErrorEvent( toStackTraceWriter( args ) );
- case BOOTERCODE_JVM_EXIT_ERROR:
- return new JvmExitErrorEvent( toStackTraceWriter( args ) );
- case BOOTERCODE_CONSOLE_INFO:
- return new ConsoleInfoEvent( (String) args.get( 0 ) );
- case BOOTERCODE_CONSOLE_DEBUG:
- return new ConsoleDebugEvent( (String) args.get( 0 ) );
- case BOOTERCODE_CONSOLE_WARNING:
- return new ConsoleWarningEvent( (String) args.get( 0 ) );
- case BOOTERCODE_STDOUT:
- return new StandardStreamOutEvent( runMode, (String) args.get( 0 ) );
- case BOOTERCODE_STDOUT_NEW_LINE:
- return new StandardStreamOutWithNewLineEvent( runMode, (String) args.get( 0 ) );
- case BOOTERCODE_STDERR:
- return new StandardStreamErrEvent( runMode, (String) args.get( 0 ) );
- case BOOTERCODE_STDERR_NEW_LINE:
- return new StandardStreamErrWithNewLineEvent( runMode, (String) args.get( 0 ) );
- case BOOTERCODE_SYSPROPS:
- String key = (String) args.get( 0 );
- String value = (String) args.get( 1 );
- return new SystemPropertyEvent( runMode, key, value );
- case BOOTERCODE_TESTSET_STARTING:
- return new TestsetStartingEvent( runMode, toReportEntry( args ) );
- case BOOTERCODE_TESTSET_COMPLETED:
- return new TestsetCompletedEvent( runMode, toReportEntry( args ) );
- case BOOTERCODE_TEST_STARTING:
- return new TestStartingEvent( runMode, toReportEntry( args ) );
- case BOOTERCODE_TEST_SUCCEEDED:
- return new TestSucceededEvent( runMode, toReportEntry( args ) );
- case BOOTERCODE_TEST_FAILED:
- return new TestFailedEvent( runMode, toReportEntry( args ) );
- case BOOTERCODE_TEST_SKIPPED:
- return new TestSkippedEvent( runMode, toReportEntry( args ) );
- case BOOTERCODE_TEST_ERROR:
- return new TestErrorEvent( runMode, toReportEntry( args ) );
- case BOOTERCODE_TEST_ASSUMPTIONFAILURE:
- return new TestAssumptionFailureEvent( runMode, toReportEntry( args ) );
- default:
- throw new IllegalArgumentException( "Missing a branch for the event type " + eventType );
- }
- }
-
- private static void printCorruptedStream( Memento memento )
- {
- ByteBuffer bb = memento.bb;
- if ( bb.hasRemaining() )
- {
- int bytesToWrite = bb.remaining();
- memento.line.write( bb, bb.position(), bytesToWrite );
- bb.position( bb.position() + bytesToWrite );
- }
- }
-
- /**
- * Print the last string which has not been finished by a new line character.
- *
- * @param memento current memento object
- */
- private static void printRemainingStream( Memento memento )
- {
- printCorruptedStream( memento );
- memento.line.printExistingLine();
- memento.line.count = 0;
- }
-
- @Nonnull
- private static TestSetReportEntry toReportEntry( List<Object> args )
- {
- // ReportEntry:
- String source = (String) args.get( 0 );
- String sourceText = (String) args.get( 1 );
- String name = (String) args.get( 2 );
- String nameText = (String) args.get( 3 );
- String group = (String) args.get( 4 );
- String message = (String) args.get( 5 );
- Integer timeElapsed = (Integer) args.get( 6 );
- // StackTraceWriter:
- String traceMessage = (String) args.get( 7 );
- String smartTrimmedStackTrace = (String) args.get( 8 );
- String stackTrace = (String) args.get( 9 );
- return newReportEntry( source, sourceText, name, nameText, group, message, timeElapsed,
- traceMessage, smartTrimmedStackTrace, stackTrace );
- }
-
- private static StackTraceWriter toStackTraceWriter( List<Object> args )
- {
- String traceMessage = (String) args.get( 0 );
- String smartTrimmedStackTrace = (String) args.get( 1 );
- String stackTrace = (String) args.get( 2 );
- return toTrace( traceMessage, smartTrimmedStackTrace, stackTrace );
- }
-
- private static StackTraceWriter toTrace( String traceMessage, String smartTrimmedStackTrace, String stackTrace )
- {
- boolean exists = traceMessage != null || stackTrace != null || smartTrimmedStackTrace != null;
- return exists ? new DeserializedStacktraceWriter( traceMessage, smartTrimmedStackTrace, stackTrace ) : null;
- }
-
- static TestSetReportEntry newReportEntry( // ReportEntry:
- String source, String sourceText, String name,
- String nameText, String group, String message,
- Integer timeElapsed,
- // StackTraceWriter:
- String traceMessage,
- String smartTrimmedStackTrace, String stackTrace )
- throws NumberFormatException
- {
- StackTraceWriter stackTraceWriter = toTrace( traceMessage, smartTrimmedStackTrace, stackTrace );
- return reportEntry( source, sourceText, name, nameText, group, stackTraceWriter, timeElapsed, message,
- Collections.<String, String>emptyMap() );
- }
-
- private static boolean isJvmError( String line )
- {
- String lineLower = line.toLowerCase();
- for ( String errorPattern : JVM_ERROR_PATTERNS )
+ CountdownCloseable c = countdownCloseable;
+ EventDecoder eventDecoder = decoder )
{
- if ( lineLower.contains( errorPattern ) )
- {
- return true;
- }
- }
- return false;
- }
-
- private static int decodeString( @Nonnull CharsetDecoder decoder, @Nonnull ByteBuffer input,
- @Nonnull CharBuffer output, @Nonnegative int bytesToDecode,
- boolean endOfInput, @Nonnegative int errorStreamFrom )
- throws MalformedFrameException
- {
- int limit = input.limit();
- input.limit( input.position() + bytesToDecode );
-
- CoderResult result = decoder.decode( input, output, endOfInput );
- if ( result.isError() || result.isMalformed() )
- {
- throw new MalformedFrameException( errorStreamFrom, input.position() );
- }
-
- int decodedBytes = bytesToDecode - input.remaining();
- input.limit( limit );
- return decodedBytes;
- }
-
- String readString( @Nonnull final Memento memento, @Nonnegative final int totalBytes )
- throws IOException, MalformedFrameException
- {
- memento.getDecoder().reset();
- final CharBuffer output = memento.cb;
- output.clear();
- final ByteBuffer input = memento.bb;
- final List<String> strings = new ArrayList<>();
- int countDecodedBytes = 0;
- for ( boolean endOfInput = false; !endOfInput; )
- {
- final int bytesToRead = totalBytes - countDecodedBytes;
- read( memento, bytesToRead - input.remaining() );
- int bytesToDecode = min( input.remaining(), bytesToRead );
- final boolean isLastChunk = bytesToDecode == bytesToRead;
- endOfInput = countDecodedBytes + bytesToDecode >= totalBytes;
+ Memento memento = eventDecoder.new Memento();
do
{
- boolean endOfChunk = output.remaining() >= bytesToRead;
- boolean endOfOutput = isLastChunk && endOfChunk;
- int readInputBytes = decodeString( memento.getDecoder(), input, output, bytesToDecode, endOfOutput,
- memento.line.positionByteBuffer );
- bytesToDecode -= readInputBytes;
- countDecodedBytes += readInputBytes;
- }
- while ( isLastChunk && bytesToDecode > 0 && output.hasRemaining() );
-
- if ( isLastChunk || !output.hasRemaining() )
- {
- strings.add( output.flip().toString() );
- output.clear();
- }
- }
-
- memento.getDecoder().reset();
- output.clear();
-
- return toString( strings );
- }
-
- protected byte readByte( Memento memento ) throws IOException, MalformedFrameException
- {
- read( memento, BYTE_LENGTH + DELIMITER_LENGTH );
- byte b = memento.bb.get();
- checkDelimiter( memento );
- return b;
- }
-
- protected int readInt( Memento memento ) throws IOException, MalformedFrameException
- {
- read( memento, INT_LENGTH + DELIMITER_LENGTH );
- int i = memento.bb.getInt();
- checkDelimiter( memento );
- return i;
- }
-
- protected Integer readInteger( Memento memento ) throws IOException, MalformedFrameException
- {
- read( memento, BYTE_LENGTH );
- boolean isNullObject = memento.bb.get() == 0;
- if ( isNullObject )
- {
- read( memento, DELIMITER_LENGTH );
- checkDelimiter( memento );
- return null;
- }
- return readInt( memento );
- }
-
- private static String toString( List<String> strings )
- {
- if ( strings.size() == 1 )
- {
- return strings.get( 0 );
- }
- StringBuilder concatenated = new StringBuilder( strings.size() * BUFFER_SIZE );
- for ( String s : strings )
- {
- concatenated.append( s );
- }
- return concatenated.toString();
- }
-
- static Map<Segment, ForkedProcessEventType> mapEventTypes()
- {
- Map<Segment, ForkedProcessEventType> map = new HashMap<>();
- for ( ForkedProcessEventType e : ForkedProcessEventType.values() )
- {
- byte[] array = e.getOpcode().getBytes( US_ASCII );
- map.put( new Segment( array, 0, array.length ), e );
- }
- return map;
- }
-
- static Map<Segment, RunMode> mapRunModes()
- {
- Map<Segment, RunMode> map = new HashMap<>();
- for ( RunMode e : RunMode.values() )
- {
- byte[] array = e.geRunmode().getBytes( US_ASCII );
- map.put( new Segment( array, 0, array.length ), e );
- }
- return map;
- }
-
- enum StreamReadStatus
- {
- UNDERFLOW,
- OVERFLOW,
- EOF
- }
-
- enum SegmentType
- {
- RUN_MODE,
- STRING_ENCODING,
- DATA_STRING,
- DATA_INT,
- END_OF_FRAME
- }
-
- /**
- * This class avoids locking which gains the performance of this decoder.
- */
- private class BufferedStream
- {
- private byte[] buffer;
- private int count;
- private int positionByteBuffer;
- private boolean isNewLine;
-
- BufferedStream( int capacity )
- {
- this.buffer = new byte[capacity];
- }
-
- void write( ByteBuffer bb, int position, int length )
- {
- ensureCapacity( length );
- byte[] array = bb.array();
- int pos = bb.arrayOffset() + position;
- while ( length-- > 0 )
- {
- positionByteBuffer++;
- byte b = array[pos++];
- if ( b == '\r' || b == '\n' )
+ Event event = eventDecoder.decode( memento );
+ if ( event != null && !disabled )
{
- if ( !isNewLine )
- {
- printExistingLine();
- count = 0;
- }
- isNewLine = true;
- }
- else
- {
- buffer[count++] = b;
- isNewLine = false;
+ eventHandler.handleEvent( event );
}
}
+ while ( true );
}
-
- private boolean isEmpty()
+ catch ( EOFException e )
{
- return count == 0;
- }
-
- @Override
- public String toString()
- {
- return new String( buffer, 0, count, DEFAULT_STREAM_ENCODING );
- }
-
- private void ensureCapacity( int addCapacity )
- {
- int oldCapacity = buffer.length;
- int exactCapacity = count + addCapacity;
- if ( exactCapacity < 0 )
- {
- throw new OutOfMemoryError();
- }
-
- if ( oldCapacity < exactCapacity )
- {
- int newCapacity = oldCapacity << 1;
- buffer = Arrays.copyOf( buffer, max( newCapacity, exactCapacity ) );
- }
+ //
}
-
- void printExistingLine()
- {
- if ( isEmpty() )
- {
- return;
- }
-
- String s = toString();
- if ( s == null )
- {
- return;
- }
-
- ConsoleLogger logger = arguments.getConsoleLogger();
- if ( s.contains( PRINTABLE_JVM_NATIVE_STREAM ) )
- {
- if ( logger.isDebugEnabled() )
- {
- logger.debug( s );
- }
- else if ( logger.isInfoEnabled() )
- {
- logger.info( s );
- }
- else
- {
- // In case of debugging forked JVM, see PRINTABLE_JVM_NATIVE_STREAM.
- System.out.println( s );
- }
- }
- else
- {
- if ( isJvmError( s ) )
- {
- logger.error( s );
- }
- else if ( logger.isDebugEnabled() )
- {
- logger.debug( s );
- }
-
- String msg = "Corrupted STDOUT by directly writing to native stream in forked JVM "
- + arguments.getForkChannelId() + ".";
- File dumpFile = arguments.dumpStreamText( msg + " Stream '" + s + "'." );
- arguments.logWarningAtEnd( msg + " See FAQ web page and the dump file " + dumpFile.getAbsolutePath() );
- }
- }
- }
-
- class Memento
- {
- private final CharsetDecoder defaultDecoder;
- private CharsetDecoder currentDecoder;
- final BufferedStream line = new BufferedStream( 32 );
- final List<Object> data = new ArrayList<>();
- final CharBuffer cb = CharBuffer.allocate( BUFFER_SIZE );
- final ByteBuffer bb = ByteBuffer.allocate( BUFFER_SIZE );
-
- Memento()
- {
- defaultDecoder = DEFAULT_STREAM_ENCODING.newDecoder()
- .onMalformedInput( REPLACE )
- .onUnmappableCharacter( REPLACE );
- }
-
- void reset()
- {
- currentDecoder = null;
- data.clear();
- }
-
- CharsetDecoder getDecoder()
- {
- return currentDecoder == null ? defaultDecoder : currentDecoder;
- }
-
- void setCharset( Charset charset )
+ catch ( IOException e )
{
- if ( charset.name().equals( defaultDecoder.charset().name() ) )
+ if ( !( e.getCause() instanceof InterruptedException ) )
{
- currentDecoder = defaultDecoder;
- }
- else
- {
- currentDecoder = charset.newDecoder()
- .onMalformedInput( REPLACE )
- .onUnmappableCharacter( REPLACE );
+ arguments.dumpStreamException( e );
}
}
}
- static class Segment
+ @Override
+ public void disable()
{
- private final byte[] array;
- private final int fromIndex;
- private final int length;
- private final int hashCode;
-
- Segment( byte[] array, int fromIndex, int length )
- {
- this.array = array;
- this.fromIndex = fromIndex;
- this.length = length;
-
- int hashCode = 0;
- int i = fromIndex;
- for ( int loops = length >> 1; loops-- != 0; )
- {
- hashCode = 31 * hashCode + array[i++];
- hashCode = 31 * hashCode + array[i++];
- }
- this.hashCode = i == fromIndex + length ? hashCode : 31 * hashCode + array[i];
- }
-
- @Override
- public int hashCode()
- {
- return hashCode;
- }
-
- @Override
- public boolean equals( Object obj )
- {
- if ( !( obj instanceof Segment ) )
- {
- return false;
- }
-
- Segment that = (Segment) obj;
- if ( that.length != length )
- {
- return false;
- }
-
- for ( int i = 0; i < length; i++ )
- {
- if ( that.array[that.fromIndex + i] != array[fromIndex + i] )
- {
- return false;
- }
- }
- return true;
- }
+ disabled = true;
}
- /**
- *
- */
- static class MalformedFrameException extends Exception
+ @Override
+ public void close() throws IOException
{
- private final int readFrom;
- private final int readTo;
-
- MalformedFrameException( int readFrom, int readTo )
- {
- this.readFrom = readFrom;
- this.readTo = readTo;
- }
-
- boolean hasValidPositions()
- {
- return readFrom != NO_POSITION && readTo != NO_POSITION && readTo - readFrom > 0;
- }
+ channel.close();
}
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
index 2400f94..e18f77d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
@@ -24,7 +24,7 @@ import org.apache.maven.surefire.extensions.CloseableDaemonThread;
import org.apache.maven.surefire.extensions.CommandReader;
import org.apache.maven.surefire.extensions.EventHandler;
import org.apache.maven.surefire.extensions.ForkChannel;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
import javax.annotation.Nonnull;
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java
index 29d79af..b4d4788 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java
@@ -20,7 +20,7 @@ package org.apache.maven.plugin.surefire.extensions;
*/
import org.apache.maven.surefire.extensions.ForkChannel;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.extensions.ForkNodeFactory;
import javax.annotation.Nonnull;
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StreamFeeder.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StreamFeeder.java
index 3269967..0dcf5ef 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StreamFeeder.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StreamFeeder.java
@@ -21,28 +21,15 @@ package org.apache.maven.plugin.surefire.extensions;
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.surefire.api.booter.Command;
-import org.apache.maven.surefire.api.booter.MasterProcessCommand;
import org.apache.maven.surefire.extensions.CloseableDaemonThread;
import org.apache.maven.surefire.extensions.CommandReader;
-import org.apache.maven.surefire.api.util.internal.ImmutableMap;
+import org.apache.maven.surefire.stream.CommandEncoder;
import javax.annotation.Nonnull;
import java.io.IOException;
-import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.WritableByteChannel;
-import java.util.HashMap;
-import java.util.Map;
-
-import static java.nio.charset.StandardCharsets.US_ASCII;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.BYE_ACK;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.MAGIC_NUMBER;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.NOOP;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.RUN_CLASS;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.SHUTDOWN;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.TEST_SET_FINISHED;
/**
* Commands which are sent from plugin to the forked jvm.
@@ -59,8 +46,6 @@ import static org.apache.maven.surefire.api.booter.MasterProcessCommand.TEST_SET
*/
public class StreamFeeder extends CloseableDaemonThread
{
- private static final Map<MasterProcessCommand, String> COMMAND_OPCODES = opcodesToStrings();
-
private final WritableByteChannel channel;
private final CommandReader commandReader;
private final ConsoleLogger logger;
@@ -81,15 +66,35 @@ public class StreamFeeder extends CloseableDaemonThread
@SuppressWarnings( "checkstyle:innerassignment" )
public void run()
{
- try ( WritableByteChannel c = channel )
+ try ( CommandEncoder encoder = new CommandEncoder( channel ) )
{
for ( Command cmd; ( cmd = commandReader.readNextCommand() ) != null; )
{
if ( !disabled )
{
- MasterProcessCommand cmdType = cmd.getCommandType();
- byte[] data = cmdType.hasDataType() ? encode( cmdType, cmd.getData() ) : encode( cmdType );
- c.write( ByteBuffer.wrap( data ) );
+ switch ( cmd.getCommandType() )
+ {
+ case RUN_CLASS:
+ encoder.sendRunClass( cmd.getData() );
+ break;
+ case TEST_SET_FINISHED:
+ encoder.sendTestSetFinished();
+ break;
+ case SKIP_SINCE_NEXT_TEST:
+ encoder.sendSkipSinceNextTest();
+ break;
+ case SHUTDOWN:
+ encoder.sendShutdown( cmd.getData() );
+ break;
+ case NOOP:
+ encoder.sendNoop();
+ break;
+ case BYE_ACK:
+ encoder.sendByeAck();
+ break;
+ default:
+ logger.error( "Unknown enum " + cmd.getCommandType().name() );
+ }
}
}
}
@@ -122,82 +127,4 @@ public class StreamFeeder extends CloseableDaemonThread
{
channel.close();
}
-
- /**
- * Public method for testing purposes.
- *
- * @param cmdType command type
- * @param data data to encode
- * @return command with data encoded to bytes
- */
- public static byte[] encode( MasterProcessCommand cmdType, String data )
- {
- if ( !cmdType.hasDataType() )
- {
- throw new IllegalArgumentException( "cannot use data without data type" );
- }
-
- if ( cmdType.getDataType() != String.class )
- {
- throw new IllegalArgumentException( "Data type can be only " + String.class );
- }
-
- return encode( COMMAND_OPCODES.get( cmdType ), data )
- .toString()
- .getBytes( US_ASCII );
- }
-
- /**
- * Public method for testing purposes.
- *
- * @param cmdType command type
- * @return command without data encoded to bytes
- */
- public static byte[] encode( MasterProcessCommand cmdType )
- {
- if ( cmdType.getDataType() != Void.class )
- {
- throw new IllegalArgumentException( "Data type can be only " + cmdType.getDataType() );
- }
-
- return encode( COMMAND_OPCODES.get( cmdType ), null )
- .toString()
- .getBytes( US_ASCII );
- }
-
- /**
- * Encodes opcode and data.
- *
- * @param operation opcode
- * @param data data
- * @return encoded command
- */
- private static StringBuilder encode( String operation, String data )
- {
- StringBuilder s = new StringBuilder( 128 )
- .append( ':' )
- .append( MAGIC_NUMBER )
- .append( ':' )
- .append( operation );
-
- if ( data != null )
- {
- s.append( ':' )
- .append( data );
- }
-
- return s.append( ':' );
- }
-
- private static Map<MasterProcessCommand, String> opcodesToStrings()
- {
- Map<MasterProcessCommand, String> opcodes = new HashMap<>();
- opcodes.put( RUN_CLASS, "run-testclass" );
- opcodes.put( TEST_SET_FINISHED, "testset-finished" );
- opcodes.put( SKIP_SINCE_NEXT_TEST, "skip-since-next-test" );
- opcodes.put( SHUTDOWN, "shutdown" );
- opcodes.put( NOOP, "noop" );
- opcodes.put( BYE_ACK, "bye-ack" );
- return new ImmutableMap<>( opcodes );
- }
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
index 639944e..5c3381c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
@@ -25,7 +25,7 @@ import org.apache.maven.surefire.extensions.CloseableDaemonThread;
import org.apache.maven.surefire.extensions.CommandReader;
import org.apache.maven.surefire.extensions.EventHandler;
import org.apache.maven.surefire.extensions.ForkChannel;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
import org.apache.maven.surefire.extensions.util.LineConsumerThread;
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java
index 0b47784..f9c6d71 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java
@@ -20,7 +20,7 @@ package org.apache.maven.plugin.surefire.extensions;
*/
import org.apache.maven.surefire.extensions.ForkChannel;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.extensions.ForkNodeFactory;
import javax.annotation.Nonnull;
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/surefire/stream/CommandEncoder.java b/maven-surefire-common/src/main/java/org/apache/maven/surefire/stream/CommandEncoder.java
new file mode 100644
index 0000000..81f9d77
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/surefire/stream/CommandEncoder.java
@@ -0,0 +1,148 @@
+package org.apache.maven.surefire.stream;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.api.booter.MasterProcessCommand;
+import org.apache.maven.surefire.api.stream.AbstractStreamEncoder;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING;
+import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING_BYTES;
+import static org.apache.maven.surefire.api.booter.Constants.MAGIC_NUMBER_FOR_COMMANDS_BYTES;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.BYE_ACK;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.NOOP;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.RUN_CLASS;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.SHUTDOWN;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.TEST_SET_FINISHED;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
+
+/**
+ *
+ */
+public class CommandEncoder extends AbstractStreamEncoder<MasterProcessCommand> implements AutoCloseable
+{
+ private final WritableByteChannel out;
+
+ public CommandEncoder( WritableByteChannel out )
+ {
+ super( out );
+ this.out = out;
+ }
+
+ public void sendRunClass( String testClassName ) throws IOException
+ {
+ CharsetEncoder encoder = newCharsetEncoder();
+ int bufferMaxLength =
+ estimateBufferLength( RUN_CLASS.getOpcodeLength(), NORMAL_RUN, encoder, 0, testClassName );
+ ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
+ encode( encoder, result, RUN_CLASS, NORMAL_RUN, testClassName );
+ write( result, true );
+ }
+
+ public void sendTestSetFinished() throws IOException
+ {
+ int bufferMaxLength = estimateBufferLength( TEST_SET_FINISHED.getOpcodeLength(), null, null, 0 );
+ ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
+ encodeHeader( result, TEST_SET_FINISHED, null );
+ write( result, true );
+ }
+
+ public void sendSkipSinceNextTest() throws IOException
+ {
+ int bufferMaxLength = estimateBufferLength( SKIP_SINCE_NEXT_TEST.getOpcodeLength(), null, null, 0 );
+ ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
+ encodeHeader( result, SKIP_SINCE_NEXT_TEST, null );
+ write( result, true );
+ }
+
+ public void sendShutdown( String shutdownData ) throws IOException
+ {
+ CharsetEncoder encoder = newCharsetEncoder();
+ int bufferMaxLength =
+ estimateBufferLength( SHUTDOWN.getOpcodeLength(), null, encoder, 0, shutdownData );
+ ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
+ encode( encoder, result, SHUTDOWN, null, shutdownData );
+ write( result, true );
+ }
+
+ public void sendNoop() throws IOException
+ {
+ int bufferMaxLength = estimateBufferLength( NOOP.getOpcodeLength(), null, null, 0 );
+ ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
+ encodeHeader( result, NOOP, null );
+ write( result, true );
+ }
+
+ public void sendByeAck() throws IOException
+ {
+ int bufferMaxLength = estimateBufferLength( BYE_ACK.getOpcodeLength(), null, null, 0 );
+ ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
+ encodeHeader( result, BYE_ACK, null );
+ write( result, true );
+ }
+
+ @Nonnull
+ @Override
+ protected final byte[] getEncodedMagicNumber()
+ {
+ return MAGIC_NUMBER_FOR_COMMANDS_BYTES;
+ }
+
+ @Nonnull
+ @Override
+ protected final byte[] enumToByteArray( MasterProcessCommand masterProcessCommand )
+ {
+ return masterProcessCommand.getOpcodeBinary();
+ }
+
+ @Nonnull
+ @Override
+ protected final byte[] getEncodedCharsetName()
+ {
+ return DEFAULT_STREAM_ENCODING_BYTES;
+ }
+
+ @Nonnull
+ @Override
+ protected final Charset getCharset()
+ {
+ return DEFAULT_STREAM_ENCODING;
+ }
+
+ @Nonnull
+ @Override
+ protected final CharsetEncoder newCharsetEncoder()
+ {
+ return getCharset().newEncoder();
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ out.close();
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/surefire/stream/EventDecoder.java b/maven-surefire-common/src/main/java/org/apache/maven/surefire/stream/EventDecoder.java
new file mode 100644
index 0000000..3698511
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/surefire/stream/EventDecoder.java
@@ -0,0 +1,468 @@
+package org.apache.maven.surefire.stream;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.booterclient.output.DeserializedStacktraceWriter;
+import org.apache.maven.surefire.api.booter.ForkedProcessEventType;
+import org.apache.maven.surefire.api.event.ConsoleDebugEvent;
+import org.apache.maven.surefire.api.event.ConsoleErrorEvent;
+import org.apache.maven.surefire.api.event.ConsoleInfoEvent;
+import org.apache.maven.surefire.api.event.ConsoleWarningEvent;
+import org.apache.maven.surefire.api.event.ControlByeEvent;
+import org.apache.maven.surefire.api.event.ControlNextTestEvent;
+import org.apache.maven.surefire.api.event.ControlStopOnNextTestEvent;
+import org.apache.maven.surefire.api.event.Event;
+import org.apache.maven.surefire.api.event.JvmExitErrorEvent;
+import org.apache.maven.surefire.api.event.StandardStreamErrEvent;
+import org.apache.maven.surefire.api.event.StandardStreamErrWithNewLineEvent;
+import org.apache.maven.surefire.api.event.StandardStreamOutEvent;
+import org.apache.maven.surefire.api.event.StandardStreamOutWithNewLineEvent;
+import org.apache.maven.surefire.api.event.SystemPropertyEvent;
+import org.apache.maven.surefire.api.event.TestAssumptionFailureEvent;
+import org.apache.maven.surefire.api.event.TestErrorEvent;
+import org.apache.maven.surefire.api.event.TestFailedEvent;
+import org.apache.maven.surefire.api.event.TestSkippedEvent;
+import org.apache.maven.surefire.api.event.TestStartingEvent;
+import org.apache.maven.surefire.api.event.TestSucceededEvent;
+import org.apache.maven.surefire.api.event.TestsetCompletedEvent;
+import org.apache.maven.surefire.api.event.TestsetStartingEvent;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.RunMode;
+import org.apache.maven.surefire.api.report.StackTraceWriter;
+import org.apache.maven.surefire.api.report.TestSetReportEntry;
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder;
+import org.apache.maven.surefire.api.stream.SegmentType;
+
+import javax.annotation.Nonnull;
+import java.io.BufferedOutputStream;
+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.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+
+import static org.apache.maven.surefire.api.booter.Constants.MAGIC_NUMBER_FOR_EVENTS_BYTES;
+import static org.apache.maven.surefire.api.report.CategorizedReportEntry.reportEntry;
+import static org.apache.maven.surefire.api.stream.SegmentType.DATA_INTEGER;
+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;
+
+/**
+ *
+ */
+public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEventType, SegmentType>
+{
+ private static final int DEBUG_SINK_BUFFER_SIZE = 64 * 1024;
+ // due to have fast and thread-safe Map
+ private static final Map<Segment, ForkedProcessEventType> EVENT_TYPES = segmentsToEvents();
+ private static final Map<Segment, RunMode> RUN_MODES = segmentsToRunModes();
+
+ private static final SegmentType[] EVENT_WITHOUT_DATA = new SegmentType[] {
+ END_OF_FRAME
+ };
+
+ private static final SegmentType[] EVENT_WITH_ERROR_TRACE = new SegmentType[] {
+ STRING_ENCODING,
+ DATA_STRING,
+ DATA_STRING,
+ DATA_STRING,
+ END_OF_FRAME
+ };
+
+ private static final SegmentType[] EVENT_WITH_ONE_STRING = new SegmentType[] {
+ STRING_ENCODING,
+ DATA_STRING,
+ END_OF_FRAME
+ };
+
+ private static final SegmentType[] EVENT_WITH_RUNMODE_AND_ONE_STRING = new SegmentType[] {
+ RUN_MODE,
+ STRING_ENCODING,
+ DATA_STRING,
+ END_OF_FRAME
+ };
+
+ private static final SegmentType[] EVENT_WITH_RUNMODE_AND_TWO_STRINGS = new SegmentType[] {
+ RUN_MODE,
+ STRING_ENCODING,
+ DATA_STRING,
+ DATA_STRING,
+ END_OF_FRAME
+ };
+
+ private static final SegmentType[] EVENT_TEST_CONTROL = new SegmentType[] {
+ RUN_MODE,
+ STRING_ENCODING,
+ DATA_STRING,
+ DATA_STRING,
+ DATA_STRING,
+ DATA_STRING,
+ DATA_STRING,
+ DATA_STRING,
+ DATA_INTEGER,
+ DATA_STRING,
+ DATA_STRING,
+ DATA_STRING,
+ END_OF_FRAME
+ };
+
+ private static final int NO_POSITION = -1;
+
+ private final OutputStream debugSink;
+
+ public EventDecoder( @Nonnull ReadableByteChannel channel,
+ @Nonnull ForkNodeArguments arguments )
+ {
+ super( channel, arguments, EVENT_TYPES );
+ debugSink = newDebugSink( arguments );
+ }
+
+ @Override
+ public Event decode( @Nonnull Memento memento ) throws IOException
+ {
+ try
+ {
+ ForkedProcessEventType eventType = readMessageType( memento );
+ if ( eventType == null )
+ {
+ throw new MalformedFrameException( memento.getLine().getPositionByteBuffer(),
+ memento.getByteBuffer().position() );
+ }
+ RunMode runMode = null;
+ for ( SegmentType segmentType : nextSegmentType( eventType ) )
+ {
+ switch ( segmentType )
+ {
+ case RUN_MODE:
+ runMode = RUN_MODES.get( readSegment( memento ) );
+ break;
+ case STRING_ENCODING:
+ memento.setCharset( readCharset( memento ) );
+ break;
+ case DATA_STRING:
+ memento.getData().add( readString( memento ) );
+ break;
+ case DATA_INTEGER:
+ memento.getData().add( readInteger( memento ) );
+ break;
+ case END_OF_FRAME:
+ memento.getLine().setPositionByteBuffer( memento.getByteBuffer().position() );
+ return toMessage( eventType, runMode, memento );
+ default:
+ memento.getLine().setPositionByteBuffer( NO_POSITION );
+ getArguments()
+ .dumpStreamText( "Unknown enum ("
+ + SegmentType.class.getSimpleName()
+ + ") "
+ + segmentType );
+ }
+ }
+ }
+ catch ( MalformedFrameException e )
+ {
+ if ( e.hasValidPositions() )
+ {
+ int length = e.readTo() - e.readFrom();
+ memento.getLine().write( memento.getByteBuffer(), e.readFrom(), length );
+ }
+ return null;
+ }
+ catch ( RuntimeException e )
+ {
+ getArguments().dumpStreamException( e );
+ return null;
+ }
+ catch ( IOException e )
+ {
+ if ( !( e.getCause() instanceof InterruptedException ) )
+ {
+ printRemainingStream( memento );
+ }
+ throw e;
+ }
+ finally
+ {
+ memento.reset();
+ }
+
+ throw new IOException( "unreachable statement" );
+ }
+
+ @Nonnull
+ @Override
+ protected final byte[] getEncodedMagicNumber()
+ {
+ return MAGIC_NUMBER_FOR_EVENTS_BYTES;
+ }
+
+ @Override
+ @Nonnull
+ protected final SegmentType[] nextSegmentType( @Nonnull ForkedProcessEventType eventType )
+ {
+ switch ( eventType )
+ {
+ case BOOTERCODE_BYE:
+ case BOOTERCODE_STOP_ON_NEXT_TEST:
+ case BOOTERCODE_NEXT_TEST:
+ return EVENT_WITHOUT_DATA;
+ case BOOTERCODE_CONSOLE_ERROR:
+ case BOOTERCODE_JVM_EXIT_ERROR:
+ return EVENT_WITH_ERROR_TRACE;
+ case BOOTERCODE_CONSOLE_INFO:
+ case BOOTERCODE_CONSOLE_DEBUG:
+ case BOOTERCODE_CONSOLE_WARNING:
+ return EVENT_WITH_ONE_STRING;
+ case BOOTERCODE_STDOUT:
+ case BOOTERCODE_STDOUT_NEW_LINE:
+ case BOOTERCODE_STDERR:
+ case BOOTERCODE_STDERR_NEW_LINE:
+ return EVENT_WITH_RUNMODE_AND_ONE_STRING;
+ case BOOTERCODE_SYSPROPS:
+ return EVENT_WITH_RUNMODE_AND_TWO_STRINGS;
+ case BOOTERCODE_TESTSET_STARTING:
+ case BOOTERCODE_TESTSET_COMPLETED:
+ case BOOTERCODE_TEST_STARTING:
+ case BOOTERCODE_TEST_SUCCEEDED:
+ case BOOTERCODE_TEST_FAILED:
+ case BOOTERCODE_TEST_SKIPPED:
+ case BOOTERCODE_TEST_ERROR:
+ case BOOTERCODE_TEST_ASSUMPTIONFAILURE:
+ return EVENT_TEST_CONTROL;
+ default:
+ throw new IllegalArgumentException( "Unknown enum " + eventType );
+ }
+ }
+
+ @Override
+ @Nonnull
+ protected final Event toMessage( @Nonnull ForkedProcessEventType eventType,
+ RunMode runMode,
+ @Nonnull Memento memento )
+ throws MalformedFrameException
+ {
+ switch ( eventType )
+ {
+ case BOOTERCODE_BYE:
+ checkArguments( memento, 0 );
+ return new ControlByeEvent();
+ case BOOTERCODE_STOP_ON_NEXT_TEST:
+ checkArguments( memento, 0 );
+ return new ControlStopOnNextTestEvent();
+ case BOOTERCODE_NEXT_TEST:
+ checkArguments( memento, 0 );
+ return new ControlNextTestEvent();
+ case BOOTERCODE_CONSOLE_ERROR:
+ checkArguments( memento, 3 );
+ return new ConsoleErrorEvent( toStackTraceWriter( memento.getData() ) );
+ case BOOTERCODE_JVM_EXIT_ERROR:
+ checkArguments( memento, 3 );
+ return new JvmExitErrorEvent( toStackTraceWriter( memento.getData() ) );
+ case BOOTERCODE_CONSOLE_INFO:
+ checkArguments( memento, 1 );
+ return new ConsoleInfoEvent( (String) memento.getData().get( 0 ) );
+ case BOOTERCODE_CONSOLE_DEBUG:
+ checkArguments( memento, 1 );
+ return new ConsoleDebugEvent( (String) memento.getData().get( 0 ) );
+ case BOOTERCODE_CONSOLE_WARNING:
+ checkArguments( memento, 1 );
+ return new ConsoleWarningEvent( (String) memento.getData().get( 0 ) );
+ case BOOTERCODE_STDOUT:
+ checkArguments( runMode, memento, 1 );
+ return new StandardStreamOutEvent( runMode, (String) memento.getData().get( 0 ) );
+ case BOOTERCODE_STDOUT_NEW_LINE:
+ checkArguments( runMode, memento, 1 );
+ return new StandardStreamOutWithNewLineEvent( runMode, (String) memento.getData().get( 0 ) );
+ case BOOTERCODE_STDERR:
+ checkArguments( runMode, memento, 1 );
+ return new StandardStreamErrEvent( runMode, (String) memento.getData().get( 0 ) );
+ case BOOTERCODE_STDERR_NEW_LINE:
+ checkArguments( runMode, memento, 1 );
+ return new StandardStreamErrWithNewLineEvent( runMode, (String) memento.getData().get( 0 ) );
+ case BOOTERCODE_SYSPROPS:
+ checkArguments( runMode, memento, 2 );
+ String key = (String) memento.getData().get( 0 );
+ String value = (String) memento.getData().get( 1 );
+ return new SystemPropertyEvent( runMode, key, value );
+ case BOOTERCODE_TESTSET_STARTING:
+ checkArguments( runMode, memento, 10 );
+ return new TestsetStartingEvent( runMode, toReportEntry( memento.getData() ) );
+ case BOOTERCODE_TESTSET_COMPLETED:
+ checkArguments( runMode, memento, 10 );
+ return new TestsetCompletedEvent( runMode, toReportEntry( memento.getData() ) );
+ case BOOTERCODE_TEST_STARTING:
+ checkArguments( runMode, memento, 10 );
+ return new TestStartingEvent( runMode, toReportEntry( memento.getData() ) );
+ case BOOTERCODE_TEST_SUCCEEDED:
+ checkArguments( runMode, memento, 10 );
+ return new TestSucceededEvent( runMode, toReportEntry( memento.getData() ) );
+ case BOOTERCODE_TEST_FAILED:
+ checkArguments( runMode, memento, 10 );
+ return new TestFailedEvent( runMode, toReportEntry( memento.getData() ) );
+ case BOOTERCODE_TEST_SKIPPED:
+ checkArguments( runMode, memento, 10 );
+ return new TestSkippedEvent( runMode, toReportEntry( memento.getData() ) );
+ case BOOTERCODE_TEST_ERROR:
+ checkArguments( runMode, memento, 10 );
+ return new TestErrorEvent( runMode, toReportEntry( memento.getData() ) );
+ case BOOTERCODE_TEST_ASSUMPTIONFAILURE:
+ checkArguments( runMode, memento, 10 );
+ return new TestAssumptionFailureEvent( runMode, toReportEntry( memento.getData() ) );
+ default:
+ throw new IllegalArgumentException( "Missing a branch for the event type " + eventType );
+ }
+ }
+
+ @Nonnull
+ private static TestSetReportEntry toReportEntry( List<Object> args )
+ {
+ // ReportEntry:
+ String source = (String) args.get( 0 );
+ String sourceText = (String) args.get( 1 );
+ String name = (String) args.get( 2 );
+ String nameText = (String) args.get( 3 );
+ String group = (String) args.get( 4 );
+ String message = (String) args.get( 5 );
+ Integer timeElapsed = (Integer) args.get( 6 );
+ // StackTraceWriter:
+ String traceMessage = (String) args.get( 7 );
+ String smartTrimmedStackTrace = (String) args.get( 8 );
+ String stackTrace = (String) args.get( 9 );
+ return newReportEntry( source, sourceText, name, nameText, group, message, timeElapsed,
+ traceMessage, smartTrimmedStackTrace, stackTrace );
+ }
+
+ private static StackTraceWriter toStackTraceWriter( List<Object> args )
+ {
+ String traceMessage = (String) args.get( 0 );
+ String smartTrimmedStackTrace = (String) args.get( 1 );
+ String stackTrace = (String) args.get( 2 );
+ return toTrace( traceMessage, smartTrimmedStackTrace, stackTrace );
+ }
+
+ private static StackTraceWriter toTrace( String traceMessage, String smartTrimmedStackTrace, String stackTrace )
+ {
+ boolean exists = traceMessage != null || stackTrace != null || smartTrimmedStackTrace != null;
+ return exists ? new DeserializedStacktraceWriter( traceMessage, smartTrimmedStackTrace, stackTrace ) : null;
+ }
+
+ static TestSetReportEntry newReportEntry( // ReportEntry:
+ String source, String sourceText, String name,
+ String nameText, String group, String message,
+ Integer timeElapsed,
+ // StackTraceWriter:
+ String traceMessage,
+ String smartTrimmedStackTrace, String stackTrace )
+ throws NumberFormatException
+ {
+ StackTraceWriter stackTraceWriter = toTrace( traceMessage, smartTrimmedStackTrace, stackTrace );
+ return reportEntry( source, sourceText, name, nameText, group, stackTraceWriter, timeElapsed, message,
+ Collections.<String, String>emptyMap() );
+ }
+
+ private static Map<Segment, ForkedProcessEventType> segmentsToEvents()
+ {
+ Map<Segment, ForkedProcessEventType> events = new HashMap<>();
+ for ( ForkedProcessEventType event : ForkedProcessEventType.values() )
+ {
+ byte[] array = event.getOpcodeBinary();
+ events.put( new Segment( array, 0, array.length ), event );
+ }
+ return events;
+ }
+
+ private static Map<Segment, RunMode> segmentsToRunModes()
+ {
+ Map<Segment, RunMode> runModes = new HashMap<>();
+ for ( RunMode runMode : RunMode.values() )
+ {
+ byte[] array = runMode.getRunmodeBinary();
+ runModes.put( new Segment( array, 0, array.length ), runMode );
+ }
+ return runModes;
+ }
+
+ @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( ForkNodeArguments arguments )
+ {
+ final File sink = arguments.getEventStreamBinaryFile();
+ if ( sink == null )
+ {
+ return null;
+ }
+
+ try
+ {
+ OutputStream fos = new FileOutputStream( sink, true );
+ final OutputStream os = new BufferedOutputStream( fos, DEBUG_SINK_BUFFER_SIZE );
+ 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;
+ }
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ // do NOT close the channel, it's std/out.
+ if ( debugSink != null )
+ {
+ debugSink.close();
+ }
+ }
+}
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 a8eb91f..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
@@ -25,10 +25,10 @@ import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
import org.apache.maven.plugin.surefire.extensions.EventConsumerThread;
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.surefire.api.booter.ForkingRunListener;
-import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder;
+import org.apache.maven.surefire.booter.spi.EventChannelEncoder;
import org.apache.maven.surefire.api.event.Event;
import org.apache.maven.surefire.extensions.EventHandler;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
import org.apache.maven.surefire.api.report.CategorizedReportEntry;
import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
@@ -258,11 +258,11 @@ public class ForkingRunListenerTest
ReportEntry expected = createDefaultReportEntry();
SimpleReportEntry secondExpected = createAnotherDefaultReportEntry();
- new ForkingRunListener( new LegacyMasterProcessChannelEncoder( newBufferedChannel( printStream ) ), false )
+ new ForkingRunListener( new EventChannelEncoder( newBufferedChannel( printStream ) ), false )
.testStarting( expected );
new ForkingRunListener(
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( anotherPrintStream ) ), false )
+ new EventChannelEncoder( newBufferedChannel( anotherPrintStream ) ), false )
.testSkipped( secondExpected );
TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
@@ -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>
@@ -446,7 +458,7 @@ public class ForkingRunListenerTest
private RunListener createForkingRunListener()
{
WritableBufferedByteChannel channel = (WritableBufferedByteChannel) newChannel( printStream );
- return new ForkingRunListener( new LegacyMasterProcessChannelEncoder( channel ), false );
+ return new ForkingRunListener( new EventChannelEncoder( channel ), false );
}
private class StandardTestRun
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java
index 3d58ade..845897a 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java
@@ -35,7 +35,7 @@ public class MainClass
else
{
System.out.println( ":maven-surefire-event:\u0003:bye:" );
- String byeAck = ":maven-surefire-command:bye-ack:";
+ String byeAck = ":maven-surefire-command:\u0007:bye-ack:";
byte[] cmd = new byte[byeAck.length()];
int len = System.in.read( cmd );
if ( len != -1 && new String( cmd, 0, len ).equals( byeAck ) )
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 3c68778..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
@@ -20,9 +20,9 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
*/
import org.apache.maven.surefire.api.booter.Command;
-import org.apache.maven.surefire.api.booter.MasterProcessCommand;
-import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelDecoder;
import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
+import org.apache.maven.surefire.booter.ForkedNodeArg;
+import org.apache.maven.surefire.booter.spi.CommandChannelDecoder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -33,17 +33,19 @@ import java.util.Iterator;
import java.util.NoSuchElementException;
import static java.nio.channels.Channels.newChannel;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
import static org.apache.maven.surefire.api.booter.Command.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.NOOP;
import static org.apache.maven.surefire.api.booter.MasterProcessCommand.SHUTDOWN;
import static org.apache.maven.surefire.api.booter.Shutdown.EXIT;
import static org.apache.maven.surefire.api.booter.Shutdown.KILL;
-import static org.apache.maven.plugin.surefire.extensions.StreamFeeder.encode;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* Testing cached and immediate commands in {@link TestLessInputStream}.
@@ -144,6 +146,7 @@ public class TestLessInputStreamBuilderTest
{
private byte[] buffer;
private int idx;
+ private boolean isLastBuffer;
@Override
public int read() throws IOException
@@ -154,8 +157,20 @@ public class TestLessInputStreamBuilderTest
Command cmd = pluginIs.readNextCommand();
if ( cmd != null )
{
- MasterProcessCommand cmdType = cmd.getCommandType();
- buffer = cmdType.hasDataType() ? encode( cmdType, cmd.getData() ) : encode( cmdType );
+ if ( cmd.getCommandType() == SHUTDOWN )
+ {
+ buffer = ( ":maven-surefire-command:\u0008:shutdown:\u0005:UTF-8:\u0000\u0000\u0000\u0004:"
+ + cmd.toShutdownData().getParam() + ":" ).getBytes( UTF_8 );
+ }
+ else if ( cmd.getCommandType() == NOOP )
+ {
+ buffer = ":maven-surefire-command:\u0004:noop:".getBytes( UTF_8 );
+ isLastBuffer = true;
+ }
+ else
+ {
+ fail();
+ }
}
}
@@ -169,10 +184,16 @@ public class TestLessInputStreamBuilderTest
}
return b;
}
+
+ if ( isLastBuffer )
+ {
+ return -1;
+ }
throw new IOException();
}
};
- MasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
+ MasterProcessChannelDecoder decoder =
+ new CommandChannelDecoder( newChannel( is ), new ForkedNodeArg( 1, false ) );
builder.getImmediateCommands().shutdown( KILL );
builder.getImmediateCommands().noop();
Command bye = decoder.decode();
@@ -181,7 +202,7 @@ public class TestLessInputStreamBuilderTest
assertThat( bye.getData(), is( KILL.name() ) );
Command noop = decoder.decode();
assertThat( noop, is( notNullValue() ) );
- assertThat( noop.getCommandType(), is( MasterProcessCommand.NOOP ) );
+ assertThat( noop.getCommandType(), is( NOOP ) );
}
@Test( expected = UnsupportedOperationException.class )
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 e0acb9f..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
@@ -20,9 +20,9 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
*/
import org.apache.maven.surefire.api.booter.Command;
-import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelDecoder;
-import org.apache.maven.plugin.surefire.extensions.StreamFeeder;
import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
+import org.apache.maven.surefire.booter.ForkedNodeArg;
+import org.apache.maven.surefire.booter.spi.CommandChannelDecoder;
import org.junit.Test;
import java.io.IOException;
@@ -36,7 +36,8 @@ import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import static java.nio.channels.Channels.newChannel;
-import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.maven.surefire.api.booter.Command.TEST_SET_FINISHED;
import static org.apache.maven.surefire.api.booter.MasterProcessCommand.BYE_ACK;
import static org.apache.maven.surefire.api.booter.MasterProcessCommand.NOOP;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -44,6 +45,7 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* Asserts that this stream properly reads bytes from queue.
@@ -93,8 +95,7 @@ public class TestProvidingInputStreamTest
public void finishedTestsetShouldNotBlock()
throws IOException
{
- Queue<String> commands = new ArrayDeque<>();
- final TestProvidingInputStream is = new TestProvidingInputStream( commands );
+ final TestProvidingInputStream is = new TestProvidingInputStream( new ArrayDeque<String>() );
is.testSetFinished();
new Thread( new Runnable()
{
@@ -105,16 +106,12 @@ public class TestProvidingInputStreamTest
}
} ).start();
- Command cmd = is.readNextCommand();
- assertThat( cmd.getData(), is( nullValue() ) );
- String stream = new String( StreamFeeder.encode( cmd.getCommandType() ), US_ASCII );
-
- cmd = is.readNextCommand();
- assertThat( cmd.getData(), is( nullValue() ) );
- stream += new String( StreamFeeder.encode( cmd.getCommandType() ), US_ASCII );
-
- assertThat( stream,
- is( ":maven-surefire-command:testset-finished::maven-surefire-command:testset-finished:" ) );
+ for ( int i = 0; i < 2; i++ )
+ {
+ Command cmd = is.readNextCommand();
+ assertThat( cmd.getData(), is( nullValue() ) );
+ assertThat( cmd, is( TEST_SET_FINISHED ) );
+ }
boolean emptyStream = isInputStreamEmpty( is );
@@ -162,7 +159,21 @@ public class TestProvidingInputStreamTest
{
idx = 0;
Command cmd = pluginIs.readNextCommand();
- buffer = cmd == null ? null : StreamFeeder.encode( cmd.getCommandType() );
+ if ( cmd != null )
+ {
+ if ( cmd.getCommandType() == BYE_ACK )
+ {
+ buffer = ":maven-surefire-command:\u0007:bye-ack:".getBytes( UTF_8 );
+ }
+ else if ( cmd.getCommandType() == NOOP )
+ {
+ buffer = ":maven-surefire-command:\u0004:noop:".getBytes( UTF_8 );
+ }
+ else
+ {
+ fail();
+ }
+ }
}
if ( buffer != null )
@@ -178,7 +189,8 @@ public class TestProvidingInputStreamTest
throw new IOException();
}
};
- MasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
+ 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 5d871a3..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
@@ -52,7 +52,7 @@ import org.apache.maven.surefire.api.report.SimpleReportEntry;
import org.apache.maven.surefire.api.report.StackTraceWriter;
import org.apache.maven.surefire.api.report.TestSetReportEntry;
import org.apache.maven.surefire.extensions.EventHandler;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
import org.junit.Test;
@@ -302,7 +302,8 @@ public class ForkClientTest
assertThat( logger.isDebugEnabledCalled )
.isTrue();
- String msg = "Corrupted STDOUT by directly writing to native stream in forked JVM 0. Stream 'unordered error'.";
+ String msg =
+ "Corrupted channel by directly writing to native stream in forked JVM 0. Stream 'unordered error'.";
assertThat( arguments.dumpStreamText )
.hasSize( 1 )
.contains( msg );
@@ -310,7 +311,7 @@ public class ForkClientTest
assertThat( arguments.logWarningAtEnd )
.hasSize( 1 );
assertThat( arguments.logWarningAtEnd.peek() )
- .startsWith( "Corrupted STDOUT by directly writing to native stream in forked JVM 0. "
+ .startsWith( "Corrupted channel by directly writing to native stream in forked JVM 0. "
+ "See FAQ web page and the dump file" );
}
@@ -1883,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 f3becd7..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
@@ -27,7 +27,7 @@ import org.apache.maven.surefire.api.event.Event;
import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
import org.apache.maven.surefire.extensions.EventHandler;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
import org.junit.Rule;
import org.junit.Test;
@@ -74,7 +74,7 @@ public class E2ETest
final SurefireMasterProcessChannelProcessorFactory factory = new SurefireMasterProcessChannelProcessorFactory();
factory.connect( connection );
- final MasterProcessChannelEncoder encoder = factory.createEncoder();
+ final MasterProcessChannelEncoder encoder = factory.createEncoder( arguments );
System.gc();
@@ -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 63287df..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
@@ -19,424 +19,32 @@ package org.apache.maven.plugin.surefire.extensions;
* under the License.
*/
-import org.apache.maven.plugin.surefire.extensions.EventConsumerThread.Memento;
-import org.apache.maven.plugin.surefire.extensions.EventConsumerThread.Segment;
-import org.apache.maven.plugin.surefire.extensions.EventConsumerThread.SegmentType;
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
-import org.apache.maven.surefire.api.booter.ForkedProcessEventType;
-import org.apache.maven.surefire.api.event.ConsoleDebugEvent;
-import org.apache.maven.surefire.api.event.ConsoleErrorEvent;
-import org.apache.maven.surefire.api.event.ConsoleInfoEvent;
-import org.apache.maven.surefire.api.event.ConsoleWarningEvent;
-import org.apache.maven.surefire.api.event.ControlByeEvent;
-import org.apache.maven.surefire.api.event.ControlNextTestEvent;
-import org.apache.maven.surefire.api.event.ControlStopOnNextTestEvent;
import org.apache.maven.surefire.api.event.Event;
-import org.apache.maven.surefire.api.event.JvmExitErrorEvent;
-import org.apache.maven.surefire.api.event.StandardStreamErrEvent;
-import org.apache.maven.surefire.api.event.StandardStreamErrWithNewLineEvent;
-import org.apache.maven.surefire.api.event.StandardStreamOutEvent;
-import org.apache.maven.surefire.api.event.StandardStreamOutWithNewLineEvent;
-import org.apache.maven.surefire.api.event.SystemPropertyEvent;
-import org.apache.maven.surefire.api.event.TestAssumptionFailureEvent;
-import org.apache.maven.surefire.api.event.TestErrorEvent;
-import org.apache.maven.surefire.api.event.TestFailedEvent;
-import org.apache.maven.surefire.api.event.TestSkippedEvent;
-import org.apache.maven.surefire.api.event.TestStartingEvent;
-import org.apache.maven.surefire.api.event.TestSucceededEvent;
-import org.apache.maven.surefire.api.event.TestsetCompletedEvent;
-import org.apache.maven.surefire.api.event.TestsetStartingEvent;
-import org.apache.maven.surefire.api.report.RunMode;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.extensions.EventHandler;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
-import org.fest.assertions.Condition;
import org.junit.Test;
import javax.annotation.Nonnull;
import java.io.Closeable;
-import java.io.EOFException;
import java.io.File;
-import java.math.BigInteger;
import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
import java.nio.channels.ReadableByteChannel;
-import java.nio.charset.CharsetDecoder;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.Math.min;
-import static java.nio.charset.CodingErrorAction.REPLACE;
-import static java.nio.charset.StandardCharsets.ISO_8859_1;
-import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Arrays.asList;
import static java.util.Arrays.copyOfRange;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.SegmentType.DATA_INT;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.SegmentType.DATA_STRING;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.SegmentType.END_OF_FRAME;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.SegmentType.RUN_MODE;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.SegmentType.STRING_ENCODING;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.mapEventTypes;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.mapRunModes;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.nextSegmentType;
-import static org.apache.maven.plugin.surefire.extensions.EventConsumerThread.toEvent;
-import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_BYE;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_DEBUG;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_ERROR;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_INFO;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_WARNING;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_JVM_EXIT_ERROR;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_NEXT_TEST;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDERR;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDERR_NEW_LINE;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT_NEW_LINE;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STOP_ON_NEXT_TEST;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_SYSPROPS;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_COMPLETED;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_STARTING;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ASSUMPTIONFAILURE;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ERROR;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_FAILED;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SKIPPED;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_STARTING;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SUCCEEDED;
-import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
-import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAILURE;
+import static java.util.concurrent.TimeUnit.SECONDS;
import static org.fest.assertions.Assertions.assertThat;
-import static org.powermock.reflect.Whitebox.invokeMethod;
/**
- * The performance of "get( Integer )" is 13.5 nano seconds on i5/2.6GHz:
- * <pre>
- * {@code
- * TreeMap<Integer, ForkedProcessEventType> map = new TreeMap<>();
- * map.get( hash );
- * }
- * </pre>
*
- * <br> The performance of getting event type by Segment is 33.7 nano seconds:
- * <pre>
- * {@code
- * Map<Segment, ForkedProcessEventType> map = new HashMap<>();
- * byte[] array = ForkedProcessEventType.BOOTERCODE_STDOUT.getOpcode().getBytes( UTF_8 );
- * map.get( new Segment( array, 0, array.length ) );
- * }
- * </pre>
- *
- * <br> The performance of decoder:
- * <pre>
- * {@code
- * CharsetDecoder decoder = STREAM_ENCODING.newDecoder()
- * .onMalformedInput( REPLACE )
- * .onUnmappableCharacter( REPLACE );
- * ByteBuffer buffer = ByteBuffer.wrap( ForkedProcessEventType.BOOTERCODE_STDOUT.getOpcode().getBytes( UTF_8 ) );
- * CharBuffer chars = CharBuffer.allocate( 100 );
- * decoder.reset().decode( buffer, chars, true );
- *
- * String s = chars.flip().toString(); // 37 nanos = CharsetDecoder + toString
- *
- * buffer.clear();
- * chars.clear();
- *
- * ForkedProcessEventType.byOpcode( s ); // 65 nanos = CharsetDecoder + toString + byOpcode
- * }
- * </pre>
- *
- * <br> The performance of decoding 100 bytes via CharacterDecoder - 71 nano seconds:
- * <pre>
- * {@code
- * decoder.reset()
- * .decode( buffer, chars, true ); // CharsetDecoder 71 nanos
- * chars.flip().toString(); // CharsetDecoder + toString = 91 nanos
- * }
- * </pre>
- *
- * <br> The performance of a pure string creation (instead of decoder) - 31.5 nano seconds:
- * <pre>
- * {@code
- * byte[] b = {};
- * new String( b, UTF_8 );
- * }
- * </pre>
- *
- * <br> The performance of CharsetDecoder with empty ByteBuffer:
- * <pre>
- * {@code
- * CharsetDecoder + ByteBuffer.allocate( 0 ) makes 11.5 nanos
- * CharsetDecoder + ByteBuffer.allocate( 0 ) + toString() makes 16.1 nanos
- * }
- * </pre>
*/
-@SuppressWarnings( "checkstyle:magicnumber" )
public class EventConsumerThreadTest
{
- private static final CountdownCloseable COUNTDOWN_CLOSEABLE = new CountdownCloseable( new MockCloseable(), 0 );
-
- private static final String PATTERN1 =
- "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
-
- private static final String PATTERN2 = "€ab©c";
-
- private static final byte[] PATTERN2_BYTES =
- new byte[]{(byte) -30, (byte) -126, (byte) -84, 'a', 'b', (byte) 0xc2, (byte) 0xa9, 'c'};
-
- @Test
- public void shouldDecodeHappyCase() throws Exception
- {
- CharsetDecoder decoder = UTF_8.newDecoder()
- .onMalformedInput( REPLACE )
- .onUnmappableCharacter( REPLACE );
- ByteBuffer input = ByteBuffer.allocate( 1024 );
- input.put( PATTERN2_BYTES )
- .flip();
- int bytesToDecode = PATTERN2_BYTES.length;
- CharBuffer output = CharBuffer.allocate( 1024 );
- int readBytes =
- invokeMethod( EventConsumerThread.class, "decodeString", decoder, input, output, bytesToDecode, true, 0 );
-
- assertThat( readBytes )
- .isEqualTo( bytesToDecode );
-
- assertThat( output.flip().toString() )
- .isEqualTo( PATTERN2 );
- }
-
- @Test
- public void shouldDecodeShifted() throws Exception
- {
- CharsetDecoder decoder = UTF_8.newDecoder()
- .onMalformedInput( REPLACE )
- .onUnmappableCharacter( REPLACE );
- ByteBuffer input = ByteBuffer.allocate( 1024 );
- input.put( PATTERN1.getBytes( UTF_8 ) )
- .put( 90, (byte) 'A' )
- .put( 91, (byte) 'B' )
- .put( 92, (byte) 'C' )
- .position( 90 );
- CharBuffer output = CharBuffer.allocate( 1024 );
- int readBytes =
- invokeMethod( EventConsumerThread.class, "decodeString", decoder, input, output, 2, true, 0 );
-
- assertThat( readBytes )
- .isEqualTo( 2 );
-
- assertThat( output.flip().toString() )
- .isEqualTo( "AB" );
- }
-
- @Test( expected = IllegalArgumentException.class )
- public void shouldNotDecode() throws Exception
- {
- CharsetDecoder decoder = UTF_8.newDecoder();
- ByteBuffer input = ByteBuffer.allocate( 100 );
- int bytesToDecode = 101;
- CharBuffer output = CharBuffer.allocate( 1000 );
- invokeMethod( EventConsumerThread.class, "decodeString", decoder, input, output, bytesToDecode, true, 0 );
- }
-
- @Test
- public void shouldReadInt() throws Exception
- {
- Channel channel = new Channel( new byte[] {0x01, 0x02, 0x03, 0x04, ':'}, 1 );
-
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- assertThat( thread.readInt( memento ) )
- .isEqualTo( new BigInteger( new byte[] {0x01, 0x02, 0x03, 0x04} ).intValue() );
- }
-
- @Test
- public void shouldReadInteger() throws Exception
- {
- Channel channel = new Channel( new byte[] {(byte) 0xff, 0x01, 0x02, 0x03, 0x04, ':'}, 1 );
-
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- assertThat( thread.readInteger( memento ) )
- .isEqualTo( new BigInteger( new byte[] {0x01, 0x02, 0x03, 0x04} ).intValue() );
- }
-
- @Test
- public void shouldReadNullInteger() throws Exception
- {
- Channel channel = new Channel( new byte[] {(byte) 0x00, ':'}, 1 );
-
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- assertThat( thread.readInteger( memento ) )
- .isNull();
- }
-
- @Test( expected = EOFException.class )
- public void shouldNotReadString() throws Exception
- {
- Channel channel = new Channel( PATTERN1.getBytes(), PATTERN1.length() );
- channel.read( ByteBuffer.allocate( 100 ) );
-
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- thread.readString( memento, 10 );
- }
-
- @Test
- public void shouldReadString() throws Exception
- {
- Channel channel = new Channel( PATTERN1.getBytes(), PATTERN1.length() );
-
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- String s = thread.readString( memento, 10 );
- assertThat( s )
- .isEqualTo( "0123456789" );
- }
-
- @Test
- public void shouldReadStringShiftedBuffer() throws Exception
- {
- StringBuilder s = new StringBuilder( 1100 );
- for ( int i = 0; i < 11; i++ )
- {
- s.append( PATTERN1 );
- }
-
- Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );
-
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- // whatever position will be compacted to 0
- memento.bb.position( 974 ).limit( 974 );
- assertThat( thread.readString( memento, PATTERN1.length() + 3 ) )
- .isEqualTo( PATTERN1 + "012" );
- }
-
- @Test
- public void shouldReadStringShiftedInput() throws Exception
- {
- StringBuilder s = new StringBuilder( 1100 );
- for ( int i = 0; i < 11; i++ )
- {
- s.append( PATTERN1 );
- }
-
- Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );
- channel.read( ByteBuffer.allocate( 997 ) );
-
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.limit( 0 );
- assertThat( thread.readString( memento, PATTERN1.length() ) )
- .isEqualTo( "789" + PATTERN1.substring( 0, 97 ) );
- }
-
- @Test
- public void shouldReadMultipleStringsAndShiftedInput() throws Exception
- {
- StringBuilder s = new StringBuilder( 5000 );
-
- for ( int i = 0; i < 50; i++ )
- {
- s.append( PATTERN1 );
- }
-
- Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );
- channel.read( ByteBuffer.allocate( 1997 ) );
-
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- // whatever position will be compacted to 0
- memento.bb.position( 974 ).limit( 974 );
-
- StringBuilder expected = new StringBuilder( "789" );
- for ( int i = 0; i < 11; i++ )
- {
- expected.append( PATTERN1 );
- }
- expected.setLength( 1100 );
- assertThat( thread.readString( memento, 1100 ) )
- .isEqualTo( expected.toString() );
- }
-
- @Test
- public void shouldDecode3BytesEncodedSymbol() throws Exception
- {
- byte[] encodedSymbol = new byte[] {(byte) -30, (byte) -126, (byte) -84};
- int countSymbols = 1024;
- byte[] input = new byte[encodedSymbol.length * countSymbols];
- for ( int i = 0; i < countSymbols; i++ )
- {
- System.arraycopy( encodedSymbol, 0, input, encodedSymbol.length * i, encodedSymbol.length );
- }
-
- Channel channel = new Channel( input, 2 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- String decodedOutput = thread.readString( memento, input.length );
-
- assertThat( decodedOutput )
- .isEqualTo( new String( input, 0, input.length, UTF_8 ) );
- }
-
- @Test
- public void shouldDecode100Bytes()
- {
- CharsetDecoder decoder = DEFAULT_STREAM_ENCODING.newDecoder()
- .onMalformedInput( REPLACE )
- .onUnmappableCharacter( REPLACE );
- // empty stream: CharsetDecoder + ByteBuffer.allocate( 0 ) makes 11.5 nanos
- // empty stream: CharsetDecoder + ByteBuffer.allocate( 0 ) + toString() makes 16.1 nanos
- ByteBuffer buffer = ByteBuffer.wrap( PATTERN1.getBytes( UTF_8 ) );
- CharBuffer chars = CharBuffer.allocate( 100 );
- /* uncomment this section for a proper measurement of the exec time
- TimeUnit.SECONDS.sleep( 2 );
- System.gc();
- TimeUnit.SECONDS.sleep( 5 );
- */
- String s = null;
- long l1 = System.currentTimeMillis();
- for ( int i = 0; i < 10_000_000; i++ )
- {
- decoder.reset()
- .decode( buffer, chars, true ); // CharsetDecoder 71 nanos
- s = chars.flip().toString(); // CharsetDecoder + toString = 91 nanos
- buffer.clear();
- chars.clear();
- }
- long l2 = System.currentTimeMillis();
- System.out.println( "decoded 100 bytes within " + ( l2 - l1 ) + " millis (10 million cycles)" );
- assertThat( s )
- .isEqualTo( PATTERN1 );
- }
-
+ @SuppressWarnings( "checkstyle:magicnumber" )
@Test( timeout = 60_000L )
public void performanceTest() throws Exception
{
@@ -477,32 +85,36 @@ public class EventConsumerThreadTest
event.flip();
byte[] frame = copyOfRange( event.array(), event.arrayOffset(), event.arrayOffset() + event.remaining() );
- ReadableByteChannel channel = new Channel( frame, 1024 )
+ ReadableByteChannel channel = new Channel( frame, 100 )
{
private int countRounds;
@Override
- public int read( ByteBuffer dst )
+ public synchronized int read( ByteBuffer dst )
{
- int length = super.read( dst );
- if ( length == -1 && countRounds < totalCalls )
+ if ( countRounds == totalCalls )
+ {
+ return -1;
+ }
+
+ if ( remaining() == 0 )
{
- i = 0;
- length = super.read( dst );
countRounds++;
+ i = 0;
}
- return length;
+
+ return super.read( dst );
}
};
EventConsumerThread thread = new EventConsumerThread( "t", channel, handler,
new CountdownCloseable( new MockCloseable(), 1 ), new MockForkNodeArguments() );
- TimeUnit.SECONDS.sleep( 2 );
+ SECONDS.sleep( 2 );
System.gc();
- TimeUnit.SECONDS.sleep( 5 );
+ SECONDS.sleep( 5 );
- System.out.println( "Staring the event thread..." );
+ System.out.println( "Starting the event thread..." );
thread.start();
thread.join();
@@ -510,596 +122,12 @@ public class EventConsumerThreadTest
long execTime = finishedAt[0] - staredAt[0];
System.out.println( execTime );
- // 0.6 seconds while using the encoder/decoder
+ // 0.6 seconds while using the encoder/decoder for 10 million messages
assertThat( execTime )
- .describedAs( "The performance test should assert 1.0s of read time. "
- + "The limit 3.6s guarantees that the read time does not exceed this limit on overloaded CPU." )
+ .describedAs( "The performance test should assert 0.75s of read time. "
+ + "The limit 3.65s guarantees that the read time does not exceed this limit on overloaded CPU." )
.isPositive()
- .isLessThanOrEqualTo( 3_600L );
- }
-
- @Test
- public void shouldReadEventType() throws Exception
- {
- Map<Segment, ForkedProcessEventType> eventTypes = mapEventTypes();
- assertThat( eventTypes )
- .hasSize( ForkedProcessEventType.values().length );
-
- byte[] stream = ":maven-surefire-event:\u000E:std-out-stream:".getBytes( UTF_8 );
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- memento.setCharset( UTF_8 );
-
- ForkedProcessEventType eventType = thread.readEventType( eventTypes, memento );
- assertThat( eventType )
- .isEqualTo( BOOTERCODE_STDOUT );
- }
-
- @Test( expected = EOFException.class )
- public void shouldEventTypeReachedEndOfStream() throws Exception
- {
- Map<Segment, ForkedProcessEventType> eventTypes = mapEventTypes();
- assertThat( eventTypes )
- .hasSize( ForkedProcessEventType.values().length );
-
- byte[] stream = ":maven-surefire-event:\u000E:xxx".getBytes( UTF_8 );
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- memento.setCharset( UTF_8 );
- thread.readEventType( eventTypes, memento );
- }
-
- @Test( expected = EventConsumerThread.MalformedFrameException.class )
- public void shouldEventTypeReachedMalformedHeader() throws Exception
- {
- Map<Segment, ForkedProcessEventType> eventTypes = mapEventTypes();
- assertThat( eventTypes )
- .hasSize( ForkedProcessEventType.values().length );
-
- byte[] stream = ":xxxxx-xxxxxxxx-xxxxx:\u000E:xxx".getBytes( UTF_8 );
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- memento.setCharset( UTF_8 );
- thread.readEventType( eventTypes, memento );
- }
-
- @Test
- public void shouldMapSegmentToEventType()
- {
- Map<Segment, RunMode> map = mapRunModes();
-
- assertThat( map )
- .hasSize( 2 );
-
- byte[] stream = "normal-run".getBytes( US_ASCII );
- Segment segment = new Segment( stream, 0, stream.length );
- assertThat( map.get( segment ) )
- .isEqualTo( NORMAL_RUN );
-
- stream = "rerun-test-after-failure".getBytes( US_ASCII );
- segment = new Segment( stream, 0, stream.length );
- assertThat( map.get( segment ) )
- .isEqualTo( RERUN_TEST_AFTER_FAILURE );
- }
-
- @Test
- public void shouldReadEmptyString() throws Exception
- {
- byte[] stream = "\u0000\u0000\u0000\u0000::".getBytes( UTF_8 );
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- memento.setCharset( UTF_8 );
-
- assertThat( thread.readString( memento ) )
- .isEmpty();
- }
-
- @Test
- public void shouldReadNullString() throws Exception
- {
- byte[] stream = "\u0000\u0000\u0000\u0001:\u0000:".getBytes( UTF_8 );
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- memento.setCharset( UTF_8 );
-
- assertThat( thread.readString( memento ) )
- .isNull();
- }
-
- @Test
- public void shouldReadSingleCharString() throws Exception
- {
- byte[] stream = "\u0000\u0000\u0000\u0001:A:".getBytes( UTF_8 );
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- memento.setCharset( UTF_8 );
-
- assertThat( thread.readString( memento ) )
- .isEqualTo( "A" );
- }
-
- @Test
- public void shouldReadThreeCharactersString() throws Exception
- {
- byte[] stream = "\u0000\u0000\u0000\u0003:ABC:".getBytes( UTF_8 );
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- memento.setCharset( UTF_8 );
-
- assertThat( thread.readString( memento ) )
- .isEqualTo( "ABC" );
- }
-
- @Test
- public void shouldReadDefaultCharset() throws Exception
- {
- byte[] stream = "\u0005:UTF-8:".getBytes( US_ASCII );
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- memento.setCharset( UTF_8 );
-
- assertThat( thread.readCharset( memento ) )
- .isNotNull()
- .isEqualTo( UTF_8 );
- }
-
- @Test
- public void shouldReadNonDefaultCharset() throws Exception
- {
- byte[] stream = ( (char) 10 + ":ISO_8859_1:" ).getBytes( US_ASCII );
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- memento.setCharset( UTF_8 );
-
- assertThat( thread.readCharset( memento ) )
- .isNotNull()
- .isEqualTo( ISO_8859_1 );
- }
-
- @Test
- public void shouldSetNonDefaultCharset()
- {
- byte[] stream = {};
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
- Memento memento = thread.new Memento();
-
- memento.setCharset( ISO_8859_1 );
- assertThat( memento.getDecoder().charset() ).isEqualTo( ISO_8859_1 );
-
- memento.setCharset( UTF_8 );
- assertThat( memento.getDecoder().charset() ).isEqualTo( UTF_8 );
-
- memento.reset();
- assertThat( memento.getDecoder() ).isNotNull();
- assertThat( memento.getDecoder().charset() ).isEqualTo( UTF_8 );
- }
-
- @Test( expected = EventConsumerThread.MalformedFrameException.class )
- public void malformedCharset() throws Exception
- {
- byte[] stream = ( (char) 8 + ":ISO_8859:" ).getBytes( US_ASCII );
- Channel channel = new Channel( stream, 1 );
- EventConsumerThread thread = new EventConsumerThread( "t", channel,
- new MockEventHandler<Event>(), COUNTDOWN_CLOSEABLE, new MockForkNodeArguments() );
-
- Memento memento = thread.new Memento();
- memento.bb.position( 0 ).limit( 0 );
- memento.setCharset( UTF_8 );
-
- thread.readCharset( memento );
- }
-
- @Test
- public void shouldMapEventTypeToSegmentType()
- {
- SegmentType[] segmentTypes = nextSegmentType( BOOTERCODE_BYE );
- assertThat( segmentTypes )
- .hasSize( 1 )
- .containsOnly( END_OF_FRAME );
-
- segmentTypes = nextSegmentType( BOOTERCODE_STOP_ON_NEXT_TEST );
- assertThat( segmentTypes )
- .hasSize( 1 )
- .containsOnly( END_OF_FRAME );
-
- segmentTypes = nextSegmentType( BOOTERCODE_NEXT_TEST );
- assertThat( segmentTypes )
- .hasSize( 1 )
- .containsOnly( END_OF_FRAME );
-
- segmentTypes = nextSegmentType( BOOTERCODE_CONSOLE_ERROR );
- assertThat( segmentTypes )
- .hasSize( 5 )
- .satisfies( new InOrder( STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_JVM_EXIT_ERROR );
- assertThat( segmentTypes )
- .hasSize( 5 )
- .satisfies( new InOrder( STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( BOOTERCODE_CONSOLE_INFO );
- assertThat( segmentTypes )
- .hasSize( 3 )
- .satisfies( new InOrder( STRING_ENCODING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_CONSOLE_DEBUG );
- assertThat( segmentTypes )
- .hasSize( 3 )
- .satisfies( new InOrder( STRING_ENCODING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_CONSOLE_WARNING );
- assertThat( segmentTypes )
- .hasSize( 3 )
- .satisfies( new InOrder( STRING_ENCODING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( BOOTERCODE_STDOUT );
- assertThat( segmentTypes )
- .hasSize( 4 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_STDOUT_NEW_LINE );
- assertThat( segmentTypes )
- .hasSize( 4 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_STDERR );
- assertThat( segmentTypes )
- .hasSize( 4 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_STDERR_NEW_LINE );
- assertThat( segmentTypes )
- .hasSize( 4 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( BOOTERCODE_SYSPROPS );
- assertThat( segmentTypes )
- .hasSize( 5 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( BOOTERCODE_TESTSET_STARTING );
- assertThat( segmentTypes )
- .hasSize( 13 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING,
- DATA_STRING, DATA_STRING, DATA_INT, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_TESTSET_COMPLETED );
- assertThat( segmentTypes )
- .hasSize( 13 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING,
- DATA_STRING, DATA_STRING, DATA_INT, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_TEST_STARTING );
- assertThat( segmentTypes )
- .hasSize( 13 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING,
- DATA_STRING, DATA_STRING, DATA_INT, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( BOOTERCODE_TEST_SUCCEEDED );
- assertThat( segmentTypes )
- .hasSize( 13 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING,
- DATA_STRING, DATA_STRING, DATA_INT, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( BOOTERCODE_TEST_FAILED );
- assertThat( segmentTypes )
- .hasSize( 13 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING,
- DATA_STRING, DATA_STRING, DATA_INT, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_TEST_SKIPPED );
- assertThat( segmentTypes )
- .hasSize( 13 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING,
- DATA_STRING, DATA_STRING, DATA_INT, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_TEST_ERROR );
- assertThat( segmentTypes )
- .hasSize( 13 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING,
- DATA_STRING, DATA_STRING, DATA_INT, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
-
- segmentTypes = nextSegmentType( ForkedProcessEventType.BOOTERCODE_TEST_ASSUMPTIONFAILURE );
- assertThat( segmentTypes )
- .hasSize( 13 )
- .satisfies( new InOrder( RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING,
- DATA_STRING, DATA_STRING, DATA_INT, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME ) );
- }
-
- @Test
- public void shouldCreateEvent()
- {
- Event event = toEvent( BOOTERCODE_BYE, NORMAL_RUN, emptyList() );
- assertThat( event )
- .isInstanceOf( ControlByeEvent.class );
-
- event = toEvent( BOOTERCODE_STOP_ON_NEXT_TEST, NORMAL_RUN, emptyList() );
- assertThat( event )
- .isInstanceOf( ControlStopOnNextTestEvent.class );
-
- event = toEvent( BOOTERCODE_NEXT_TEST, NORMAL_RUN, emptyList() );
- assertThat( event )
- .isInstanceOf( ControlNextTestEvent.class );
-
- List data = asList( "1", "2", "3" );
- event = toEvent( BOOTERCODE_CONSOLE_ERROR, NORMAL_RUN, data );
- assertThat( event )
- .isInstanceOf( ConsoleErrorEvent.class );
- ConsoleErrorEvent consoleErrorEvent = (ConsoleErrorEvent) event;
- assertThat( consoleErrorEvent.getStackTraceWriter().getThrowable().getLocalizedMessage() )
- .isEqualTo( "1" );
- assertThat( consoleErrorEvent.getStackTraceWriter().smartTrimmedStackTrace() )
- .isEqualTo( "2" );
- assertThat( consoleErrorEvent.getStackTraceWriter().writeTraceToString() )
- .isEqualTo( "3" );
-
- data = asList( null, null, null );
- event = toEvent( BOOTERCODE_CONSOLE_ERROR, NORMAL_RUN, data );
- assertThat( event )
- .isInstanceOf( ConsoleErrorEvent.class );
- consoleErrorEvent = (ConsoleErrorEvent) event;
- assertThat( consoleErrorEvent.getStackTraceWriter() )
- .isNull();
-
- data = asList( "1", "2", "3" );
- event = toEvent( BOOTERCODE_JVM_EXIT_ERROR, NORMAL_RUN, data );
- assertThat( event )
- .isInstanceOf( JvmExitErrorEvent.class );
- JvmExitErrorEvent jvmExitErrorEvent = (JvmExitErrorEvent) event;
- assertThat( jvmExitErrorEvent.getStackTraceWriter().getThrowable().getLocalizedMessage() )
- .isEqualTo( "1" );
- assertThat( jvmExitErrorEvent.getStackTraceWriter().smartTrimmedStackTrace() )
- .isEqualTo( "2" );
- assertThat( jvmExitErrorEvent.getStackTraceWriter().writeTraceToString() )
- .isEqualTo( "3" );
-
- data = asList( null, null, null );
- event = toEvent( BOOTERCODE_JVM_EXIT_ERROR, NORMAL_RUN, data );
- assertThat( event )
- .isInstanceOf( JvmExitErrorEvent.class );
- jvmExitErrorEvent = (JvmExitErrorEvent) event;
- assertThat( jvmExitErrorEvent.getStackTraceWriter() )
- .isNull();
-
- data = singletonList( "m" );
- event = toEvent( BOOTERCODE_CONSOLE_INFO, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( ConsoleInfoEvent.class );
- assertThat( ( (ConsoleInfoEvent) event ).getMessage() ).isEqualTo( "m" );
-
- data = singletonList( "" );
- event = toEvent( BOOTERCODE_CONSOLE_WARNING, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( ConsoleWarningEvent.class );
- assertThat( ( (ConsoleWarningEvent) event ).getMessage() ).isEmpty();
-
- data = singletonList( null );
- event = toEvent( BOOTERCODE_CONSOLE_DEBUG, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( ConsoleDebugEvent.class );
- assertThat( ( (ConsoleDebugEvent) event ).getMessage() ).isNull();
-
- data = singletonList( "m" );
- event = toEvent( BOOTERCODE_STDOUT, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( StandardStreamOutEvent.class );
- assertThat( ( (StandardStreamOutEvent) event ).getMessage() ).isEqualTo( "m" );
- assertThat( ( (StandardStreamOutEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
-
- data = singletonList( null );
- event = toEvent( BOOTERCODE_STDOUT_NEW_LINE, RERUN_TEST_AFTER_FAILURE, data );
- assertThat( event ).isInstanceOf( StandardStreamOutWithNewLineEvent.class );
- assertThat( ( (StandardStreamOutWithNewLineEvent) event ).getMessage() ).isNull();
- assertThat( ( (StandardStreamOutWithNewLineEvent) event ).getRunMode() ).isEqualTo( RERUN_TEST_AFTER_FAILURE );
-
- data = singletonList( null );
- event = toEvent( BOOTERCODE_STDERR, RERUN_TEST_AFTER_FAILURE, data );
- assertThat( event ).isInstanceOf( StandardStreamErrEvent.class );
- assertThat( ( (StandardStreamErrEvent) event ).getMessage() ).isNull();
- assertThat( ( (StandardStreamErrEvent) event ).getRunMode() ).isEqualTo( RERUN_TEST_AFTER_FAILURE );
-
- data = singletonList( "abc" );
- event = toEvent( BOOTERCODE_STDERR_NEW_LINE, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( StandardStreamErrWithNewLineEvent.class );
- assertThat( ( (StandardStreamErrWithNewLineEvent) event ).getMessage() ).isEqualTo( "abc" );
- assertThat( ( (StandardStreamErrWithNewLineEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
-
- data = asList( "key", "value" );
- event = toEvent( BOOTERCODE_SYSPROPS, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( SystemPropertyEvent.class );
- assertThat( ( (SystemPropertyEvent) event ).getKey() ).isEqualTo( "key" );
- assertThat( ( (SystemPropertyEvent) event ).getValue() ).isEqualTo( "value" );
- assertThat( ( (SystemPropertyEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
-
- data = asList( "source", "sourceText", "name", "nameText", "group", "message", 5,
- "traceMessage", "smartTrimmedStackTrace", "stackTrace" );
- event = toEvent( BOOTERCODE_TESTSET_STARTING, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( TestsetStartingEvent.class );
- assertThat( ( (TestsetStartingEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
- assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
- assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
- assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
- assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getNameText() ).isEqualTo( "nameText" );
- assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
- assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getMessage() ).isEqualTo( "message" );
- assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
- assertThat( ( (TestsetStartingEvent) event )
- .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
- .isEqualTo( "traceMessage" );
- assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
- .isEqualTo( "smartTrimmedStackTrace" );
- assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getStackTraceWriter().writeTraceToString() )
- .isEqualTo( "stackTrace" );
-
- data = asList( "source", "sourceText", "name", "nameText", "group", null, 5,
- "traceMessage", "smartTrimmedStackTrace", "stackTrace" );
- event = toEvent( BOOTERCODE_TESTSET_COMPLETED, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( TestsetCompletedEvent.class );
- assertThat( ( (TestsetCompletedEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
- assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
- assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
- assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
- assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getNameText() ).isEqualTo( "nameText" );
- assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
- assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getMessage() ).isNull();
- assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
- assertThat( ( (TestsetCompletedEvent) event )
- .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
- .isEqualTo( "traceMessage" );
- assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
- .isEqualTo( "smartTrimmedStackTrace" );
- assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getStackTraceWriter().writeTraceToString() )
- .isEqualTo( "stackTrace" );
-
- data = asList( "source", "sourceText", "name", "nameText", "group", "message", 5,
- null, "smartTrimmedStackTrace", "stackTrace" );
- event = toEvent( BOOTERCODE_TEST_STARTING, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( TestStartingEvent.class );
- assertThat( ( (TestStartingEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
- assertThat( ( (TestStartingEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
- assertThat( ( (TestStartingEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
- assertThat( ( (TestStartingEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
- assertThat( ( (TestStartingEvent) event ).getReportEntry().getNameText() ).isEqualTo( "nameText" );
- assertThat( ( (TestStartingEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
- assertThat( ( (TestStartingEvent) event ).getReportEntry().getMessage() ).isEqualTo( "message" );
- assertThat( ( (TestStartingEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
- assertThat( ( (TestStartingEvent) event )
- .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
- .isNull();
- assertThat( ( (TestStartingEvent) event ).getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
- .isEqualTo( "smartTrimmedStackTrace" );
- assertThat( ( (TestStartingEvent) event ).getReportEntry().getStackTraceWriter().writeTraceToString() )
- .isEqualTo( "stackTrace" );
-
- data = asList( "source", "sourceText", "name", "nameText", "group", "message", 5, null, null, null );
- event = toEvent( BOOTERCODE_TEST_SUCCEEDED, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( TestSucceededEvent.class );
- assertThat( ( (TestSucceededEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
- assertThat( ( (TestSucceededEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
- assertThat( ( (TestSucceededEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
- assertThat( ( (TestSucceededEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
- assertThat( ( (TestSucceededEvent) event ).getReportEntry().getNameText() ).isEqualTo( "nameText" );
- assertThat( ( (TestSucceededEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
- assertThat( ( (TestSucceededEvent) event ).getReportEntry().getMessage() ).isEqualTo( "message" );
- assertThat( ( (TestSucceededEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
- assertThat( ( (TestSucceededEvent) event ).getReportEntry().getStackTraceWriter() ).isNull();
-
- data = asList( "source", null, "name", null, "group", null, 5,
- "traceMessage", "smartTrimmedStackTrace", "stackTrace" );
- event = toEvent( BOOTERCODE_TEST_FAILED, RERUN_TEST_AFTER_FAILURE, data );
- assertThat( event ).isInstanceOf( TestFailedEvent.class );
- assertThat( ( (TestFailedEvent) event ).getRunMode() ).isEqualTo( RERUN_TEST_AFTER_FAILURE );
- assertThat( ( (TestFailedEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
- assertThat( ( (TestFailedEvent) event ).getReportEntry().getSourceText() ).isNull();
- assertThat( ( (TestFailedEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
- assertThat( ( (TestFailedEvent) event ).getReportEntry().getNameText() ).isNull();
- assertThat( ( (TestFailedEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
- assertThat( ( (TestFailedEvent) event ).getReportEntry().getMessage() ).isNull();
- assertThat( ( (TestFailedEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
- assertThat( ( (TestFailedEvent) event )
- .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
- .isEqualTo( "traceMessage" );
- assertThat( ( (TestFailedEvent) event ).getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
- .isEqualTo( "smartTrimmedStackTrace" );
- assertThat( ( (TestFailedEvent) event ).getReportEntry().getStackTraceWriter().writeTraceToString() )
- .isEqualTo( "stackTrace" );
-
- data = asList( "source", null, "name", null, null, null, 5, null, null, "stackTrace" );
- event = toEvent( BOOTERCODE_TEST_SKIPPED, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( TestSkippedEvent.class );
- assertThat( ( (TestSkippedEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
- assertThat( ( (TestSkippedEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
- assertThat( ( (TestSkippedEvent) event ).getReportEntry().getSourceText() ).isNull();
- assertThat( ( (TestSkippedEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
- assertThat( ( (TestSkippedEvent) event ).getReportEntry().getNameText() ).isNull();
- assertThat( ( (TestSkippedEvent) event ).getReportEntry().getGroup() ).isNull();
- assertThat( ( (TestSkippedEvent) event ).getReportEntry().getMessage() ).isNull();
- assertThat( ( (TestSkippedEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
- assertThat( ( (TestSkippedEvent) event )
- .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
- .isNull();
- assertThat( ( (TestSkippedEvent) event )
- .getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
- .isNull();
- assertThat( ( (TestSkippedEvent) event )
- .getReportEntry().getStackTraceWriter().writeTraceToString() )
- .isEqualTo( "stackTrace" );
-
- data = asList( "source", null, "name", "nameText", null, null, 0, null, null, "stackTrace" );
- event = toEvent( BOOTERCODE_TEST_ERROR, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( TestErrorEvent.class );
- assertThat( ( (TestErrorEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
- assertThat( ( (TestErrorEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
- assertThat( ( (TestErrorEvent) event ).getReportEntry().getSourceText() ).isNull();
- assertThat( ( (TestErrorEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
- assertThat( ( (TestErrorEvent) event ).getReportEntry().getNameText() ).isEqualTo( "nameText" );
- assertThat( ( (TestErrorEvent) event ).getReportEntry().getGroup() ).isNull();
- assertThat( ( (TestErrorEvent) event ).getReportEntry().getMessage() ).isNull();
- assertThat( ( (TestErrorEvent) event ).getReportEntry().getElapsed() ).isZero();
- assertThat( ( (TestErrorEvent) event )
- .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
- .isNull();
- assertThat( ( (TestErrorEvent) event )
- .getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
- .isNull();
- assertThat( ( (TestErrorEvent) event )
- .getReportEntry().getStackTraceWriter().writeTraceToString() )
- .isEqualTo( "stackTrace" );
-
- data = asList( "source", null, "name", null, "group", null, 5, null, null, "stackTrace" );
- event = toEvent( BOOTERCODE_TEST_ASSUMPTIONFAILURE, NORMAL_RUN, data );
- assertThat( event ).isInstanceOf( TestAssumptionFailureEvent.class );
- assertThat( ( (TestAssumptionFailureEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
- assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
- assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getSourceText() ).isNull();
- assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
- assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getNameText() ).isNull();
- assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
- assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getMessage() ).isNull();
- assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
- assertThat( ( (TestAssumptionFailureEvent) event )
- .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
- .isNull();
- assertThat( ( (TestAssumptionFailureEvent) event )
- .getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
- .isNull();
- assertThat( ( (TestAssumptionFailureEvent) event )
- .getReportEntry().getStackTraceWriter().writeTraceToString() )
- .isEqualTo( "stackTrace" );
+ .isLessThanOrEqualTo( 3_650L );
}
private static class Channel implements ReadableByteChannel
@@ -1117,13 +145,13 @@ public class EventConsumerThreadTest
@Override
public int read( ByteBuffer dst )
{
- if ( i == bytes.length )
+ if ( remaining() == 0 )
{
return -1;
}
else if ( dst.hasRemaining() )
{
- int length = min( min( chunkSize, bytes.length - i ), dst.remaining() ) ;
+ int length = min( min( chunkSize, remaining() ), dst.remaining() ) ;
dst.put( bytes, i, length );
i += length;
return length;
@@ -1134,6 +162,11 @@ public class EventConsumerThreadTest
}
}
+ protected final int remaining()
+ {
+ return bytes.length - i;
+ }
+
@Override
public boolean isOpen()
{
@@ -1154,14 +187,6 @@ public class EventConsumerThreadTest
}
}
- private static class MockEventHandler<T> implements EventHandler<T>
- {
- @Override
- public void handleEvent( @Nonnull T event )
- {
- }
- }
-
private static class MockForkNodeArguments implements ForkNodeArguments
{
@Nonnull
@@ -1202,36 +227,17 @@ public class EventConsumerThreadTest
{
return null;
}
- }
-
- private static class InOrder extends Condition<Object[]>
- {
- private final SegmentType[] expected;
- InOrder( SegmentType... expected )
+ @Override
+ public File getEventStreamBinaryFile()
{
- this.expected = expected;
+ return null;
}
@Override
- public boolean matches( Object[] values )
+ public File getCommandStreamBinaryFile()
{
- if ( values == null && expected == null )
- {
- return true;
- }
- else if ( values != null && expected != null && values.length == expected.length )
- {
- boolean matches = true;
- for ( int i = 0; i < values.length; i++ )
- {
-
- assertThat( values[i] ).isInstanceOf( SegmentType.class );
- matches &= values[i] == expected[i];
- }
- return matches;
- }
- return false;
+ 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 3dbde1f..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
@@ -31,15 +31,15 @@ import org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessStringE
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerUtils;
import org.apache.maven.surefire.api.event.Event;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.api.report.ReportEntry;
import org.apache.maven.surefire.api.report.RunMode;
import org.apache.maven.surefire.api.report.SafeThrowable;
import org.apache.maven.surefire.api.report.StackTraceWriter;
import org.apache.maven.surefire.api.util.internal.ObjectUtils;
import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
-import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder;
+import org.apache.maven.surefire.booter.spi.EventChannelEncoder;
import org.apache.maven.surefire.extensions.EventHandler;
-import org.apache.maven.surefire.extensions.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
import org.junit.Rule;
import org.junit.Test;
@@ -106,9 +106,9 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
WritableBufferedByteChannel wChannel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
+ EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
Map<String, String> props = ObjectUtils.systemProps();
- encoder.sendSystemProperties( props );
+ encoder.systemProperties( props );
wChannel.close();
ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
@@ -138,154 +138,10 @@ public class ForkedProcessEventNotifierTest
}
@Test
- public void shouldRecognizeEmptyStream4ReportEntry()
- {
- ReportEntry reportEntry = EventConsumerThread.newReportEntry( "", "", "", "", "", "", null, "", "", "" );
- assertThat( reportEntry ).isNotNull();
- assertThat( reportEntry.getStackTraceWriter() ).isNotNull();
- assertThat( reportEntry.getStackTraceWriter().smartTrimmedStackTrace() ).isEmpty();
- assertThat( reportEntry.getStackTraceWriter().writeTraceToString() ).isEmpty();
- assertThat( reportEntry.getStackTraceWriter().writeTrimmedTraceToString() ).isEmpty();
- assertThat( reportEntry.getSourceName() ).isEmpty();
- assertThat( reportEntry.getSourceText() ).isEmpty();
- assertThat( reportEntry.getName() ).isEmpty();
- assertThat( reportEntry.getNameText() ).isEmpty();
- assertThat( reportEntry.getGroup() ).isEmpty();
- assertThat( reportEntry.getNameWithGroup() ).isEmpty();
- assertThat( reportEntry.getMessage() ).isEmpty();
- assertThat( reportEntry.getElapsed() ).isNull();
- }
-
- @Test
- @SuppressWarnings( "checkstyle:magicnumber" )
- public void testCreatingReportEntry()
- {
- final String exceptionMessage = "msg";
- final String smartStackTrace = "MyTest:86 >> Error";
- final String stackTrace = "Exception: msg\ntrace line 1\ntrace line 2";
- final String trimmedStackTrace = "trace line 1\ntrace line 2";
-
- SafeThrowable safeThrowable = new SafeThrowable( exceptionMessage );
- StackTraceWriter stackTraceWriter = mock( StackTraceWriter.class );
- when( stackTraceWriter.getThrowable() ).thenReturn( safeThrowable );
- when( stackTraceWriter.smartTrimmedStackTrace() ).thenReturn( smartStackTrace );
- when( stackTraceWriter.writeTrimmedTraceToString() ).thenReturn( trimmedStackTrace );
- when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
-
- ReportEntry reportEntry = mock( ReportEntry.class );
- when( reportEntry.getElapsed() ).thenReturn( 102 );
- when( reportEntry.getGroup() ).thenReturn( "this group" );
- when( reportEntry.getMessage() ).thenReturn( "skipped test" );
- when( reportEntry.getName() ).thenReturn( "my test" );
- when( reportEntry.getNameText() ).thenReturn( "my display name" );
- when( reportEntry.getNameWithGroup() ).thenReturn( "name with group" );
- when( reportEntry.getSourceName() ).thenReturn( "pkg.MyTest" );
- when( reportEntry.getSourceText() ).thenReturn( "test class display name" );
- when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
-
- ReportEntry decodedReportEntry = EventConsumerThread.newReportEntry( reportEntry.getSourceName(),
- reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
- reportEntry.getMessage(), null, null, null, null );
-
- assertThat( decodedReportEntry ).isNotNull();
- assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
- assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
- assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
- assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
- assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
- assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
- assertThat( decodedReportEntry.getStackTraceWriter() ).isNull();
-
- decodedReportEntry = EventConsumerThread.newReportEntry( reportEntry.getSourceName(),
- reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
- reportEntry.getMessage(), null, exceptionMessage, smartStackTrace, null );
-
- assertThat( decodedReportEntry ).isNotNull();
- assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
- assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
- assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
- assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
- assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
- assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
- assertThat( decodedReportEntry.getElapsed() ).isNull();
- assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
- assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
- .isEqualTo( exceptionMessage );
- assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
- .isEqualTo( smartStackTrace );
- assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() )
- .isNull();
-
- decodedReportEntry = EventConsumerThread.newReportEntry( reportEntry.getSourceName(),
- reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
- reportEntry.getMessage(), 1003, exceptionMessage, smartStackTrace, null );
-
- assertThat( decodedReportEntry ).isNotNull();
- assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
- assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
- assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
- assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
- assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
- assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
- assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
- assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
- assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
- .isEqualTo( exceptionMessage );
- assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
- .isEqualTo( smartStackTrace );
- assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() )
- .isNull();
-
- decodedReportEntry = EventConsumerThread.newReportEntry( reportEntry.getSourceName(),
- reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
- reportEntry.getMessage(), 1003, exceptionMessage, smartStackTrace, stackTrace );
-
- assertThat( decodedReportEntry ).isNotNull();
- assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
- assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
- assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
- assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
- assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
- assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
- assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
- assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
- assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() ).isNotNull();
- assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
- .isEqualTo( exceptionMessage );
- assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
- .isEqualTo( smartStackTrace );
- assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() ).isEqualTo( stackTrace );
- assertThat( decodedReportEntry.getStackTraceWriter().writeTrimmedTraceToString() ).isEqualTo( stackTrace );
-
- decodedReportEntry = EventConsumerThread.newReportEntry( reportEntry.getSourceName(),
- reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
- reportEntry.getMessage(), 1003, exceptionMessage, smartStackTrace, trimmedStackTrace );
-
- assertThat( decodedReportEntry ).isNotNull();
- assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
- assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
- assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
- assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
- assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
- assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
- assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
- assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
- assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() ).isNotNull();
- assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
- .isEqualTo( exceptionMessage );
- assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
- .isEqualTo( smartStackTrace );
- assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() ).isEqualTo( trimmedStackTrace );
- assertThat( decodedReportEntry.getStackTraceWriter().writeTrimmedTraceToString() )
- .isEqualTo( trimmedStackTrace );
- }
-
- @Test
public void shouldSendByeEvent() throws Exception
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.bye();
String read = new String( out.toByteArray(), UTF_8 );
@@ -325,8 +181,7 @@ public class ForkedProcessEventNotifierTest
public void shouldSendStopOnNextTestEvent() throws Exception
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.stopOnNextTest();
String read = new String( out.toByteArray(), UTF_8 );
@@ -390,8 +245,7 @@ public class ForkedProcessEventNotifierTest
when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
final Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.testFailed( reportEntry, true );
ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -421,8 +275,7 @@ public class ForkedProcessEventNotifierTest
public void shouldSendNextTestEvent() throws Exception
{
final Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.acquireNextTest();
String read = new String( out.toByteArray(), UTF_8 );
@@ -456,8 +309,7 @@ public class ForkedProcessEventNotifierTest
public void testConsole() throws Exception
{
final Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.consoleInfoLog( "msg" );
ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -487,8 +339,7 @@ public class ForkedProcessEventNotifierTest
public void testError() throws Exception
{
final Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.consoleErrorLog( "msg" );
ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -518,8 +369,7 @@ public class ForkedProcessEventNotifierTest
public void testErrorWithException() throws Exception
{
final Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
Throwable throwable = new Throwable( "msg" );
encoder.consoleErrorLog( throwable );
@@ -531,7 +381,7 @@ public class ForkedProcessEventNotifierTest
notifier.setConsoleErrorListener( listener );
EH eventHandler = new EH();
- CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
+ CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 1 );
ConsoleLoggerMock logger = new ConsoleLoggerMock( false, false, false, false );
ForkNodeArgumentsMock arguments = new ForkNodeArgumentsMock( logger, new File( "" ) );
try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
@@ -552,8 +402,7 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
StackTraceWriter stackTraceWriter = new DeserializedStacktraceWriter( "1", "2", "3" );
encoder.consoleErrorLog( stackTraceWriter, false );
@@ -585,8 +434,7 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.consoleDebugLog( "msg" );
ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -623,8 +471,7 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.consoleWarningLog( "msg" );
ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -655,7 +502,7 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
WritableBufferedByteChannel wChannel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
+ EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
encoder.stdOut( "msg", false );
wChannel.close();
@@ -688,7 +535,7 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
WritableBufferedByteChannel wChannel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
+ EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
encoder.stdOut( "", false );
wChannel.close();
@@ -721,7 +568,7 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
WritableBufferedByteChannel wChannel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
+ EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
encoder.stdOut( null, false );
wChannel.close();
@@ -754,7 +601,7 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
WritableBufferedByteChannel wChannel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
+ EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
encoder.stdOut( "", true );
wChannel.close();
@@ -787,7 +634,7 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
WritableBufferedByteChannel wChannel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
+ EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
encoder.stdOut( null, true );
wChannel.close();
@@ -820,7 +667,7 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
WritableBufferedByteChannel wChannel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
+ EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
encoder.stdErr( "msg", false );
wChannel.close();
@@ -853,8 +700,8 @@ public class ForkedProcessEventNotifierTest
{
final Stream out = Stream.newStream();
WritableBufferedByteChannel wChannel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
- encoder.sendSystemProperties( ObjectUtils.systemProps() );
+ EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
+ encoder.systemProperties( ObjectUtils.systemProps() );
wChannel.close();
ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -918,7 +765,7 @@ public class ForkedProcessEventNotifierTest
assertThat( logger.debug.peek() )
.contains( ":maven-surefire-event:\u000c:abnormal-run:-:" );
- String dump = "Corrupted STDOUT by directly writing to native stream in forked JVM 0.";
+ String dump = "Corrupted channel by directly writing to native stream in forked JVM 0.";
assertThat( arguments.dumpStreamText )
.hasSize( 1 )
.contains( format( dump + " Stream '%s'.", ":maven-surefire-event:\u000c:abnormal-run:-:" ) );
@@ -934,8 +781,7 @@ public class ForkedProcessEventNotifierTest
public void shouldHandleExit() throws Exception
{
final Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
StackTraceWriter stackTraceWriter = mock( StackTraceWriter.class );
when( stackTraceWriter.getThrowable() ).thenReturn( new SafeThrowable( "1" ) );
@@ -1050,10 +896,9 @@ public class ForkedProcessEventNotifierTest
final Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder =
- new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
- LegacyMasterProcessChannelEncoder.class.getMethod( operation[0], ReportEntry.class, boolean.class )
+ EventChannelEncoder.class.getMethod( operation[0], ReportEntry.class, boolean.class )
.invoke( encoder, reportEntry, trim );
ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
@@ -1323,7 +1168,7 @@ public class ForkedProcessEventNotifierTest
@Override
public File dumpStreamException( @Nonnull Throwable t )
{
- throw new UnsupportedOperationException();
+ return dumpStreamTextFile;
}
@Override
@@ -1343,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/plugin/surefire/extensions/StreamFeederTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/StreamFeederTest.java
index 06cef43..adbb2a7 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/StreamFeederTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/StreamFeederTest.java
@@ -36,7 +36,6 @@ import java.util.Iterator;
import static java.util.Arrays.asList;
import static org.apache.maven.surefire.api.booter.Command.TEST_SET_FINISHED;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.NOOP;
import static org.apache.maven.surefire.api.booter.MasterProcessCommand.RUN_CLASS;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -92,8 +91,8 @@ public class StreamFeederTest
{
ByteBuffer bb = invocation.getArgument( 0 );
bb.flip();
- out.write( bb.array() );
- return 0;
+ out.write( bb.array(), 0, bb.limit() );
+ return bb.limit();
}
} );
@@ -104,8 +103,28 @@ public class StreamFeederTest
streamFeeder.join();
String commands = out.toString();
+ String expected = new StringBuilder()
+ .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 )
+ .append( (char) 0 )
+ .append( (char) 0 )
+ .append( (char) 9 )
+ .append( ":" )
+ .append( "pkg.ATest" )
+ .append( ":" )
+ .append( ":maven-surefire-command:" )
+ .append( (char) 16 )
+ .append( ":testset-finished:" )
+ .toString();
+
assertThat( commands )
- .isEqualTo( ":maven-surefire-command:run-testclass:pkg.ATest::maven-surefire-command:testset-finished:" );
+ .isEqualTo( expected );
verify( channel, times( 1 ) )
.close();
@@ -147,16 +166,4 @@ public class StreamFeederTest
verifyZeroInteractions( logger );
}
-
- @Test( expected = IllegalArgumentException.class )
- public void shouldFailWithoutData()
- {
- StreamFeeder.encode( RUN_CLASS );
- }
-
- @Test( expected = IllegalArgumentException.class )
- public void shouldFailWithData()
- {
- StreamFeeder.encode( NOOP, "" );
- }
}
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
index 7692a04..dd1f08d 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
@@ -62,6 +62,7 @@ import org.apache.maven.surefire.extensions.StatelessTestsetInfoReporterTest;
import org.apache.maven.surefire.report.FileReporterTest;
import org.apache.maven.surefire.report.RunStatisticsTest;
import org.apache.maven.surefire.spi.SPITest;
+import org.apache.maven.surefire.stream.EventDecoderTest;
import org.apache.maven.surefire.util.RelocatorTest;
/**
@@ -115,6 +116,7 @@ public class JUnit4SuiteTest extends TestCase
suite.addTest( new JUnit4TestAdapter( StreamFeederTest.class ) );
suite.addTest( new JUnit4TestAdapter( E2ETest.class ) );
suite.addTest( new JUnit4TestAdapter( ThreadedStreamConsumerTest.class ) );
+ suite.addTest( new JUnit4TestAdapter( EventDecoderTest.class ) );
suite.addTest( new JUnit4TestAdapter( EventConsumerThreadTest.class ) );
return suite;
}
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 8caa668..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
@@ -26,6 +26,7 @@ import org.apache.maven.plugin.surefire.extensions.SurefireForkNodeFactory;
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.surefire.api.event.ControlByeEvent;
import org.apache.maven.surefire.api.event.Event;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
import org.junit.Test;
@@ -67,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()
@@ -203,7 +216,7 @@ public class ForkChannelTest
byte[] data = new byte[128];
int readLength = socket.getInputStream().read( data );
String token = new String( data, 0, readLength, US_ASCII );
- assertThat( token ).isEqualTo( ":maven-surefire-command:noop:" );
+ assertThat( token ).isEqualTo( ":maven-surefire-command:\u0004:noop:" );
socket.getOutputStream().write( ":maven-surefire-event:\u0003:bye:".getBytes( US_ASCII ) );
}
catch ( IOException e )
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
new file mode 100644
index 0000000..ccfd3f0
--- /dev/null
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java
@@ -0,0 +1,785 @@
+package org.apache.maven.surefire.stream;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.api.booter.ForkedProcessEventType;
+import org.apache.maven.surefire.api.event.ConsoleDebugEvent;
+import org.apache.maven.surefire.api.event.ConsoleErrorEvent;
+import org.apache.maven.surefire.api.event.ConsoleInfoEvent;
+import org.apache.maven.surefire.api.event.ConsoleWarningEvent;
+import org.apache.maven.surefire.api.event.ControlByeEvent;
+import org.apache.maven.surefire.api.event.ControlNextTestEvent;
+import org.apache.maven.surefire.api.event.ControlStopOnNextTestEvent;
+import org.apache.maven.surefire.api.event.Event;
+import org.apache.maven.surefire.api.event.JvmExitErrorEvent;
+import org.apache.maven.surefire.api.event.StandardStreamErrEvent;
+import org.apache.maven.surefire.api.event.StandardStreamErrWithNewLineEvent;
+import org.apache.maven.surefire.api.event.StandardStreamOutEvent;
+import org.apache.maven.surefire.api.event.StandardStreamOutWithNewLineEvent;
+import org.apache.maven.surefire.api.event.SystemPropertyEvent;
+import org.apache.maven.surefire.api.event.TestAssumptionFailureEvent;
+import org.apache.maven.surefire.api.event.TestErrorEvent;
+import org.apache.maven.surefire.api.event.TestFailedEvent;
+import org.apache.maven.surefire.api.event.TestSkippedEvent;
+import org.apache.maven.surefire.api.event.TestStartingEvent;
+import org.apache.maven.surefire.api.event.TestSucceededEvent;
+import org.apache.maven.surefire.api.event.TestsetCompletedEvent;
+import org.apache.maven.surefire.api.event.TestsetStartingEvent;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.ReportEntry;
+import org.apache.maven.surefire.api.report.RunMode;
+import org.apache.maven.surefire.api.report.SafeThrowable;
+import org.apache.maven.surefire.api.report.StackTraceWriter;
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.Memento;
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.Segment;
+import org.apache.maven.surefire.api.stream.SegmentType;
+import org.junit.Test;
+
+import javax.annotation.Nonnull;
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Map;
+
+import static java.lang.Math.min;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_BYE;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_DEBUG;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_ERROR;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_INFO;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_WARNING;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_JVM_EXIT_ERROR;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_NEXT_TEST;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDERR;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDERR_NEW_LINE;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT_NEW_LINE;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STOP_ON_NEXT_TEST;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_SYSPROPS;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_COMPLETED;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_STARTING;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ASSUMPTIONFAILURE;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ERROR;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_FAILED;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SKIPPED;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_STARTING;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SUCCEEDED;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
+import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAILURE;
+import static org.apache.maven.surefire.api.stream.SegmentType.DATA_INTEGER;
+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;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.when;
+import static org.powermock.reflect.Whitebox.invokeMethod;
+
+/**
+ *
+ */
+public class EventDecoderTest
+{
+ @Test
+ public void shouldMapEventTypes() throws Exception
+ {
+ Map<Segment, ForkedProcessEventType> eventTypes = invokeMethod( EventDecoder.class, "segmentsToEvents" );
+ assertThat( eventTypes )
+ .hasSize( ForkedProcessEventType.values().length );
+ }
+
+ @Test
+ public void shouldMapRunModes() throws Exception
+ {
+ Map<Segment, RunMode> map = invokeMethod( EventDecoder.class, "segmentsToRunModes" );
+
+ assertThat( map )
+ .hasSize( 2 );
+
+ byte[] stream = "normal-run".getBytes( US_ASCII );
+ Segment segment = new Segment( stream, 0, stream.length );
+ assertThat( map.get( segment ) )
+ .isEqualTo( NORMAL_RUN );
+
+ stream = "rerun-test-after-failure".getBytes( US_ASCII );
+ segment = new Segment( stream, 0, stream.length );
+ assertThat( map.get( segment ) )
+ .isEqualTo( RERUN_TEST_AFTER_FAILURE );
+ }
+
+ @Test
+ public void shouldMapEventTypeToSegmentType()
+ {
+ byte[] stream = {};
+ Channel channel = new Channel( stream, 1 );
+ EventDecoder decoder = new EventDecoder( channel, new MockForkNodeArguments() );
+
+ SegmentType[] segmentTypes = decoder.nextSegmentType( BOOTERCODE_BYE );
+ assertThat( segmentTypes )
+ .hasSize( 1 )
+ .containsOnly( END_OF_FRAME );
+
+ segmentTypes = decoder.nextSegmentType( BOOTERCODE_STOP_ON_NEXT_TEST );
+ assertThat( segmentTypes )
+ .hasSize( 1 )
+ .containsOnly( END_OF_FRAME );
+
+ segmentTypes = decoder.nextSegmentType( BOOTERCODE_NEXT_TEST );
+ assertThat( segmentTypes )
+ .hasSize( 1 )
+ .containsOnly( END_OF_FRAME );
+
+ segmentTypes = decoder.nextSegmentType( BOOTERCODE_CONSOLE_ERROR );
+ assertThat( segmentTypes )
+ .hasSize( 5 )
+ .isEqualTo( new SegmentType[] { STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_JVM_EXIT_ERROR );
+ assertThat( segmentTypes )
+ .hasSize( 5 )
+ .isEqualTo( new SegmentType[] { STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( BOOTERCODE_CONSOLE_INFO );
+ assertThat( segmentTypes )
+ .hasSize( 3 )
+ .isEqualTo( new SegmentType[] { STRING_ENCODING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_CONSOLE_DEBUG );
+ assertThat( segmentTypes )
+ .hasSize( 3 )
+ .isEqualTo( new SegmentType[] { STRING_ENCODING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_CONSOLE_WARNING );
+ assertThat( segmentTypes )
+ .hasSize( 3 )
+ .isEqualTo( new SegmentType[] { STRING_ENCODING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( BOOTERCODE_STDOUT );
+ assertThat( segmentTypes )
+ .hasSize( 4 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_STDOUT_NEW_LINE );
+ assertThat( segmentTypes )
+ .hasSize( 4 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_STDERR );
+ assertThat( segmentTypes )
+ .hasSize( 4 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_STDERR_NEW_LINE );
+ assertThat( segmentTypes )
+ .hasSize( 4 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( BOOTERCODE_SYSPROPS );
+ assertThat( segmentTypes )
+ .hasSize( 5 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( BOOTERCODE_TESTSET_STARTING );
+ assertThat( segmentTypes )
+ .hasSize( 13 )
+ .isEqualTo( new SegmentType[] {
+ RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING,
+ DATA_STRING, DATA_INTEGER, DATA_STRING, DATA_STRING, DATA_STRING, END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_TESTSET_COMPLETED );
+ assertThat( segmentTypes )
+ .hasSize( 13 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING,
+ DATA_STRING, DATA_STRING, DATA_STRING, DATA_INTEGER, DATA_STRING, DATA_STRING, DATA_STRING,
+ END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_TEST_STARTING );
+ assertThat( segmentTypes )
+ .hasSize( 13 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING,
+ DATA_STRING, DATA_STRING, DATA_STRING, DATA_INTEGER, DATA_STRING, DATA_STRING, DATA_STRING,
+ END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( BOOTERCODE_TEST_SUCCEEDED );
+ assertThat( segmentTypes )
+ .hasSize( 13 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING,
+ DATA_STRING, DATA_STRING, DATA_STRING, DATA_INTEGER, DATA_STRING, DATA_STRING, DATA_STRING,
+ END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( BOOTERCODE_TEST_FAILED );
+ assertThat( segmentTypes )
+ .hasSize( 13 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING,
+ DATA_STRING, DATA_STRING, DATA_STRING, DATA_INTEGER, DATA_STRING, DATA_STRING, DATA_STRING,
+ END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_TEST_SKIPPED );
+ assertThat( segmentTypes )
+ .hasSize( 13 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING,
+ DATA_STRING, DATA_STRING, DATA_STRING, DATA_INTEGER, DATA_STRING, DATA_STRING, DATA_STRING,
+ END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_TEST_ERROR );
+ assertThat( segmentTypes )
+ .hasSize( 13 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING,
+ DATA_STRING, DATA_STRING, DATA_STRING, DATA_INTEGER, DATA_STRING, DATA_STRING, DATA_STRING,
+ END_OF_FRAME } );
+
+ segmentTypes = decoder.nextSegmentType( ForkedProcessEventType.BOOTERCODE_TEST_ASSUMPTIONFAILURE );
+ assertThat( segmentTypes )
+ .hasSize( 13 )
+ .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING,
+ DATA_STRING, DATA_STRING, DATA_STRING, DATA_INTEGER, DATA_STRING, DATA_STRING, DATA_STRING,
+ END_OF_FRAME } );
+ }
+
+ @Test
+ public void shouldCreateEvent() throws Exception
+ {
+ byte[] stream = {};
+ Channel channel = new Channel( stream, 1 );
+ EventDecoder decoder = new EventDecoder( channel, new MockForkNodeArguments() );
+
+ Event event = decoder.toMessage( BOOTERCODE_BYE, NORMAL_RUN, decoder.new Memento() );
+ assertThat( event )
+ .isInstanceOf( ControlByeEvent.class );
+
+ event = decoder.toMessage( BOOTERCODE_STOP_ON_NEXT_TEST, NORMAL_RUN, decoder.new Memento() );
+ assertThat( event )
+ .isInstanceOf( ControlStopOnNextTestEvent.class );
+
+ event = decoder.toMessage( BOOTERCODE_NEXT_TEST, NORMAL_RUN, decoder.new Memento() );
+ assertThat( event )
+ .isInstanceOf( ControlNextTestEvent.class );
+
+ Memento memento = decoder.new Memento();
+ memento.getData().addAll( asList( "1", "2", "3" ) );
+ event = decoder.toMessage( BOOTERCODE_CONSOLE_ERROR, NORMAL_RUN, memento );
+ assertThat( event )
+ .isInstanceOf( ConsoleErrorEvent.class );
+ ConsoleErrorEvent consoleErrorEvent = (ConsoleErrorEvent) event;
+ assertThat( consoleErrorEvent.getStackTraceWriter().getThrowable().getLocalizedMessage() )
+ .isEqualTo( "1" );
+ assertThat( consoleErrorEvent.getStackTraceWriter().smartTrimmedStackTrace() )
+ .isEqualTo( "2" );
+ assertThat( consoleErrorEvent.getStackTraceWriter().writeTraceToString() )
+ .isEqualTo( "3" );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( asList( null, null, null ) );
+ event = decoder.toMessage( BOOTERCODE_CONSOLE_ERROR, NORMAL_RUN, memento );
+ assertThat( event )
+ .isInstanceOf( ConsoleErrorEvent.class );
+ consoleErrorEvent = (ConsoleErrorEvent) event;
+ assertThat( consoleErrorEvent.getStackTraceWriter() )
+ .isNull();
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( asList( "1", "2", "3" ) );
+ event = decoder.toMessage( BOOTERCODE_JVM_EXIT_ERROR, NORMAL_RUN, memento );
+ assertThat( event )
+ .isInstanceOf( JvmExitErrorEvent.class );
+ JvmExitErrorEvent jvmExitErrorEvent = (JvmExitErrorEvent) event;
+ assertThat( jvmExitErrorEvent.getStackTraceWriter().getThrowable().getLocalizedMessage() )
+ .isEqualTo( "1" );
+ assertThat( jvmExitErrorEvent.getStackTraceWriter().smartTrimmedStackTrace() )
+ .isEqualTo( "2" );
+ assertThat( jvmExitErrorEvent.getStackTraceWriter().writeTraceToString() )
+ .isEqualTo( "3" );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( asList( null, null, null ) );
+ event = decoder.toMessage( BOOTERCODE_JVM_EXIT_ERROR, NORMAL_RUN, memento );
+ assertThat( event )
+ .isInstanceOf( JvmExitErrorEvent.class );
+ jvmExitErrorEvent = (JvmExitErrorEvent) event;
+ assertThat( jvmExitErrorEvent.getStackTraceWriter() )
+ .isNull();
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( singletonList( "m" ) );
+ event = decoder.toMessage( BOOTERCODE_CONSOLE_INFO, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( ConsoleInfoEvent.class );
+ assertThat( ( (ConsoleInfoEvent) event ).getMessage() ).isEqualTo( "m" );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( singletonList( "" ) );
+ event = decoder.toMessage( BOOTERCODE_CONSOLE_WARNING, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( ConsoleWarningEvent.class );
+ assertThat( ( (ConsoleWarningEvent) event ).getMessage() ).isEmpty();
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( singletonList( null ) );
+ event = decoder.toMessage( BOOTERCODE_CONSOLE_DEBUG, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( ConsoleDebugEvent.class );
+ assertThat( ( (ConsoleDebugEvent) event ).getMessage() ).isNull();
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( singletonList( "m" ) );
+ event = decoder.toMessage( BOOTERCODE_STDOUT, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( StandardStreamOutEvent.class );
+ assertThat( ( (StandardStreamOutEvent) event ).getMessage() ).isEqualTo( "m" );
+ assertThat( ( (StandardStreamOutEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( singletonList( null ) );
+ event = decoder.toMessage( BOOTERCODE_STDOUT_NEW_LINE, RERUN_TEST_AFTER_FAILURE, memento );
+ assertThat( event ).isInstanceOf( StandardStreamOutWithNewLineEvent.class );
+ assertThat( ( (StandardStreamOutWithNewLineEvent) event ).getMessage() ).isNull();
+ assertThat( ( (StandardStreamOutWithNewLineEvent) event ).getRunMode() ).isEqualTo( RERUN_TEST_AFTER_FAILURE );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( singletonList( null ) );
+ event = decoder.toMessage( BOOTERCODE_STDERR, RERUN_TEST_AFTER_FAILURE, memento );
+ assertThat( event ).isInstanceOf( StandardStreamErrEvent.class );
+ assertThat( ( (StandardStreamErrEvent) event ).getMessage() ).isNull();
+ assertThat( ( (StandardStreamErrEvent) event ).getRunMode() ).isEqualTo( RERUN_TEST_AFTER_FAILURE );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( singletonList( "abc" ) );
+ event = decoder.toMessage( BOOTERCODE_STDERR_NEW_LINE, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( StandardStreamErrWithNewLineEvent.class );
+ assertThat( ( (StandardStreamErrWithNewLineEvent) event ).getMessage() ).isEqualTo( "abc" );
+ assertThat( ( (StandardStreamErrWithNewLineEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( asList( "key", "value" ) );
+ event = decoder.toMessage( BOOTERCODE_SYSPROPS, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( SystemPropertyEvent.class );
+ assertThat( ( (SystemPropertyEvent) event ).getKey() ).isEqualTo( "key" );
+ assertThat( ( (SystemPropertyEvent) event ).getValue() ).isEqualTo( "value" );
+ assertThat( ( (SystemPropertyEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( asList( "source", "sourceText", "name", "nameText", "group", "message", 5,
+ "traceMessage", "smartTrimmedStackTrace", "stackTrace" ) );
+ event = decoder.toMessage( BOOTERCODE_TESTSET_STARTING, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( TestsetStartingEvent.class );
+ assertThat( ( (TestsetStartingEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+ assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
+ assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
+ assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
+ assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getNameText() ).isEqualTo( "nameText" );
+ assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
+ assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getMessage() ).isEqualTo( "message" );
+ assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
+ assertThat( ( (TestsetStartingEvent) event )
+ .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
+ .isEqualTo( "traceMessage" );
+ assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
+ .isEqualTo( "smartTrimmedStackTrace" );
+ assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getStackTraceWriter().writeTraceToString() )
+ .isEqualTo( "stackTrace" );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( asList( "source", "sourceText", "name", "nameText", "group", null, 5,
+ "traceMessage", "smartTrimmedStackTrace", "stackTrace" ) );
+ event = decoder.toMessage( BOOTERCODE_TESTSET_COMPLETED, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( TestsetCompletedEvent.class );
+ assertThat( ( (TestsetCompletedEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+ assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
+ assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
+ assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
+ assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getNameText() ).isEqualTo( "nameText" );
+ assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
+ assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getMessage() ).isNull();
+ assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
+ assertThat( ( (TestsetCompletedEvent) event )
+ .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
+ .isEqualTo( "traceMessage" );
+ assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
+ .isEqualTo( "smartTrimmedStackTrace" );
+ assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getStackTraceWriter().writeTraceToString() )
+ .isEqualTo( "stackTrace" );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( asList( "source", "sourceText", "name", "nameText", "group", "message", 5,
+ null, "smartTrimmedStackTrace", "stackTrace" ) );
+ event = decoder.toMessage( BOOTERCODE_TEST_STARTING, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( TestStartingEvent.class );
+ assertThat( ( (TestStartingEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+ assertThat( ( (TestStartingEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
+ assertThat( ( (TestStartingEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
+ assertThat( ( (TestStartingEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
+ assertThat( ( (TestStartingEvent) event ).getReportEntry().getNameText() ).isEqualTo( "nameText" );
+ assertThat( ( (TestStartingEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
+ assertThat( ( (TestStartingEvent) event ).getReportEntry().getMessage() ).isEqualTo( "message" );
+ assertThat( ( (TestStartingEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
+ assertThat( ( (TestStartingEvent) event )
+ .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
+ .isNull();
+ assertThat( ( (TestStartingEvent) event ).getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
+ .isEqualTo( "smartTrimmedStackTrace" );
+ assertThat( ( (TestStartingEvent) event ).getReportEntry().getStackTraceWriter().writeTraceToString() )
+ .isEqualTo( "stackTrace" );
+
+ memento = decoder.new Memento();
+ memento.getData()
+ .addAll( asList( "source", "sourceText", "name", "nameText", "group", "message", 5, null, null, null ) );
+ event = decoder.toMessage( BOOTERCODE_TEST_SUCCEEDED, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( TestSucceededEvent.class );
+ assertThat( ( (TestSucceededEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+ assertThat( ( (TestSucceededEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
+ assertThat( ( (TestSucceededEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
+ assertThat( ( (TestSucceededEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
+ assertThat( ( (TestSucceededEvent) event ).getReportEntry().getNameText() ).isEqualTo( "nameText" );
+ assertThat( ( (TestSucceededEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
+ assertThat( ( (TestSucceededEvent) event ).getReportEntry().getMessage() ).isEqualTo( "message" );
+ assertThat( ( (TestSucceededEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
+ assertThat( ( (TestSucceededEvent) event ).getReportEntry().getStackTraceWriter() ).isNull();
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( asList( "source", null, "name", null, "group", null, 5,
+ "traceMessage", "smartTrimmedStackTrace", "stackTrace" ) );
+ event = decoder.toMessage( BOOTERCODE_TEST_FAILED, RERUN_TEST_AFTER_FAILURE, memento );
+ assertThat( event ).isInstanceOf( TestFailedEvent.class );
+ assertThat( ( (TestFailedEvent) event ).getRunMode() ).isEqualTo( RERUN_TEST_AFTER_FAILURE );
+ assertThat( ( (TestFailedEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
+ assertThat( ( (TestFailedEvent) event ).getReportEntry().getSourceText() ).isNull();
+ assertThat( ( (TestFailedEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
+ assertThat( ( (TestFailedEvent) event ).getReportEntry().getNameText() ).isNull();
+ assertThat( ( (TestFailedEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
+ assertThat( ( (TestFailedEvent) event ).getReportEntry().getMessage() ).isNull();
+ assertThat( ( (TestFailedEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
+ assertThat( ( (TestFailedEvent) event )
+ .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
+ .isEqualTo( "traceMessage" );
+ assertThat( ( (TestFailedEvent) event ).getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
+ .isEqualTo( "smartTrimmedStackTrace" );
+ assertThat( ( (TestFailedEvent) event ).getReportEntry().getStackTraceWriter().writeTraceToString() )
+ .isEqualTo( "stackTrace" );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( asList( "source", null, "name", null, null, null, 5, null, null, "stackTrace" ) );
+ event = decoder.toMessage( BOOTERCODE_TEST_SKIPPED, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( TestSkippedEvent.class );
+ assertThat( ( (TestSkippedEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+ assertThat( ( (TestSkippedEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
+ assertThat( ( (TestSkippedEvent) event ).getReportEntry().getSourceText() ).isNull();
+ assertThat( ( (TestSkippedEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
+ assertThat( ( (TestSkippedEvent) event ).getReportEntry().getNameText() ).isNull();
+ assertThat( ( (TestSkippedEvent) event ).getReportEntry().getGroup() ).isNull();
+ assertThat( ( (TestSkippedEvent) event ).getReportEntry().getMessage() ).isNull();
+ assertThat( ( (TestSkippedEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
+ assertThat( ( (TestSkippedEvent) event )
+ .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
+ .isNull();
+ assertThat( ( (TestSkippedEvent) event )
+ .getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
+ .isNull();
+ assertThat( ( (TestSkippedEvent) event )
+ .getReportEntry().getStackTraceWriter().writeTraceToString() )
+ .isEqualTo( "stackTrace" );
+
+ memento = decoder.new Memento();
+ memento.getData()
+ .addAll( asList( "source", null, "name", "nameText", null, null, 0, null, null, "stackTrace" ) );
+ event = decoder.toMessage( BOOTERCODE_TEST_ERROR, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( TestErrorEvent.class );
+ assertThat( ( (TestErrorEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+ assertThat( ( (TestErrorEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
+ assertThat( ( (TestErrorEvent) event ).getReportEntry().getSourceText() ).isNull();
+ assertThat( ( (TestErrorEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
+ assertThat( ( (TestErrorEvent) event ).getReportEntry().getNameText() ).isEqualTo( "nameText" );
+ assertThat( ( (TestErrorEvent) event ).getReportEntry().getGroup() ).isNull();
+ assertThat( ( (TestErrorEvent) event ).getReportEntry().getMessage() ).isNull();
+ assertThat( ( (TestErrorEvent) event ).getReportEntry().getElapsed() ).isZero();
+ assertThat( ( (TestErrorEvent) event )
+ .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
+ .isNull();
+ assertThat( ( (TestErrorEvent) event )
+ .getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
+ .isNull();
+ assertThat( ( (TestErrorEvent) event )
+ .getReportEntry().getStackTraceWriter().writeTraceToString() )
+ .isEqualTo( "stackTrace" );
+
+ memento = decoder.new Memento();
+ memento.getData().addAll( asList( "source", null, "name", null, "group", null, 5, null, null, "stackTrace" ) );
+ event = decoder.toMessage( BOOTERCODE_TEST_ASSUMPTIONFAILURE, NORMAL_RUN, memento );
+ assertThat( event ).isInstanceOf( TestAssumptionFailureEvent.class );
+ assertThat( ( (TestAssumptionFailureEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+ assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
+ assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getSourceText() ).isNull();
+ assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
+ assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getNameText() ).isNull();
+ assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getGroup() ).isEqualTo( "group" );
+ assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getMessage() ).isNull();
+ assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getElapsed() ).isEqualTo( 5 );
+ assertThat( ( (TestAssumptionFailureEvent) event )
+ .getReportEntry().getStackTraceWriter().getThrowable().getLocalizedMessage() )
+ .isNull();
+ assertThat( ( (TestAssumptionFailureEvent) event )
+ .getReportEntry().getStackTraceWriter().smartTrimmedStackTrace() )
+ .isNull();
+ assertThat( ( (TestAssumptionFailureEvent) event )
+ .getReportEntry().getStackTraceWriter().writeTraceToString() )
+ .isEqualTo( "stackTrace" );
+ }
+
+ @Test
+ public void shouldRecognizeEmptyStream4ReportEntry()
+ {
+ ReportEntry reportEntry = EventDecoder.newReportEntry( "", "", "", "", "", "", null, "", "", "" );
+ assertThat( reportEntry ).isNotNull();
+ assertThat( reportEntry.getStackTraceWriter() ).isNotNull();
+ assertThat( reportEntry.getStackTraceWriter().smartTrimmedStackTrace() ).isEmpty();
+ assertThat( reportEntry.getStackTraceWriter().writeTraceToString() ).isEmpty();
+ assertThat( reportEntry.getStackTraceWriter().writeTrimmedTraceToString() ).isEmpty();
+ assertThat( reportEntry.getSourceName() ).isEmpty();
+ assertThat( reportEntry.getSourceText() ).isEmpty();
+ assertThat( reportEntry.getName() ).isEmpty();
+ assertThat( reportEntry.getNameText() ).isEmpty();
+ assertThat( reportEntry.getGroup() ).isEmpty();
+ assertThat( reportEntry.getNameWithGroup() ).isEmpty();
+ assertThat( reportEntry.getMessage() ).isEmpty();
+ assertThat( reportEntry.getElapsed() ).isNull();
+ }
+
+ @Test
+ @SuppressWarnings( "checkstyle:magicnumber" )
+ public void testCreatingReportEntry()
+ {
+ final String exceptionMessage = "msg";
+ final String smartStackTrace = "MyTest:86 >> Error";
+ final String stackTrace = "Exception: msg\ntrace line 1\ntrace line 2";
+ final String trimmedStackTrace = "trace line 1\ntrace line 2";
+
+ SafeThrowable safeThrowable = new SafeThrowable( exceptionMessage );
+ StackTraceWriter stackTraceWriter = mock( StackTraceWriter.class );
+ when( stackTraceWriter.getThrowable() ).thenReturn( safeThrowable );
+ when( stackTraceWriter.smartTrimmedStackTrace() ).thenReturn( smartStackTrace );
+ when( stackTraceWriter.writeTrimmedTraceToString() ).thenReturn( trimmedStackTrace );
+ when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
+
+ ReportEntry reportEntry = mock( ReportEntry.class );
+ when( reportEntry.getElapsed() ).thenReturn( 102 );
+ when( reportEntry.getGroup() ).thenReturn( "this group" );
+ when( reportEntry.getMessage() ).thenReturn( "skipped test" );
+ when( reportEntry.getName() ).thenReturn( "my test" );
+ when( reportEntry.getNameText() ).thenReturn( "my display name" );
+ when( reportEntry.getNameWithGroup() ).thenReturn( "name with group" );
+ when( reportEntry.getSourceName() ).thenReturn( "pkg.MyTest" );
+ when( reportEntry.getSourceText() ).thenReturn( "test class display name" );
+ when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
+
+ ReportEntry decodedReportEntry = EventDecoder.newReportEntry( reportEntry.getSourceName(),
+ reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
+ reportEntry.getMessage(), null, null, null, null );
+
+ assertThat( decodedReportEntry ).isNotNull();
+ assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
+ assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
+ assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
+ assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
+ assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
+ assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
+ assertThat( decodedReportEntry.getStackTraceWriter() ).isNull();
+
+ decodedReportEntry = EventDecoder.newReportEntry( reportEntry.getSourceName(),
+ reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
+ reportEntry.getMessage(), null, exceptionMessage, smartStackTrace, null );
+
+ assertThat( decodedReportEntry ).isNotNull();
+ assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
+ assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
+ assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
+ assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
+ assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
+ assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
+ assertThat( decodedReportEntry.getElapsed() ).isNull();
+ assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
+ assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
+ .isEqualTo( exceptionMessage );
+ assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
+ .isEqualTo( smartStackTrace );
+ assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() )
+ .isNull();
+
+ decodedReportEntry = EventDecoder.newReportEntry( reportEntry.getSourceName(),
+ reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
+ reportEntry.getMessage(), 1003, exceptionMessage, smartStackTrace, null );
+
+ assertThat( decodedReportEntry ).isNotNull();
+ assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
+ assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
+ assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
+ assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
+ assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
+ assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
+ assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
+ assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
+ assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
+ .isEqualTo( exceptionMessage );
+ assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
+ .isEqualTo( smartStackTrace );
+ assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() )
+ .isNull();
+
+ decodedReportEntry = EventDecoder.newReportEntry( reportEntry.getSourceName(),
+ reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
+ reportEntry.getMessage(), 1003, exceptionMessage, smartStackTrace, stackTrace );
+
+ assertThat( decodedReportEntry ).isNotNull();
+ assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
+ assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
+ assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
+ assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
+ assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
+ assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
+ assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
+ assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
+ assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() ).isNotNull();
+ assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
+ .isEqualTo( exceptionMessage );
+ assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
+ .isEqualTo( smartStackTrace );
+ assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() ).isEqualTo( stackTrace );
+ assertThat( decodedReportEntry.getStackTraceWriter().writeTrimmedTraceToString() ).isEqualTo( stackTrace );
+
+ decodedReportEntry = EventDecoder.newReportEntry( reportEntry.getSourceName(),
+ reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
+ reportEntry.getMessage(), 1003, exceptionMessage, smartStackTrace, trimmedStackTrace );
+
+ assertThat( decodedReportEntry ).isNotNull();
+ assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
+ assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
+ assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
+ assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
+ assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
+ assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
+ assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
+ assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
+ assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() ).isNotNull();
+ assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
+ .isEqualTo( exceptionMessage );
+ assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
+ .isEqualTo( smartStackTrace );
+ assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() ).isEqualTo( trimmedStackTrace );
+ assertThat( decodedReportEntry.getStackTraceWriter().writeTrimmedTraceToString() )
+ .isEqualTo( trimmedStackTrace );
+ }
+
+ private static class Channel implements ReadableByteChannel
+ {
+ private final byte[] bytes;
+ private final int chunkSize;
+ protected int i;
+
+ Channel( byte[] bytes, int chunkSize )
+ {
+ this.bytes = bytes;
+ this.chunkSize = chunkSize;
+ }
+
+ @Override
+ public int read( ByteBuffer dst )
+ {
+ if ( i == bytes.length )
+ {
+ return -1;
+ }
+ else if ( dst.hasRemaining() )
+ {
+ int length = min( min( chunkSize, bytes.length - i ), dst.remaining() ) ;
+ dst.put( bytes, i, length );
+ i += length;
+ return length;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ @Override
+ public boolean isOpen()
+ {
+ return false;
+ }
+
+ @Override
+ public void close()
+ {
+ }
+ }
+
+ private static class MockForkNodeArguments implements ForkNodeArguments
+ {
+ @Nonnull
+ @Override
+ public String getSessionId()
+ {
+ return null;
+ }
+
+ @Override
+ public int getForkChannelId()
+ {
+ return 0;
+ }
+
+ @Nonnull
+ @Override
+ public File dumpStreamText( @Nonnull String text )
+ {
+ return null;
+ }
+
+ @Nonnull
+ @Override
+ public File dumpStreamException( @Nonnull Throwable t )
+ {
+ return null;
+ }
+
+ @Override
+ public void logWarningAtEnd( @Nonnull String text )
+ {
+ }
+
+ @Nonnull
+ @Override
+ public ConsoleLogger getConsoleLogger()
+ {
+ return null;
+ }
+
+ @Override
+ public File getEventStreamBinaryFile()
+ {
+ return null;
+ }
+
+ @Override
+ public File getCommandStreamBinaryFile()
+ {
+ return null;
+ }
+ }
+
+}
diff --git a/maven-surefire-plugin/src/site/fml/faq.fml b/maven-surefire-plugin/src/site/fml/faq.fml
index e5aaada..2464d08 100644
--- a/maven-surefire-plugin/src/site/fml/faq.fml
+++ b/maven-surefire-plugin/src/site/fml/faq.fml
@@ -138,7 +138,7 @@ under the License.
</answer>
</faq>
<faq id="corruptedstream">
- <question>Corrupted STDOUT by directly writing to native stream in forked JVM</question>
+ <question>Corrupted channel by directly writing to native stream in forked JVM</question>
<answer>
<p>
If your tests use native library which prints to STDOUT this warning message appears because the library
@@ -149,7 +149,7 @@ under the License.
This warning message appears if you use <em>FileDescriptor.out</em> or JVM prints GC summary.
<br/>
In that case the warning is printed
- <em>"Corrupted STDOUT by directly writing to native stream in forked JVM"</em>, and a dump file can be found
+ <em>"Corrupted channel by directly writing to native stream in forked JVM"</em>, and a dump file can be found
in Reports directory.
<br/>
If debug level is enabled then messages of corrupted stream appear in the console.
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/Constants.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/Constants.java
index bcb7b17..639aba8 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/Constants.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/Constants.java
@@ -29,8 +29,10 @@ import static java.nio.charset.StandardCharsets.UTF_8;
*/
public final class Constants
{
- public static final String MAGIC_NUMBER = "maven-surefire-event";
- public static final byte[] MAGIC_NUMBER_BYTES = MAGIC_NUMBER.getBytes( US_ASCII );
+ private static final String MAGIC_NUMBER_FOR_EVENTS = "maven-surefire-event";
+ public static final String MAGIC_NUMBER_FOR_COMMANDS = "maven-surefire-command";
+ public static final byte[] MAGIC_NUMBER_FOR_EVENTS_BYTES = MAGIC_NUMBER_FOR_EVENTS.getBytes( US_ASCII );
+ public static final byte[] MAGIC_NUMBER_FOR_COMMANDS_BYTES = MAGIC_NUMBER_FOR_COMMANDS.getBytes( US_ASCII );
public static final Charset DEFAULT_STREAM_ENCODING = UTF_8;
public static final byte[] DEFAULT_STREAM_ENCODING_BYTES = UTF_8.name().getBytes( US_ASCII );
}
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 b54bb2e..791dae5 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,36 +55,50 @@ public final class DumpErrorSingleton
{
dumpFile = createDumpFile( reportsDir, dumpFileName );
dumpStreamFile = createDumpStreamFile( reportsDir, dumpFileName );
+ String fileNameWithoutExtension =
+ dumpFileName.contains( "." ) ? dumpFileName.substring( 0, dumpFileName.lastIndexOf( '.' ) ) : dumpFileName;
+ binaryDumpStreamFile = newDumpFile( reportsDir, fileNameWithoutExtension + "-commands.bin" );
}
- public synchronized void dumpException( Throwable t, String msg )
+ public synchronized File dumpException( Throwable t, String msg )
{
DumpFileUtils.dumpException( t, msg == null ? "null" : msg, dumpFile );
+ return dumpFile;
}
- public synchronized void dumpException( Throwable t )
+ public synchronized File dumpException( Throwable t )
{
DumpFileUtils.dumpException( t, dumpFile );
+ return dumpFile;
}
- public synchronized void dumpText( String msg )
+ public synchronized File dumpText( String msg )
{
DumpFileUtils.dumpText( msg == null ? "null" : msg, dumpFile );
+ return dumpFile;
}
- public synchronized void dumpStreamException( Throwable t, String msg )
+ public synchronized File dumpStreamException( Throwable t, String msg )
{
DumpFileUtils.dumpException( t, msg == null ? "null" : msg, dumpStreamFile );
+ return dumpStreamFile;
}
- public synchronized void dumpStreamException( Throwable t )
+ public synchronized File dumpStreamException( Throwable t )
{
DumpFileUtils.dumpException( t, dumpStreamFile );
+ return dumpStreamFile;
}
- public synchronized void dumpStreamText( String msg )
+ public synchronized File dumpStreamText( String msg )
{
DumpFileUtils.dumpText( msg == null ? "null" : msg, dumpStreamFile );
+ return dumpStreamFile;
+ }
+
+ public File getCommandStreamBinaryFile()
+ {
+ return binaryDumpStreamFile;
}
private File createDumpFile( File reportsDir, String dumpFileName )
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkingRunListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkingRunListener.java
index 7cf1dea..169f358 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkingRunListener.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkingRunListener.java
@@ -71,7 +71,7 @@ public class ForkingRunListener
@Override
public void testSetCompleted( TestSetReportEntry report )
{
- target.sendSystemProperties( report.getSystemProperties() );
+ target.systemProperties( report.getSystemProperties() );
target.testSetCompleted( report, trim );
}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/MasterProcessChannelEncoder.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/MasterProcessChannelEncoder.java
index e8ad45b..cb543f6 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/MasterProcessChannelEncoder.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/MasterProcessChannelEncoder.java
@@ -32,15 +32,11 @@ import java.util.Map;
*/
public interface MasterProcessChannelEncoder
{
- MasterProcessChannelEncoder asRerunMode();
-
- MasterProcessChannelEncoder asNormalMode();
-
boolean checkError();
void onJvmExit();
- void sendSystemProperties( Map<String, String> sysProps );
+ void systemProperties( Map<String, String> sysProps );
void testSetStarting( ReportEntry reportEntry, boolean trimStackTraces );
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/MasterProcessCommand.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/MasterProcessCommand.java
index 64c392d..f492339 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/MasterProcessCommand.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/MasterProcessCommand.java
@@ -19,6 +19,12 @@ package org.apache.maven.surefire.api.booter;
* under the License.
*/
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.Segment;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.util.Objects.requireNonNull;
/**
@@ -29,24 +35,39 @@ import static java.util.Objects.requireNonNull;
*/
public enum MasterProcessCommand
{
- RUN_CLASS( String.class ),
- TEST_SET_FINISHED( Void.class ),
- SKIP_SINCE_NEXT_TEST( Void.class ),
- SHUTDOWN( String.class ),
+ RUN_CLASS( "run-testclass", String.class ),
+ TEST_SET_FINISHED( "testset-finished", Void.class ),
+ SKIP_SINCE_NEXT_TEST( "skip-since-next-test", Void.class ),
+ SHUTDOWN( "shutdown", String.class ),
/** To tell a forked process that the master process is still alive. Repeated after 10 seconds. */
- NOOP( Void.class ),
- BYE_ACK( Void.class );
+ NOOP( "noop", Void.class ),
+ BYE_ACK( "bye-ack", Void.class );
- public static final String MAGIC_NUMBER = "maven-surefire-command";
+ // due to have fast and thread-safe Map
+ public static final Map<Segment, MasterProcessCommand> COMMAND_TYPES = segmentsToCmds();
+ private final String opcode;
+ private final byte[] opcodeBinary;
private final Class<?> dataType;
- MasterProcessCommand( Class<?> dataType )
+ MasterProcessCommand( String opcode, Class<?> dataType )
{
+ this.opcode = requireNonNull( opcode, "value cannot be null" );
+ opcodeBinary = opcode.getBytes( US_ASCII );
this.dataType = requireNonNull( dataType, "dataType cannot be null" );
}
+ public byte[] getOpcodeBinary()
+ {
+ return opcodeBinary;
+ }
+
+ public int getOpcodeLength()
+ {
+ return opcodeBinary.length;
+ }
+
public Class<?> getDataType()
{
return dataType;
@@ -56,4 +77,21 @@ public enum MasterProcessCommand
{
return dataType != Void.class;
}
+
+ @Override
+ public String toString()
+ {
+ return opcode;
+ }
+
+ private static Map<Segment, MasterProcessCommand> segmentsToCmds()
+ {
+ Map<Segment, MasterProcessCommand> commands = new HashMap<>();
+ for ( MasterProcessCommand command : MasterProcessCommand.values() )
+ {
+ byte[] array = command.toString().getBytes( US_ASCII );
+ commands.put( new Segment( array, 0, array.length ), command );
+ }
+ return commands;
+ }
}
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java
similarity index 92%
rename from surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java
rename to surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java
index 4ab6cc3..7d20e0e 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.extensions;
+package org.apache.maven.surefire.api.fork;
/*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -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/report/RunMode.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/RunMode.java
index 7329f9c..dbdd580 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/RunMode.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/RunMode.java
@@ -19,6 +19,11 @@ package org.apache.maven.surefire.api.report;
* under the License.
*/
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.Segment;
+
+import java.util.HashMap;
+import java.util.Map;
+
import static java.nio.charset.StandardCharsets.US_ASCII;
/**
@@ -35,6 +40,9 @@ public enum RunMode
RERUN_TEST_AFTER_FAILURE( "rerun-test-after-failure" );
//todo add here RERUN_TESTSET, see https://github.com/apache/maven-surefire/pull/221
+ // due to have fast and thread-safe Map
+ public static final Map<Segment, RunMode> RUN_MODES = segmentsToRunModes();
+
private final String runmode;
private final byte[] runmodeBinary;
@@ -53,4 +61,15 @@ public enum RunMode
{
return runmodeBinary;
}
+
+ private static Map<Segment, RunMode> segmentsToRunModes()
+ {
+ Map<Segment, RunMode> runModes = new HashMap<>();
+ for ( RunMode runMode : RunMode.values() )
+ {
+ byte[] array = runMode.getRunmodeBinary();
+ runModes.put( new Segment( array, 0, array.length ), runMode );
+ }
+ return runModes;
+ }
}
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
new file mode 100644
index 0000000..862820c
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java
@@ -0,0 +1,735 @@
+package org.apache.maven.surefire.api.stream;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.RunMode;
+
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import static java.nio.charset.CodingErrorAction.REPLACE;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.util.Arrays.copyOf;
+import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING;
+import static org.apache.maven.surefire.api.stream.AbstractStreamDecoder.StreamReadStatus.OVERFLOW;
+import static org.apache.maven.surefire.api.stream.AbstractStreamDecoder.StreamReadStatus.UNDERFLOW;
+import static org.apache.maven.surefire.shared.lang3.StringUtils.isBlank;
+
+/**
+ * @param <M> message object
+ * @param <MT> enum describing the meaning of the message
+ * @param <ST> enum for segment type
+ */
+public abstract class AbstractStreamDecoder<M, MT extends Enum<MT>, ST extends Enum<ST>> implements AutoCloseable
+{
+ public static final int BUFFER_SIZE = 1024;
+
+ private static final String PRINTABLE_JVM_NATIVE_STREAM = "Listening for transport dt_socket at address:";
+
+ private static final String[] JVM_ERROR_PATTERNS = {
+ "could not create the java virtual machine", "error occurred during initialization", // of VM, of boot layer
+ "error:", // general errors
+ "could not reserve enough space", "could not allocate", "unable to allocate", // memory errors
+ "java.lang.module.findexception" // JPMS errors
+ };
+
+ private static final byte[] DEFAULT_STREAM_ENCODING_BYTES = DEFAULT_STREAM_ENCODING.name().getBytes( US_ASCII );
+
+ private static final int NO_POSITION = -1;
+ private static final int DELIMITER_LENGTH = 1;
+ private static final int BYTE_LENGTH = 1;
+ private static final int INT_LENGTH = 4;
+
+ private final ReadableByteChannel channel;
+ private final ForkNodeArguments arguments;
+ private final Map<Segment, MT> messageTypes;
+ private final ConsoleLogger logger;
+
+ protected AbstractStreamDecoder( @Nonnull ReadableByteChannel channel,
+ @Nonnull ForkNodeArguments arguments,
+ @Nonnull Map<Segment, MT> messageTypes )
+ {
+ this.channel = channel;
+ this.arguments = arguments;
+ this.messageTypes = messageTypes;
+ logger = arguments.getConsoleLogger();
+ }
+
+ public abstract M decode( @Nonnull Memento memento ) throws MalformedChannelException, IOException;
+
+ @Nonnull
+ protected abstract byte[] getEncodedMagicNumber();
+
+ @Nonnull
+ protected abstract ST[] nextSegmentType( @Nonnull MT messageType );
+
+ @Nonnull
+ protected abstract M toMessage( @Nonnull MT messageType, RunMode runMode, @Nonnull Memento memento )
+ throws MalformedFrameException;
+
+ @Nonnull
+ protected final ForkNodeArguments getArguments()
+ {
+ return arguments;
+ }
+
+ protected void debugStream( byte[] array, int position, int remaining )
+ {
+ }
+
+ protected MT readMessageType( @Nonnull Memento memento ) throws IOException, MalformedFrameException
+ {
+ byte[] header = getEncodedMagicNumber();
+ int readCount = DELIMITER_LENGTH + header.length + DELIMITER_LENGTH + BYTE_LENGTH + DELIMITER_LENGTH;
+ read( memento, readCount );
+ checkHeader( memento );
+ return messageTypes.get( readSegment( memento ) );
+ }
+
+ @Nonnull
+ @SuppressWarnings( "checkstyle:magicnumber" )
+ protected Segment readSegment( @Nonnull Memento memento ) throws IOException, MalformedFrameException
+ {
+ int readCount = readByte( memento ) & 0xff;
+ read( memento, readCount + DELIMITER_LENGTH );
+ ByteBuffer bb = memento.getByteBuffer();
+ Segment segment = new Segment( bb.array(), bb.arrayOffset() + bb.position(), readCount );
+ bb.position( bb.position() + readCount );
+ checkDelimiter( memento );
+ return segment;
+ }
+
+ @Nonnull
+ @SuppressWarnings( "checkstyle:magicnumber" )
+ protected Charset readCharset( @Nonnull Memento memento ) throws IOException, MalformedFrameException
+ {
+ int length = readByte( memento ) & 0xff;
+ read( memento, length + DELIMITER_LENGTH );
+ ByteBuffer bb = memento.getByteBuffer();
+ byte[] array = bb.array();
+ int offset = bb.arrayOffset() + bb.position();
+ bb.position( bb.position() + length );
+ boolean isDefaultEncoding = false;
+ if ( length == DEFAULT_STREAM_ENCODING_BYTES.length )
+ {
+ isDefaultEncoding = true;
+ for ( int i = 0; i < length; i++ )
+ {
+ isDefaultEncoding &= DEFAULT_STREAM_ENCODING_BYTES[i] == array[offset + i];
+ }
+ }
+
+ try
+ {
+ Charset charset =
+ isDefaultEncoding
+ ? DEFAULT_STREAM_ENCODING
+ : Charset.forName( new String( array, offset, length, US_ASCII ) );
+
+ checkDelimiter( memento );
+ return charset;
+ }
+ catch ( IllegalArgumentException e )
+ {
+ throw new MalformedFrameException( memento.getLine().getPositionByteBuffer(), bb.position() );
+ }
+ }
+
+ protected String readString( @Nonnull Memento memento ) throws IOException, MalformedFrameException
+ {
+ memento.getCharBuffer().clear();
+ int readCount = readInt( memento );
+ if ( readCount < 0 )
+ {
+ throw new MalformedFrameException( memento.getLine().getPositionByteBuffer(),
+ memento.getByteBuffer().position() );
+ }
+ read( memento, readCount + DELIMITER_LENGTH );
+
+ final String string;
+ if ( readCount == 0 )
+ {
+ string = "";
+ }
+ else if ( readCount == 1 )
+ {
+ read( memento, 1 );
+ byte oneChar = memento.getByteBuffer().get();
+ string = oneChar == 0 ? null : String.valueOf( (char) oneChar );
+ }
+ else
+ {
+ string = readString( memento, readCount );
+ }
+ read( memento, 1 );
+ checkDelimiter( memento );
+ return string;
+ }
+
+ protected Integer readInteger( @Nonnull Memento memento ) throws IOException, MalformedFrameException
+ {
+ read( memento, BYTE_LENGTH );
+ boolean isNullObject = memento.getByteBuffer().get() == 0;
+ if ( isNullObject )
+ {
+ read( memento, DELIMITER_LENGTH );
+ checkDelimiter( memento );
+ return null;
+ }
+ return readInt( memento );
+ }
+
+ protected byte readByte( @Nonnull Memento memento ) throws IOException, MalformedFrameException
+ {
+ read( memento, BYTE_LENGTH + DELIMITER_LENGTH );
+ byte b = memento.getByteBuffer().get();
+ checkDelimiter( memento );
+ return b;
+ }
+
+ protected int readInt( @Nonnull Memento memento ) throws IOException, MalformedFrameException
+ {
+ read( memento, INT_LENGTH + DELIMITER_LENGTH );
+ int i = memento.getByteBuffer().getInt();
+ checkDelimiter( memento );
+ return i;
+ }
+
+ @SuppressWarnings( "checkstyle:magicnumber" )
+ protected final void checkDelimiter( Memento memento ) throws MalformedFrameException
+ {
+ ByteBuffer bb = memento.bb;
+ if ( ( 0xff & bb.get() ) != ':' )
+ {
+ throw new MalformedFrameException( memento.getLine().getPositionByteBuffer(), bb.position() );
+ }
+ }
+
+ protected final void checkHeader( Memento memento ) throws MalformedFrameException
+ {
+ ByteBuffer bb = memento.bb;
+
+ checkDelimiter( memento );
+
+ int shift = 0;
+ try
+ {
+ byte[] header = getEncodedMagicNumber();
+ for ( int start = bb.arrayOffset() + bb.position(), length = header.length; shift < length; shift++ )
+ {
+ if ( bb.array()[shift + start] != header[shift] )
+ {
+ throw new MalformedFrameException( memento.getLine().getPositionByteBuffer(),
+ bb.position() + shift );
+ }
+ }
+ }
+ finally
+ {
+ bb.position( bb.position() + shift );
+ }
+
+ checkDelimiter( memento );
+ }
+
+ protected void checkArguments( RunMode runMode, Memento memento, int expectedDataElements )
+ throws MalformedFrameException
+ {
+ if ( runMode == null )
+ {
+ throw new MalformedFrameException( memento.getLine().getPositionByteBuffer(),
+ memento.getByteBuffer().position() );
+ }
+ checkArguments( memento, expectedDataElements );
+ }
+
+ protected void checkArguments( Memento memento, int expectedDataElements )
+ throws MalformedFrameException
+ {
+ if ( memento.getData().size() != expectedDataElements )
+ {
+ throw new MalformedFrameException( memento.getLine().getPositionByteBuffer(),
+ memento.getByteBuffer().position() );
+ }
+ }
+
+ private String readString( @Nonnull final Memento memento, @Nonnegative final int totalBytes )
+ throws IOException, MalformedFrameException
+ {
+ memento.getDecoder().reset();
+ final CharBuffer output = memento.getCharBuffer();
+ output.clear();
+ final ByteBuffer input = memento.getByteBuffer();
+ final List<String> strings = new ArrayList<>();
+ int countDecodedBytes = 0;
+ for ( boolean endOfInput = false; !endOfInput; )
+ {
+ final int bytesToRead = totalBytes - countDecodedBytes;
+ read( memento, bytesToRead );
+ int bytesToDecode = min( input.remaining(), bytesToRead );
+ final boolean isLastChunk = bytesToDecode == bytesToRead;
+ endOfInput = countDecodedBytes + bytesToDecode >= totalBytes;
+ do
+ {
+ boolean endOfChunk = output.remaining() >= bytesToRead;
+ boolean endOfOutput = isLastChunk && endOfChunk;
+ int readInputBytes = decodeString( memento.getDecoder(), input, output, bytesToDecode, endOfOutput,
+ memento.getLine().getPositionByteBuffer() );
+ bytesToDecode -= readInputBytes;
+ countDecodedBytes += readInputBytes;
+ }
+ while ( isLastChunk && bytesToDecode > 0 && output.hasRemaining() );
+
+ if ( isLastChunk || !output.hasRemaining() )
+ {
+ strings.add( output.flip().toString() );
+ output.clear();
+ }
+ }
+
+ memento.getDecoder().reset();
+ output.clear();
+
+ return toString( strings );
+ }
+
+ private static int decodeString( @Nonnull CharsetDecoder decoder, @Nonnull ByteBuffer input,
+ @Nonnull CharBuffer output, @Nonnegative int bytesToDecode,
+ boolean endOfInput, @Nonnegative int errorStreamFrom )
+ throws MalformedFrameException
+ {
+ int limit = input.limit();
+ input.limit( input.position() + bytesToDecode );
+
+ CoderResult result = decoder.decode( input, output, endOfInput );
+ if ( result.isError() || result.isMalformed() )
+ {
+ throw new MalformedFrameException( errorStreamFrom, input.position() );
+ }
+
+ int decodedBytes = bytesToDecode - input.remaining();
+ input.limit( limit );
+ return decodedBytes;
+ }
+
+ private static String toString( List<String> strings )
+ {
+ if ( strings.size() == 1 )
+ {
+ return strings.get( 0 );
+ }
+ StringBuilder concatenated = new StringBuilder( strings.size() * BUFFER_SIZE );
+ for ( String s : strings )
+ {
+ concatenated.append( s );
+ }
+ return concatenated.toString();
+ }
+
+ private void printCorruptedStream( Memento memento )
+ {
+ ByteBuffer bb = memento.getByteBuffer();
+ if ( bb.hasRemaining() )
+ {
+ int bytesToWrite = bb.remaining();
+ memento.getLine().write( bb, bb.position(), bytesToWrite );
+ bb.position( bb.position() + bytesToWrite );
+ }
+ }
+
+ /**
+ * Print the last string which has not been finished by a new line character.
+ *
+ * @param memento current memento object
+ */
+ protected final void printRemainingStream( Memento memento )
+ {
+ printCorruptedStream( memento );
+ memento.getLine().printExistingLine();
+ memento.getLine().setCount( 0 );
+ }
+
+ /**
+ *
+ */
+ public static final class Segment
+ {
+ private final byte[] array;
+ private final int fromIndex;
+ private final int length;
+ private final int hashCode;
+
+ public Segment( byte[] array, int fromIndex, int length )
+ {
+ this.array = array;
+ this.fromIndex = fromIndex;
+ this.length = length;
+
+ int hashCode = 0;
+ int i = fromIndex;
+ for ( int loops = length >> 1; loops-- != 0; )
+ {
+ hashCode = 31 * hashCode + array[i++];
+ hashCode = 31 * hashCode + array[i++];
+ }
+ this.hashCode = i == fromIndex + length ? hashCode : 31 * hashCode + array[i];
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals( Object obj )
+ {
+ if ( !( obj instanceof Segment ) )
+ {
+ return false;
+ }
+
+ Segment that = (Segment) obj;
+ if ( that.length != length )
+ {
+ return false;
+ }
+
+ for ( int i = 0; i < length; i++ )
+ {
+ if ( that.array[that.fromIndex + i] != array[fromIndex + i] )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ protected @Nonnull StreamReadStatus read( @Nonnull Memento memento, int recommendedCount ) throws IOException
+ {
+ ByteBuffer buffer = memento.getByteBuffer();
+ if ( buffer.remaining() >= recommendedCount && buffer.limit() != 0 )
+ {
+ return OVERFLOW;
+ }
+ else
+ {
+ if ( buffer.position() != 0 && recommendedCount > buffer.capacity() - buffer.position() )
+ {
+ buffer.compact().flip();
+ memento.getLine().setPositionByteBuffer( 0 );
+ }
+ int mark = buffer.position();
+ buffer.position( buffer.limit() );
+ buffer.limit( min( buffer.position() + recommendedCount, buffer.capacity() ) );
+ boolean isEnd = false;
+ while ( !isEnd && buffer.position() - mark < recommendedCount && buffer.position() < buffer.limit() )
+ {
+ isEnd = channel.read( buffer ) == -1;
+ }
+
+ buffer.limit( buffer.position() );
+ buffer.position( mark );
+ int readBytes = buffer.remaining();
+
+ if ( isEnd && readBytes < recommendedCount )
+ {
+ throw new EOFException();
+ }
+ else
+ {
+ debugStream( buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining() );
+ return readBytes >= recommendedCount ? OVERFLOW : UNDERFLOW;
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ public final class Memento
+ {
+ private CharsetDecoder currentDecoder;
+ private final CharsetDecoder defaultDecoder;
+ private final BufferedStream line = new BufferedStream( 32 );
+ private final List<Object> data = new ArrayList<>();
+ private final CharBuffer cb = CharBuffer.allocate( BUFFER_SIZE );
+ private final ByteBuffer bb = ByteBuffer.allocate( BUFFER_SIZE );
+
+ public Memento()
+ {
+ defaultDecoder = DEFAULT_STREAM_ENCODING.newDecoder()
+ .onMalformedInput( REPLACE )
+ .onUnmappableCharacter( REPLACE );
+ bb.limit( 0 );
+ }
+
+ public void reset()
+ {
+ currentDecoder = null;
+ data.clear();
+ }
+
+ public CharsetDecoder getDecoder()
+ {
+ return currentDecoder == null ? defaultDecoder : currentDecoder;
+ }
+
+ public void setCharset( Charset charset )
+ {
+ if ( charset.name().equals( defaultDecoder.charset().name() ) )
+ {
+ currentDecoder = defaultDecoder;
+ }
+ else
+ {
+ currentDecoder = charset.newDecoder()
+ .onMalformedInput( REPLACE )
+ .onUnmappableCharacter( REPLACE );
+ }
+ }
+
+ public BufferedStream getLine()
+ {
+ return line;
+ }
+
+ public List<Object> getData()
+ {
+ return data;
+ }
+
+ public CharBuffer getCharBuffer()
+ {
+ return cb;
+ }
+
+ public ByteBuffer getByteBuffer()
+ {
+ return bb;
+ }
+ }
+
+ /**
+ * This class avoids locking which gains the performance of this decoder.
+ */
+ public final class BufferedStream
+ {
+ private byte[] buffer;
+ private int count;
+ private int positionByteBuffer;
+ private boolean isNewLine;
+
+ BufferedStream( int capacity )
+ {
+ this.buffer = new byte[capacity];
+ }
+
+ public int getPositionByteBuffer()
+ {
+ return positionByteBuffer;
+ }
+
+ public void setPositionByteBuffer( int positionByteBuffer )
+ {
+ this.positionByteBuffer = positionByteBuffer;
+ }
+
+ public void write( ByteBuffer bb, int position, int length )
+ {
+ ensureCapacity( length );
+ byte[] array = bb.array();
+ int pos = bb.arrayOffset() + position;
+ while ( length-- > 0 )
+ {
+ positionByteBuffer++;
+ byte b = array[pos++];
+ if ( b == '\r' || b == '\n' )
+ {
+ if ( !isNewLine )
+ {
+ printExistingLine();
+ count = 0;
+ }
+ isNewLine = true;
+ }
+ else
+ {
+ buffer[count++] = b;
+ isNewLine = false;
+ }
+ }
+ }
+
+ public void setCount( int count )
+ {
+ this.count = count;
+ }
+
+ @Override
+ public String toString()
+ {
+ return new String( buffer, 0, count, DEFAULT_STREAM_ENCODING );
+ }
+
+ private boolean isEmpty()
+ {
+ return count == 0;
+ }
+
+ private void ensureCapacity( int addCapacity )
+ {
+ int oldCapacity = buffer.length;
+ int exactCapacity = count + addCapacity;
+ if ( exactCapacity < 0 )
+ {
+ throw new OutOfMemoryError();
+ }
+
+ if ( oldCapacity < exactCapacity )
+ {
+ int newCapacity = oldCapacity << 1;
+ buffer = copyOf( buffer, max( newCapacity, exactCapacity ) );
+ }
+ }
+
+ public void printExistingLine()
+ {
+ if ( isEmpty() )
+ {
+ return;
+ }
+
+ String s = toString();
+ if ( isBlank( s ) )
+ {
+ return;
+ }
+
+ if ( s.contains( PRINTABLE_JVM_NATIVE_STREAM ) )
+ {
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( s );
+ }
+ else if ( logger.isInfoEnabled() )
+ {
+ logger.info( s );
+ }
+ else
+ {
+ // In case of debugging forked JVM, see PRINTABLE_JVM_NATIVE_STREAM.
+ System.out.println( s );
+ }
+ }
+ else
+ {
+ if ( isJvmError( s ) )
+ {
+ logger.error( s );
+ }
+ else if ( logger.isDebugEnabled() )
+ {
+ logger.debug( s );
+ }
+
+ String msg = "Corrupted channel by directly writing to native stream in forked JVM "
+ + arguments.getForkChannelId() + ".";
+ File dumpFile = arguments.dumpStreamText( msg + " Stream '" + s + "'." );
+ String dumpPath = dumpFile.getAbsolutePath();
+ arguments.logWarningAtEnd( msg + " See FAQ web page and the dump file " + dumpPath );
+ }
+ }
+
+ private boolean isJvmError( String line )
+ {
+ String lineLower = line.toLowerCase();
+ for ( String errorPattern : JVM_ERROR_PATTERNS )
+ {
+ if ( lineLower.contains( errorPattern ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ *
+ */
+ public static final class MalformedFrameException extends Exception
+ {
+ private final int readFrom;
+ private final int readTo;
+
+ public MalformedFrameException( int readFrom, int readTo )
+ {
+ this.readFrom = readFrom;
+ this.readTo = readTo;
+ }
+
+ public int readFrom()
+ {
+ return readFrom;
+ }
+
+ public int readTo()
+ {
+ return readTo;
+ }
+
+ public boolean hasValidPositions()
+ {
+ return readFrom != NO_POSITION && readTo != NO_POSITION && readTo - readFrom > 0;
+ }
+ }
+
+ /**
+ * Underflow - could not completely read out al bytes in one call.
+ * <br>
+ * Overflow - read all bytes or more
+ * <br>
+ * EOF - end of stream
+ */
+ public enum StreamReadStatus
+ {
+ UNDERFLOW,
+ OVERFLOW,
+ EOF
+ }
+}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamEncoder.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamEncoder.java
new file mode 100644
index 0000000..d033958
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamEncoder.java
@@ -0,0 +1,191 @@
+package org.apache.maven.surefire.api.stream;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.api.report.RunMode;
+import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+import static java.lang.Math.ceil;
+import static java.nio.CharBuffer.wrap;
+
+/**
+ * The base class of stream encoder.
+ * The type of message is expressed by opcode where the opcode object is described by the generic type {@link E}.
+ * @param <E> type of the message
+ */
+public abstract class AbstractStreamEncoder<E extends Enum<E>>
+{
+ private static final byte BOOLEAN_NON_NULL_OBJECT = (byte) 0xff;
+ private static final byte BOOLEAN_NULL_OBJECT = (byte) 0;
+ private static final byte[] INT_BINARY = new byte[] {0, 0, 0, 0};
+
+ private final WritableByteChannel out;
+
+ public AbstractStreamEncoder( WritableByteChannel out )
+ {
+ this.out = out;
+ }
+
+ @Nonnull
+ protected abstract byte[] getEncodedMagicNumber();
+
+ @Nonnull
+ protected abstract byte[] enumToByteArray( E e );
+
+ @Nonnull
+ protected abstract byte[] getEncodedCharsetName();
+
+ @Nonnull
+ protected abstract Charset getCharset();
+
+ @Nonnull
+ protected abstract CharsetEncoder newCharsetEncoder();
+
+ protected void write( ByteBuffer frame, boolean sendImmediately )
+ throws IOException
+ {
+ if ( !sendImmediately && out instanceof WritableBufferedByteChannel )
+ {
+ ( (WritableBufferedByteChannel) out ).writeBuffered( frame );
+ }
+ else
+ {
+ out.write( frame );
+ }
+ }
+
+ public void encodeHeader( ByteBuffer result, E operation, RunMode runMode )
+ {
+ result.put( (byte) ':' );
+ result.put( getEncodedMagicNumber() );
+ result.put( (byte) ':' );
+ byte[] opcode = enumToByteArray( operation );
+ result.put( (byte) opcode.length );
+ result.put( (byte) ':' );
+ result.put( opcode );
+ result.put( (byte) ':' );
+
+ if ( runMode != null )
+ {
+ byte[] runmode = runMode.getRunmodeBinary();
+ result.put( (byte) runmode.length );
+ result.put( (byte) ':' );
+ result.put( runmode );
+ result.put( (byte) ':' );
+ }
+ }
+
+ public void encodeCharset( ByteBuffer result )
+ {
+ byte[] charsetNameBinary = getEncodedCharsetName();
+ result.put( (byte) charsetNameBinary.length );
+ result.put( (byte) ':' );
+ result.put( charsetNameBinary );
+ result.put( (byte) ':' );
+ }
+
+ public void encodeString( CharsetEncoder encoder, ByteBuffer result, String string )
+ {
+ String nonNullString = nonNull( string );
+
+ int counterPosition = result.position();
+
+ result.put( INT_BINARY ).put( (byte) ':' );
+
+ int msgStart = result.position();
+ encoder.encode( wrap( nonNullString ), result, true );
+ int msgEnd = result.position();
+ int encodedMsgSize = msgEnd - msgStart;
+ result.putInt( counterPosition, encodedMsgSize );
+
+ result.position( msgEnd );
+
+ result.put( (byte) ':' );
+ }
+
+ public void encodeInteger( ByteBuffer result, Integer i )
+ {
+ if ( i == null )
+ {
+ result.put( BOOLEAN_NULL_OBJECT );
+ }
+ else
+ {
+ result.put( BOOLEAN_NON_NULL_OBJECT ).putInt( i );
+ }
+ result.put( (byte) ':' );
+ }
+
+ public void encode( CharsetEncoder encoder, ByteBuffer result, E operation, RunMode runMode, String... messages )
+ {
+ encodeHeader( result, operation, runMode );
+ encodeCharset( result );
+ for ( String message : messages )
+ {
+ encodeString( encoder, result, message );
+ }
+ }
+
+ public int estimateBufferLength( int opcodeLength, RunMode runMode, CharsetEncoder encoder,
+ int integersCounter, String... strings )
+ {
+ assert !( encoder == null && strings.length != 0 );
+
+ // one delimiter character ':' + <string> + one delimiter character ':' +
+ // one byte + one delimiter character ':' + <string> + one delimiter character ':'
+ int lengthOfMetadata = 1 + getEncodedMagicNumber().length + 1 + 1 + 1 + opcodeLength + 1;
+
+ if ( runMode != null )
+ {
+ // one byte of length + one delimiter character ':' + <string> + one delimiter character ':'
+ lengthOfMetadata += 1 + 1 + runMode.geRunmode().length() + 1;
+ }
+
+ if ( encoder != null )
+ {
+ // one byte of length + one delimiter character ':' + <string> + one delimiter character ':'
+ lengthOfMetadata += 1 + 1 + encoder.charset().name().length() + 1;
+ }
+
+ // one byte (0x00 if NULL) + 4 bytes for integer + one delimiter character ':'
+ int lengthOfData = ( 1 + 1 + 4 ) * integersCounter;
+
+ for ( String string : strings )
+ {
+ String s = nonNull( string );
+ // 4 bytes of string length + one delimiter character ':' + <string> + one delimiter character ':'
+ lengthOfData += 4 + 1 + (int) ceil( encoder.maxBytesPerChar() * s.length() ) + 1;
+ }
+
+ return lengthOfMetadata + lengthOfData;
+ }
+
+ private static String nonNull( String msg )
+ {
+ return msg == null ? "\u0000" : msg;
+ }
+}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/Constants.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/MalformedChannelException.java
similarity index 60%
copy from surefire-api/src/main/java/org/apache/maven/surefire/api/booter/Constants.java
copy to surefire-api/src/main/java/org/apache/maven/surefire/api/stream/MalformedChannelException.java
index bcb7b17..cf4468f 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/Constants.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/MalformedChannelException.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.api.booter;
+package org.apache.maven.surefire.api.stream;
/*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -19,18 +19,9 @@ package org.apache.maven.surefire.api.booter;
* under the License.
*/
-import java.nio.charset.Charset;
-
-import static java.nio.charset.StandardCharsets.US_ASCII;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
/**
*
*/
-public final class Constants
+public class MalformedChannelException extends Exception
{
- public static final String MAGIC_NUMBER = "maven-surefire-event";
- public static final byte[] MAGIC_NUMBER_BYTES = MAGIC_NUMBER.getBytes( US_ASCII );
- public static final Charset DEFAULT_STREAM_ENCODING = UTF_8;
- public static final byte[] DEFAULT_STREAM_ENCODING_BYTES = UTF_8.name().getBytes( US_ASCII );
}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/Constants.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/SegmentType.java
similarity index 60%
copy from surefire-api/src/main/java/org/apache/maven/surefire/api/booter/Constants.java
copy to surefire-api/src/main/java/org/apache/maven/surefire/api/stream/SegmentType.java
index bcb7b17..f81d2ce 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/Constants.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/SegmentType.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.api.booter;
+package org.apache.maven.surefire.api.stream;
/*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -19,18 +19,17 @@ package org.apache.maven.surefire.api.booter;
* under the License.
*/
-import java.nio.charset.Charset;
-
-import static java.nio.charset.StandardCharsets.US_ASCII;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
/**
*
*/
-public final class Constants
+public enum SegmentType
{
- public static final String MAGIC_NUMBER = "maven-surefire-event";
- public static final byte[] MAGIC_NUMBER_BYTES = MAGIC_NUMBER.getBytes( US_ASCII );
- public static final Charset DEFAULT_STREAM_ENCODING = UTF_8;
- public static final byte[] DEFAULT_STREAM_ENCODING_BYTES = UTF_8.name().getBytes( US_ASCII );
+ RUN_MODE,
+ STRING_ENCODING,
+ DATA_STRING,
+ DATA_INTEGER,
+ DATA_INT,
+ DATA_BYTE,
+ DATA_STRING_ARRAY,
+ END_OF_FRAME
}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/DumpFileUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/DumpFileUtils.java
index bac68be..b552aa2 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/DumpFileUtils.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/DumpFileUtils.java
@@ -52,6 +52,7 @@ public final class DumpFileUtils
*/
public static synchronized File newDumpFile( File reportsDir, String dumpFileName )
{
+ reportsDir.mkdirs();
return new File( reportsDir, dumpFileName );
}
diff --git a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
index 7ac3ba2..c707ed6 100644
--- a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
@@ -25,6 +25,8 @@ import org.apache.maven.surefire.api.runorder.ThreadedExecutionSchedulerTest;
import org.apache.maven.surefire.SpecificTestClassFilterTest;
import org.apache.maven.surefire.api.booter.ForkingRunListenerTest;
import org.apache.maven.surefire.api.report.LegacyPojoStackTraceWriterTest;
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoderTest;
+import org.apache.maven.surefire.api.stream.AbstractStreamEncoderTest;
import org.apache.maven.surefire.api.suite.RunResultTest;
import org.apache.maven.surefire.api.testset.FundamentalFilterTest;
import org.apache.maven.surefire.api.testset.ResolvedTestTest;
@@ -68,7 +70,9 @@ import org.junit.runners.Suite;
ReflectionUtilsTest.class,
ChannelsReaderTest.class,
ChannelsWriterTest.class,
- AsyncSocketTest.class
+ AsyncSocketTest.class,
+ AbstractStreamEncoderTest.class,
+ AbstractStreamDecoderTest.class
} )
@RunWith( Suite.class )
public class JUnit4SuiteTest
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
new file mode 100644
index 0000000..5ab7ae0
--- /dev/null
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java
@@ -0,0 +1,692 @@
+package org.apache.maven.surefire.api.stream;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.api.booter.Constants;
+import org.apache.maven.surefire.api.booter.ForkedProcessEventType;
+import org.apache.maven.surefire.api.event.Event;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.RunMode;
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.MalformedFrameException;
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.Memento;
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.Segment;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import javax.annotation.Nonnull;
+import java.io.EOFException;
+import java.io.File;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.charset.CharsetDecoder;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.Math.min;
+import static java.lang.System.arraycopy;
+import static java.nio.charset.CodingErrorAction.REPLACE;
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Collections.singletonMap;
+import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT;
+import static org.apache.maven.surefire.api.stream.SegmentType.END_OF_FRAME;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.powermock.reflect.Whitebox.invokeMethod;
+
+/**
+ * The performance of "get( Integer )" is 13.5 nano seconds on i5/2.6GHz:
+ * <pre>
+ * {@code
+ * TreeMap<Integer, ForkedProcessEventType> map = new TreeMap<>();
+ * map.get( hash );
+ * }
+ * </pre>
+ *
+ * <br> The performance of getting event type by Segment is 33.7 nano seconds:
+ * <pre>
+ * {@code
+ * Map<Segment, ForkedProcessEventType> map = new HashMap<>();
+ * byte[] array = ForkedProcessEventType.BOOTERCODE_STDOUT.getOpcode().getBytes( UTF_8 );
+ * map.get( new Segment( array, 0, array.length ) );
+ * }
+ * </pre>
+ *
+ * <br> The performance of decoder:
+ * <pre>
+ * {@code
+ * CharsetDecoder decoder = STREAM_ENCODING.newDecoder()
+ * .onMalformedInput( REPLACE )
+ * .onUnmappableCharacter( REPLACE );
+ * ByteBuffer buffer = ByteBuffer.wrap( ForkedProcessEventType.BOOTERCODE_STDOUT.getOpcode().getBytes( UTF_8 ) );
+ * CharBuffer chars = CharBuffer.allocate( 100 );
+ * decoder.reset().decode( buffer, chars, true );
+ *
+ * String s = chars.flip().toString(); // 37 nanos = CharsetDecoder + toString
+ *
+ * buffer.clear();
+ * chars.clear();
+ *
+ * ForkedProcessEventType.byOpcode( s ); // 65 nanos = CharsetDecoder + toString + byOpcode
+ * }
+ * </pre>
+ *
+ * <br> The performance of decoding 100 bytes via CharacterDecoder - 71 nano seconds:
+ * <pre>
+ * {@code
+ * decoder.reset()
+ * .decode( buffer, chars, true ); // CharsetDecoder 71 nanos
+ * chars.flip().toString(); // CharsetDecoder + toString = 91 nanos
+ * }
+ * </pre>
+ *
+ * <br> The performance of a pure string creation (instead of decoder) - 31.5 nano seconds:
+ * <pre>
+ * {@code
+ * byte[] b = {};
+ * new String( b, UTF_8 );
+ * }
+ * </pre>
+ *
+ * <br> The performance of CharsetDecoder with empty ByteBuffer:
+ * <pre>
+ * {@code
+ * CharsetDecoder + ByteBuffer.allocate( 0 ) makes 11.5 nanos
+ * CharsetDecoder + ByteBuffer.allocate( 0 ) + toString() makes 16.1 nanos
+ * }
+ * </pre>
+ */
+@SuppressWarnings( "checkstyle:magicnumber" )
+public class AbstractStreamDecoderTest
+{
+ private static final Map<Segment, ForkedProcessEventType> EVENTS = new HashMap<>();
+
+ private static final String PATTERN1 =
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
+
+ private static final String PATTERN2 = "€ab©c";
+
+ private static final byte[] PATTERN2_BYTES =
+ new byte[] {(byte) -30, (byte) -126, (byte) -84, 'a', 'b', (byte) 0xc2, (byte) 0xa9, 'c'};
+
+ @BeforeClass
+ public static void setup()
+ {
+ for ( ForkedProcessEventType event : ForkedProcessEventType.values() )
+ {
+ byte[] array = event.getOpcodeBinary();
+ EVENTS.put( new Segment( array, 0, array.length ), event );
+ }
+ }
+
+ @Test
+ public void shouldDecodeHappyCase() throws Exception
+ {
+ CharsetDecoder decoder = UTF_8.newDecoder().onMalformedInput( REPLACE ).onUnmappableCharacter( REPLACE );
+ ByteBuffer input = ByteBuffer.allocate( 1024 );
+ input.put( PATTERN2_BYTES ).flip();
+ int bytesToDecode = PATTERN2_BYTES.length;
+ CharBuffer output = CharBuffer.allocate( 1024 );
+ int readBytes = invokeMethod( AbstractStreamDecoder.class, "decodeString", decoder, input, output,
+ bytesToDecode, true, 0 );
+
+ assertThat( readBytes )
+ .isEqualTo( bytesToDecode );
+
+ assertThat( output.flip().toString() )
+ .isEqualTo( PATTERN2 );
+ }
+
+ @Test
+ public void shouldDecodeShifted() throws Exception
+ {
+ CharsetDecoder decoder = UTF_8.newDecoder().onMalformedInput( REPLACE ).onUnmappableCharacter( REPLACE );
+ ByteBuffer input = ByteBuffer.allocate( 1024 );
+ input.put( PATTERN1.getBytes( UTF_8 ) )
+ .put( 90, (byte) 'A' )
+ .put( 91, (byte) 'B' )
+ .put( 92, (byte) 'C' )
+ .position( 90 );
+ CharBuffer output = CharBuffer.allocate( 1024 );
+ int readBytes =
+ invokeMethod( AbstractStreamDecoder.class, "decodeString", decoder, input, output, 2, true, 0 );
+
+ assertThat( readBytes ).isEqualTo( 2 );
+
+ assertThat( output.flip().toString() )
+ .isEqualTo( "AB" );
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotDecode() throws Exception
+ {
+ CharsetDecoder decoder = UTF_8.newDecoder();
+ ByteBuffer input = ByteBuffer.allocate( 100 );
+ int bytesToDecode = 101;
+ CharBuffer output = CharBuffer.allocate( 1000 );
+ invokeMethod( AbstractStreamDecoder.class, "decodeString", decoder, input, output, bytesToDecode, true, 0 );
+ }
+
+ @Test
+ public void shouldReadInt() throws Exception
+ {
+ Channel channel = new Channel( new byte[] {0x01, 0x02, 0x03, 0x04, ':'}, 1 );
+
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+
+ assertThat( thread.readInt( memento ) )
+ .isEqualTo( new BigInteger( new byte[] {0x01, 0x02, 0x03, 0x04} ).intValue() );
+ }
+
+ @Test
+ public void shouldReadInteger() throws Exception
+ {
+ Channel channel = new Channel( new byte[] {(byte) 0xff, 0x01, 0x02, 0x03, 0x04, ':'}, 1 );
+
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ assertThat( thread.readInteger( memento ) )
+ .isEqualTo( new BigInteger( new byte[] {0x01, 0x02, 0x03, 0x04} ).intValue() );
+ }
+
+ @Test
+ public void shouldReadNullInteger() throws Exception
+ {
+ Channel channel = new Channel( new byte[] {(byte) 0x00, ':'}, 1 );
+
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ assertThat( thread.readInteger( memento ) )
+ .isNull();
+ }
+
+ @Test( expected = EOFException.class )
+ public void shouldNotReadString() throws Exception
+ {
+ Channel channel = new Channel( PATTERN1.getBytes(), PATTERN1.length() );
+ channel.read( ByteBuffer.allocate( 100 ) );
+
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ invokeMethod( thread, "readString", memento, 10 );
+ }
+
+ @Test
+ public void shouldReadString() throws Exception
+ {
+ Channel channel = new Channel( PATTERN1.getBytes(), PATTERN1.length() );
+
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ String s = invokeMethod( thread, "readString", memento, 10 );
+ assertThat( s )
+ .isEqualTo( "0123456789" );
+ }
+
+ @Test
+ public void shouldReadStringShiftedBuffer() throws Exception
+ {
+ StringBuilder s = new StringBuilder( 1100 );
+ for ( int i = 0; i < 11; i++ )
+ {
+ s.append( PATTERN1 );
+ }
+
+ Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );
+
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ // whatever position will be compacted to 0
+ memento.getByteBuffer().limit( 974 ).position( 974 );
+ assertThat( invokeMethod( thread, "readString", memento, PATTERN1.length() + 3 ) )
+ .isEqualTo( PATTERN1 + "012" );
+ }
+
+ @Test
+ public void shouldReadStringShiftedInput() throws Exception
+ {
+ StringBuilder s = new StringBuilder( 1100 );
+ for ( int i = 0; i < 11; i++ )
+ {
+ s.append( PATTERN1 );
+ }
+
+ Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );
+ channel.read( ByteBuffer.allocate( 997 ) );
+
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ assertThat( invokeMethod( thread, "readString", memento, PATTERN1.length() ) )
+ .isEqualTo( "789" + PATTERN1.substring( 0, 97 ) );
+ }
+
+ @Test
+ public void shouldReadMultipleStringsAndShiftedInput() throws Exception
+ {
+ StringBuilder s = new StringBuilder( 5000 );
+
+ for ( int i = 0; i < 50; i++ )
+ {
+ s.append( PATTERN1 );
+ }
+
+ Channel channel = new Channel( s.toString().getBytes( UTF_8 ), s.length() );
+ channel.read( ByteBuffer.allocate( 1997 ) );
+
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ // whatever position will be compacted to 0
+ memento.getByteBuffer().limit( 974 ).position( 974 );
+
+ StringBuilder expected = new StringBuilder( "789" );
+ for ( int i = 0; i < 11; i++ )
+ {
+ expected.append( PATTERN1 );
+ }
+ expected.setLength( 1100 );
+ assertThat( invokeMethod( thread, "readString", memento, 1100 ) )
+ .isEqualTo( expected.toString() );
+ }
+
+ @Test
+ public void shouldDecode3BytesEncodedSymbol() throws Exception
+ {
+ byte[] encodedSymbol = new byte[] {(byte) -30, (byte) -126, (byte) -84};
+ int countSymbols = 1024;
+ byte[] input = new byte[encodedSymbol.length * countSymbols];
+ for ( int i = 0; i < countSymbols; i++ )
+ {
+ arraycopy( encodedSymbol, 0, input, encodedSymbol.length * i, encodedSymbol.length );
+ }
+
+ Channel channel = new Channel( input, 64 * 1024 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+ Memento memento = thread.new Memento();
+ String decodedOutput = invokeMethod( thread, "readString", memento, input.length );
+
+ assertThat( decodedOutput )
+ .isEqualTo( new String( input, 0, input.length, UTF_8 ) );
+ }
+
+ @Test
+ public void shouldDecode100Bytes() throws Exception
+ {
+ CharsetDecoder decoder = DEFAULT_STREAM_ENCODING.newDecoder()
+ .onMalformedInput( REPLACE )
+ .onUnmappableCharacter( REPLACE );
+ // empty stream: CharsetDecoder + ByteBuffer.allocate( 0 ) makes 11.5 nanos
+ // empty stream: CharsetDecoder + ByteBuffer.allocate( 0 ) + toString() makes 16.1 nanos
+ ByteBuffer buffer = ByteBuffer.wrap( PATTERN1.getBytes( UTF_8 ) );
+ CharBuffer chars = CharBuffer.allocate( 100 );
+ // uncomment this section for a proper measurement of the exec time
+ TimeUnit.SECONDS.sleep( 2 );
+ System.gc();
+ TimeUnit.SECONDS.sleep( 5 );
+ String s = null;
+ long l1 = System.currentTimeMillis();
+ for ( int i = 0; i < 10_000_000; i++ )
+ {
+ decoder.reset()
+ .decode( buffer, chars, true ); // CharsetDecoder 71 nanos
+ s = chars.flip().toString(); // CharsetDecoder + toString = 91 nanos
+ buffer.clear();
+ chars.clear();
+ }
+ long l2 = System.currentTimeMillis();
+ System.out.println( "decoded 100 bytes within " + ( l2 - l1 ) + " millis (10 million cycles)" );
+ assertThat( s )
+ .isEqualTo( PATTERN1 );
+ }
+
+ @Test
+ public void shouldReadEventType() throws Exception
+ {
+ byte[] array = BOOTERCODE_STDOUT.getOpcodeBinary();
+ Map<Segment, ForkedProcessEventType> messageType =
+ singletonMap( new Segment( array, 0, array.length ), BOOTERCODE_STDOUT );
+
+ byte[] stream = ":maven-surefire-event:\u000E:std-out-stream:".getBytes( UTF_8 );
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(), messageType );
+
+ Memento memento = thread.new Memento();
+ memento.setCharset( UTF_8 );
+
+ ForkedProcessEventType eventType = thread.readMessageType( memento );
+ assertThat( eventType )
+ .isEqualTo( BOOTERCODE_STDOUT );
+ }
+
+ @Test( expected = EOFException.class )
+ public void shouldEventTypeReachedEndOfStream() throws Exception
+ {
+ byte[] stream = ":maven-surefire-event:\u000E:xxx".getBytes( UTF_8 );
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(), EVENTS );
+
+ Memento memento = thread.new Memento();
+ memento.setCharset( UTF_8 );
+ thread.readMessageType( memento );
+ }
+
+ @Test( expected = MalformedFrameException.class )
+ public void shouldEventTypeReachedMalformedHeader() throws Exception
+ {
+ byte[] stream = ":xxxxx-xxxxxxxx-xxxxx:\u000E:xxx".getBytes( UTF_8 );
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ memento.setCharset( UTF_8 );
+ thread.readMessageType( memento );
+ }
+
+ @Test
+ public void shouldReadEmptyString() throws Exception
+ {
+ byte[] stream = "\u0000\u0000\u0000\u0000::".getBytes( UTF_8 );
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ memento.setCharset( UTF_8 );
+
+ assertThat( thread.readString( memento ) )
+ .isEmpty();
+ }
+
+ @Test
+ public void shouldReadNullString() throws Exception
+ {
+ byte[] stream = "\u0000\u0000\u0000\u0001:\u0000:".getBytes( UTF_8 );
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ memento.setCharset( UTF_8 );
+
+ assertThat( thread.readString( memento ) )
+ .isNull();
+ }
+
+ @Test
+ public void shouldReadSingleCharString() throws Exception
+ {
+ byte[] stream = "\u0000\u0000\u0000\u0001:A:".getBytes( UTF_8 );
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ memento.setCharset( UTF_8 );
+
+ assertThat( thread.readString( memento ) )
+ .isEqualTo( "A" );
+ }
+
+ @Test
+ public void shouldReadThreeCharactersString() throws Exception
+ {
+ byte[] stream = "\u0000\u0000\u0000\u0003:ABC:".getBytes( UTF_8 );
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ memento.setCharset( UTF_8 );
+
+ assertThat( thread.readString( memento ) )
+ .isEqualTo( "ABC" );
+ }
+
+ @Test
+ public void shouldReadDefaultCharset() throws Exception
+ {
+ byte[] stream = "\u0005:UTF-8:".getBytes( US_ASCII );
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ memento.setCharset( UTF_8 );
+
+ assertThat( thread.readCharset( memento ) )
+ .isNotNull()
+ .isEqualTo( UTF_8 );
+ }
+
+ @Test
+ public void shouldReadNonDefaultCharset() throws Exception
+ {
+ byte[] stream = ( (char) 10 + ":ISO_8859_1:" ).getBytes( US_ASCII );
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ memento.setCharset( UTF_8 );
+
+ assertThat( thread.readCharset( memento ) )
+ .isNotNull()
+ .isEqualTo( ISO_8859_1 );
+ }
+
+ @Test
+ public void shouldSetNonDefaultCharset()
+ {
+ byte[] stream = {};
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+ Memento memento = thread.new Memento();
+
+ memento.setCharset( ISO_8859_1 );
+ assertThat( memento.getDecoder().charset() ).isEqualTo( ISO_8859_1 );
+
+ memento.setCharset( UTF_8 );
+ assertThat( memento.getDecoder().charset() ).isEqualTo( UTF_8 );
+
+ memento.reset();
+ assertThat( memento.getDecoder() ).isNotNull();
+ assertThat( memento.getDecoder().charset() ).isEqualTo( UTF_8 );
+ }
+
+ @Test( expected = MalformedFrameException.class )
+ public void malformedCharset() throws Exception
+ {
+ byte[] stream = ( (char) 8 + ":ISO_8859:" ).getBytes( US_ASCII );
+ Channel channel = new Channel( stream, 1 );
+ Mock thread = new Mock( channel, new MockForkNodeArguments(),
+ Collections.<Segment, ForkedProcessEventType>emptyMap() );
+
+ Memento memento = thread.new Memento();
+ memento.setCharset( UTF_8 );
+
+ thread.readCharset( memento );
+ }
+
+ private static class Channel implements ReadableByteChannel
+ {
+ private final byte[] bytes;
+ private final int chunkSize;
+ protected int i;
+
+ Channel( byte[] bytes, int chunkSize )
+ {
+ this.bytes = bytes;
+ this.chunkSize = chunkSize;
+ }
+
+ @Override
+ public int read( ByteBuffer dst )
+ {
+ if ( i == bytes.length )
+ {
+ return -1;
+ }
+ else if ( dst.hasRemaining() )
+ {
+ int length = min( min( chunkSize, bytes.length - i ), dst.remaining() ) ;
+ dst.put( bytes, i, length );
+ i += length;
+ return length;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ @Override
+ public boolean isOpen()
+ {
+ return false;
+ }
+
+ @Override
+ public void close()
+ {
+ }
+ }
+
+ private static class MockForkNodeArguments implements ForkNodeArguments
+ {
+ @Nonnull
+ @Override
+ public String getSessionId()
+ {
+ return null;
+ }
+
+ @Override
+ public int getForkChannelId()
+ {
+ return 0;
+ }
+
+ @Nonnull
+ @Override
+ public File dumpStreamText( @Nonnull String text )
+ {
+ return null;
+ }
+
+ @Nonnull
+ @Override
+ public File dumpStreamException( @Nonnull Throwable t )
+ {
+ return null;
+ }
+
+ @Override
+ public void logWarningAtEnd( @Nonnull String text )
+ {
+ }
+
+ @Nonnull
+ @Override
+ public ConsoleLogger getConsoleLogger()
+ {
+ return null;
+ }
+
+ @Override
+ public File getEventStreamBinaryFile()
+ {
+ return null;
+ }
+
+ @Override
+ public File getCommandStreamBinaryFile()
+ {
+ return null;
+ }
+ }
+
+ private static class Mock extends AbstractStreamDecoder<Event, ForkedProcessEventType, SegmentType>
+ {
+ protected Mock( @Nonnull ReadableByteChannel channel, @Nonnull ForkNodeArguments arguments,
+ @Nonnull Map<Segment, ForkedProcessEventType> messageTypes )
+ {
+ super( channel, arguments, messageTypes );
+ }
+
+ @Override
+ public Event decode( @Nonnull Memento memento ) throws MalformedChannelException
+ {
+ throw new MalformedChannelException();
+ }
+
+ @Nonnull
+ @Override
+ protected byte[] getEncodedMagicNumber()
+ {
+ return Constants.MAGIC_NUMBER_FOR_EVENTS_BYTES;
+ }
+
+ @Nonnull
+ @Override
+ protected SegmentType[] nextSegmentType( @Nonnull ForkedProcessEventType messageType )
+ {
+ return new SegmentType[] {END_OF_FRAME};
+ }
+
+ @Nonnull
+ @Override
+ protected Event toMessage(
+ @Nonnull ForkedProcessEventType messageType, RunMode runMode,
+ @Nonnull Memento memento ) throws MalformedFrameException
+ {
+ return null;
+ }
+
+ @Override
+ public void close() throws Exception
+ {
+ }
+ }
+}
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamEncoderTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamEncoderTest.java
new file mode 100644
index 0000000..e36373c
--- /dev/null
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamEncoderTest.java
@@ -0,0 +1,331 @@
+package org.apache.maven.surefire.api.stream;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.api.booter.ForkedProcessEventType;
+import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
+import org.junit.Test;
+
+import javax.annotation.Nonnull;
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.maven.surefire.api.booter.Constants.MAGIC_NUMBER_FOR_EVENTS_BYTES;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_BYE;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_DEBUG;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_ERROR;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_INFO;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_WARNING;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_JVM_EXIT_ERROR;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_NEXT_TEST;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDERR;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDERR_NEW_LINE;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT_NEW_LINE;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STOP_ON_NEXT_TEST;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_SYSPROPS;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_COMPLETED;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_STARTING;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ASSUMPTIONFAILURE;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ERROR;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_FAILED;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SKIPPED;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_STARTING;
+import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SUCCEEDED;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
+import static org.fest.assertions.Assertions.assertThat;
+
+/**
+ *
+ */
+@SuppressWarnings( { "checkstyle:linelength", "checkstyle:magicnumber" } )
+public class AbstractStreamEncoderTest
+{
+ @Test
+ public void shouldComputeStreamPreemptiveLength()
+ {
+ Encoder streamEncoder = new Encoder( new DummyChannel() );
+ CharsetEncoder encoder = streamEncoder.newCharsetEncoder();
+
+ // :maven-surefire-event:8:sys-prop:10:normal-run:5:UTF-8:0001:kkk:0001:vvv:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_SYSPROPS.getOpcodeBinary().length, NORMAL_RUN,
+ encoder, 0, "k", "v" ) )
+ .isEqualTo( 72 );
+
+ // :maven-surefire-event:16:testset-starting:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TESTSET_STARTING.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+ .isEqualTo( 149 );
+
+ // :maven-surefire-event:17:testset-completed:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TESTSET_COMPLETED.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+ .isEqualTo( 150 );
+
+ // :maven-surefire-event:13:test-starting:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_STARTING.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+ .isEqualTo( 146 );
+
+ // :maven-surefire-event:14:test-succeeded:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_SUCCEEDED.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+ .isEqualTo( 147 );
+
+ // :maven-surefire-event:11:test-failed:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_FAILED.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+ .isEqualTo( 144 );
+
+ // :maven-surefire-event:12:test-skipped:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_SKIPPED.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+ .isEqualTo( 145 );
+
+ // :maven-surefire-event:10:test-error:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_ERROR.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+ .isEqualTo( 143 );
+
+ // :maven-surefire-event:23:test-assumption-failure:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_ASSUMPTIONFAILURE.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+ .isEqualTo( 156 );
+
+ // :maven-surefire-event:14:std-out-stream:10:normal-run:5:UTF-8:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_STDOUT.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 0, "s" ) )
+ .isEqualTo( 69 );
+
+ // :maven-surefire-event:23:std-out-stream-new-line:10:normal-run:5:UTF-8:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_STDOUT_NEW_LINE.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 0, "s" ) )
+ .isEqualTo( 78 );
+
+ // :maven-surefire-event:14:std-err-stream:10:normal-run:5:UTF-8:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_STDERR.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 0, "s" ) )
+ .isEqualTo( 69 );
+
+ // :maven-surefire-event:23:std-err-stream-new-line:10:normal-run:5:UTF-8:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_STDERR_NEW_LINE.getOpcodeBinary().length,
+ NORMAL_RUN, encoder, 0, "s" ) )
+ .isEqualTo( 78 );
+
+ // :maven-surefire-event:16:console-info-log:5:UTF-8:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_CONSOLE_INFO.getOpcodeBinary().length,
+ null, encoder, 0, "s" ) )
+ .isEqualTo( 58 );
+
+ // :maven-surefire-event:17:console-debug-log:5:UTF-8:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_CONSOLE_DEBUG.getOpcodeBinary().length,
+ null, encoder, 0, "s" ) )
+ .isEqualTo( 59 );
+
+ // :maven-surefire-event:19:console-warning-log:5:UTF-8:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_CONSOLE_WARNING.getOpcodeBinary().length,
+ null, encoder, 0, "s" ) )
+ .isEqualTo( 61 );
+
+ // :maven-surefire-event:17:console-error-log:5:UTF-8:0001:sss:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_CONSOLE_ERROR.getOpcodeBinary().length,
+ null, encoder, 0, "s" ) )
+ .isEqualTo( 59 );
+
+ // :maven-surefire-event:3:bye:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_BYE.getOpcodeBinary().length,
+ null, null, 0 ) )
+ .isEqualTo( 28 );
+
+ // :maven-surefire-event:17:stop-on-next-test:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_STOP_ON_NEXT_TEST.getOpcodeBinary().length,
+ null, null, 0 ) )
+ .isEqualTo( 42 );
+
+ // :maven-surefire-event:9:next-test:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_NEXT_TEST.getOpcodeBinary().length,
+ null, null, 0 ) )
+ .isEqualTo( 34 );
+
+ // :maven-surefire-event:14:jvm-exit-error:
+ assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_JVM_EXIT_ERROR.getOpcodeBinary().length,
+ null, null, 0 ) )
+ .isEqualTo( 39 );
+ }
+
+ @Test
+ public void testSendOpcode()
+ {
+ Encoder streamEncoder = new Encoder( new DummyChannel() );
+ ByteBuffer result = ByteBuffer.allocate( 128 );
+ streamEncoder.encodeHeader( result, BOOTERCODE_TEST_ERROR, NORMAL_RUN );
+ assertThat( toString( result ) )
+ .isEqualTo( ":maven-surefire-event:" + (char) 10 + ":test-error:" + (char) 10 + ":normal-run:" );
+
+ result = ByteBuffer.allocate( 1024 );
+ streamEncoder.encodeHeader( result, BOOTERCODE_CONSOLE_ERROR, null );
+ streamEncoder.encodeCharset( result );
+ assertThat( toString( result ) )
+ .isEqualTo( ":maven-surefire-event:" + (char) 17 + ":console-error-log:" + (char) 5 + ":UTF-8:" );
+ }
+
+ @Test
+ public void testEncodedString()
+ {
+ Encoder streamEncoder = new Encoder( new DummyChannel() );
+ ByteBuffer result = ByteBuffer.allocate( 128 );
+ streamEncoder.encode( streamEncoder.newCharsetEncoder(), result, BOOTERCODE_STDOUT, NORMAL_RUN, null, "msg" );
+ assertThat( toString( result ) )
+ .isEqualTo( ":maven-surefire-event:\u000e:std-out-stream:"
+ + (char) 10 + ":normal-run:\u0005:UTF-8:\u0000\u0000\u0000\u0001:\u0000:\u0000\u0000\u0000\u0003:msg:" );
+ }
+
+ @Test
+ public void testIntegerAsNull()
+ {
+ Encoder streamEncoder = new Encoder( new DummyChannel() );
+ ByteBuffer result = ByteBuffer.allocate( 4 );
+ streamEncoder.encodeInteger( result, null );
+ assertThat( result.position() ).isEqualTo( 2 );
+ assertThat( result.get( 0 ) ).isEqualTo( (byte) 0 );
+ assertThat( result.get( 1 ) ).isEqualTo( (byte) ':' );
+ }
+
+ @Test
+ public void testInteger()
+ {
+ Encoder streamEncoder = new Encoder( new DummyChannel() );
+ ByteBuffer result = ByteBuffer.allocate( 8 );
+ streamEncoder.encodeInteger( result, 5 );
+ assertThat( result.position() ).isEqualTo( 6 );
+ result.position( 0 );
+ assertThat( result.get() ).isEqualTo( (byte) 0xff );
+ assertThat( result.getInt() ).isEqualTo( (byte) 5 );
+ assertThat( result.get( 5 ) ).isEqualTo( (byte) ':' );
+ }
+
+ @Test
+ public void testWrite() throws Exception
+ {
+ DummyChannel channel = new DummyChannel();
+ Encoder streamEncoder = new Encoder( channel );
+ streamEncoder.write( ByteBuffer.allocate( 0 ), false );
+ assertThat( channel.writeBuffered ).isTrue();
+ assertThat( channel.write ).isFalse();
+ channel.writeBuffered = false;
+ channel.write = false;
+ streamEncoder.write( ByteBuffer.allocate( 0 ), true );
+ assertThat( channel.writeBuffered ).isFalse();
+ assertThat( channel.write ).isTrue();
+ }
+
+ private static String toString( ByteBuffer frame )
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ frame.flip();
+ os.write( frame.array(), frame.arrayOffset() + frame.position(), frame.remaining() );
+ return new String( os.toByteArray(), UTF_8 );
+ }
+
+ private static class Encoder extends AbstractStreamEncoder<ForkedProcessEventType>
+ {
+
+ Encoder( WritableBufferedByteChannel out )
+ {
+ super( out );
+ }
+
+ @Nonnull
+ @Override
+ public byte[] getEncodedMagicNumber()
+ {
+ return MAGIC_NUMBER_FOR_EVENTS_BYTES;
+ }
+
+ @Nonnull
+ @Override
+ protected byte[] enumToByteArray( ForkedProcessEventType forkedProcessEventType )
+ {
+ return forkedProcessEventType.getOpcodeBinary();
+ }
+
+ @Nonnull
+ @Override
+ protected byte[] getEncodedCharsetName()
+ {
+ return getCharset().name().getBytes( US_ASCII );
+ }
+
+ @Nonnull
+ @Override
+ public Charset getCharset()
+ {
+ return UTF_8;
+ }
+
+ @Nonnull
+ @Override
+ public CharsetEncoder newCharsetEncoder()
+ {
+ return getCharset().newEncoder();
+ }
+ }
+
+ private static class DummyChannel implements WritableBufferedByteChannel
+ {
+ boolean writeBuffered;
+ boolean write;
+
+ @Override
+ public void writeBuffered( ByteBuffer src )
+ {
+ writeBuffered = true;
+ }
+
+ @Override
+ public long countBufferOverflows()
+ {
+ return 0;
+ }
+
+ @Override
+ public int write( ByteBuffer src )
+ {
+ write = true;
+ return 0;
+ }
+
+ @Override
+ public boolean isOpen()
+ {
+ return false;
+ }
+
+ @Override
+ public void close()
+ {
+ }
+ }
+
+}
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 ae950df..dcf7361 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
@@ -42,7 +42,6 @@ public final class BooterConstants
public static final String TESTARTIFACT_VERSION = "testFwJarVersion";
public static final String TESTARTIFACT_CLASSIFIER = "testFwJarClassifier";
public static final String REQUESTEDTEST = "requestedTest";
- public static final String REQUESTEDTESTMETHOD = "requestedTestMethod";
public static final String SOURCE_DIRECTORY = "testSuiteDefinitionTestSourceDirectory";
public static final String TEST_CLASSES_DIRECTORY = "testClassesDirectory";
public static final String RUN_ORDER = "runOrder";
@@ -60,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 FORK_NUMBER = "forkNumber";
}
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 6a6cfae..ca5328e 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
@@ -62,6 +62,11 @@ public class BooterDeserializer
properties = SystemPropertyManager.loadProperties( inputStream );
}
+ public int getForkNumber()
+ {
+ return properties.getIntProperty( FORK_NUMBER );
+ }
+
/**
* Describes the current connection channel used by the client in the forked JVM
* in order to connect to the plugin process.
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/CommandReader.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
index ebe72a9..6599433 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
@@ -336,11 +336,11 @@ public final class CommandReader implements CommandChainReader
{
CommandReader.this.startMonitor.countDown();
boolean isTestSetFinished = false;
- try
+ try ( MasterProcessChannelDecoder commandReader = CommandReader.this.decoder )
{
while ( CommandReader.this.state.get() == RUNNABLE )
{
- Command command = CommandReader.this.decoder.decode();
+ Command command = commandReader.decode();
switch ( command.getCommandType() )
{
case RUN_CLASS:
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..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
@@ -27,16 +27,17 @@ 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.booter.spi.LegacyMasterProcessChannelProcessorFactory;
-import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.api.provider.CommandListener;
import org.apache.maven.surefire.api.provider.ProviderParameters;
import org.apache.maven.surefire.api.provider.SurefireProvider;
import org.apache.maven.surefire.api.report.LegacyPojoStackTraceWriter;
import org.apache.maven.surefire.api.report.StackTraceWriter;
+import org.apache.maven.surefire.api.testset.TestSetFailedException;
+import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelProcessorFactory;
+import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
import org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils;
import org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory;
-import org.apache.maven.surefire.api.testset.TestSetFailedException;
import java.io.File;
import java.io.FileInputStream;
@@ -61,13 +62,14 @@ 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;
import static org.apache.maven.surefire.booter.ProcessCheckerType.ALL;
import static org.apache.maven.surefire.booter.ProcessCheckerType.NATIVE;
import static org.apache.maven.surefire.booter.ProcessCheckerType.PING;
import static org.apache.maven.surefire.booter.SystemPropertyManager.setSystemProperties;
-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;
/**
* The part of the booter that is unique to a forked vm.
@@ -113,10 +115,13 @@ public final class ForkedBooter
DumpErrorSingleton.getSingleton()
.init( providerConfiguration.getReporterConfiguration().getReportsDirectory(), dumpFileName );
+ int forkNumber = booterDeserializer.getForkNumber();
+
if ( isDebugging() )
{
DumpErrorSingleton.getSingleton()
- .dumpText( "Found Maven process ID " + booterDeserializer.getPluginPid() );
+ .dumpText( "Found Maven process ID " + booterDeserializer.getPluginPid()
+ + " for the fork " + forkNumber + "." );
}
startupConfiguration = booterDeserializer.getStartupConfiguration();
@@ -124,8 +129,11 @@ public final class ForkedBooter
String channelConfig = booterDeserializer.getConnectionString();
channelProcessorFactory = lookupDecoderFactory( channelConfig );
channelProcessorFactory.connect( channelConfig );
- eventChannel = channelProcessorFactory.createEncoder();
- MasterProcessChannelDecoder decoder = channelProcessorFactory.createDecoder();
+ 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 );
flushEventChannelOnExit();
@@ -133,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 );
@@ -671,4 +679,5 @@ public final class ForkedBooter
return ManagementFactory.getRuntimeMXBean()
.getName();
}
+
}
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
new file mode 100644
index 0000000..4bed5ca
--- /dev/null
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedNodeArg.java
@@ -0,0 +1,97 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
+import org.apache.maven.surefire.api.booter.DumpErrorSingleton;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+
+import javax.annotation.Nonnull;
+import java.io.File;
+
+/**
+ *
+ */
+public final class ForkedNodeArg implements ForkNodeArguments
+{
+ private final int forkChannelId;
+ private final ConsoleLogger logger;
+ private final boolean isDebug;
+
+ public ForkedNodeArg( int forkChannelId, boolean isDebug )
+ {
+ this.forkChannelId = forkChannelId;
+ logger = new NullConsoleLogger();
+ this.isDebug = isDebug;
+ }
+
+ @Nonnull
+ @Override
+ public String getSessionId()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getForkChannelId()
+ {
+ return forkChannelId;
+ }
+
+ @Override
+ @Nonnull
+ public File dumpStreamText( @Nonnull String text )
+ {
+ return DumpErrorSingleton.getSingleton().dumpStreamText( text );
+ }
+
+ @Nonnull
+ @Override
+ public File dumpStreamException( @Nonnull Throwable t )
+ {
+ return DumpErrorSingleton.getSingleton().dumpStreamException( t, t.getLocalizedMessage() );
+ }
+
+ @Override
+ public void logWarningAtEnd( @Nonnull String text )
+ {
+ // do nothing - the log message of forked VM already goes to the dump file
+ }
+
+ @Nonnull
+ @Override
+ public ConsoleLogger getConsoleLogger()
+ {
+ 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/PropertiesWrapper.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PropertiesWrapper.java
index c0004e5..b5cccf4 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PropertiesWrapper.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PropertiesWrapper.java
@@ -62,7 +62,7 @@ public class PropertiesWrapper
public boolean getBooleanProperty( String propertyName )
{
- return Boolean.valueOf( properties.get( propertyName ) );
+ return Boolean.parseBoolean( properties.get( propertyName ) );
}
public int getIntProperty( String propertyName )
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoder.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoder.java
new file mode 100644
index 0000000..85bda73
--- /dev/null
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoder.java
@@ -0,0 +1,86 @@
+package org.apache.maven.surefire.booter.spi;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.api.booter.Command;
+import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.Memento;
+import org.apache.maven.surefire.api.stream.MalformedChannelException;
+import org.apache.maven.surefire.booter.stream.CommandDecoder;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.nio.channels.ReadableByteChannel;
+
+/**
+ * magic number : opcode [: opcode specific data]*
+ * <br>
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M5
+ */
+public class CommandChannelDecoder implements MasterProcessChannelDecoder
+{
+ private final CommandDecoder decoder;
+ private Memento memento;
+
+ public CommandChannelDecoder( @Nonnull ReadableByteChannel channel,
+ @Nonnull ForkNodeArguments arguments )
+ {
+ decoder = new CommandDecoder( channel, arguments );
+ }
+
+ @Override
+ @Nonnull
+ @SuppressWarnings( "checkstyle:innerassignment" )
+ public Command decode() throws IOException
+ {
+ if ( memento == null )
+ {
+ // do not create memento in constructor because the constructor is called in another thread
+ // memento is the thread confinement object
+ memento = decoder.new Memento();
+ }
+
+ do
+ {
+ try
+ {
+ Command command = decoder.decode( memento );
+ if ( command != null )
+ {
+ return command;
+ }
+ }
+ catch ( MalformedChannelException e )
+ {
+ // a bad stream, already logged the stream down to a dump file or console, and continue till OEF
+ }
+ }
+ while ( true );
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ decoder.close();
+ }
+}
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/EventChannelEncoder.java
similarity index 61%
rename from surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoder.java
rename to surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/EventChannelEncoder.java
index 0d8f787..8f78072 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/EventChannelEncoder.java
@@ -28,6 +28,7 @@ import org.apache.maven.surefire.api.report.RunMode;
import org.apache.maven.surefire.api.report.SafeThrowable;
import org.apache.maven.surefire.api.report.StackTraceWriter;
import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
+import org.apache.maven.surefire.booter.stream.EventEncoder;
import javax.annotation.Nonnull;
import java.io.IOException;
@@ -39,12 +40,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
-import static java.lang.Math.ceil;
-import static java.nio.CharBuffer.wrap;
import static java.util.Objects.requireNonNull;
-import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING;
-import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING_BYTES;
-import static org.apache.maven.surefire.api.booter.Constants.MAGIC_NUMBER_BYTES;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_BYE;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_DEBUG;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_ERROR;
@@ -67,7 +63,6 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_STARTING;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SUCCEEDED;
import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
-import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAILURE;
/**
* magic number : opcode : run mode [: opcode specific data]*
@@ -77,39 +72,45 @@ import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAIL
* @since 3.0.0-M4
*/
@SuppressWarnings( "checkstyle:linelength" )
-public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEncoder
+public class EventChannelEncoder extends EventEncoder implements MasterProcessChannelEncoder
{
- private static final byte[] INT_BINARY = new byte[] {0, 0, 0, 0};
- private static final byte BOOLEAN_NON_NULL_OBJECT = (byte) 0xff;
- private static final byte BOOLEAN_NULL_OBJECT = (byte) 0;
-
- private final WritableBufferedByteChannel out;
private final RunMode runMode;
private final AtomicBoolean trouble = new AtomicBoolean();
private volatile boolean onExit;
- public LegacyMasterProcessChannelEncoder( @Nonnull WritableBufferedByteChannel out )
+ /**
+ * The encoder for events and normal test mode.
+ *
+ * @param out the channel available for writing the events
+ */
+ public EventChannelEncoder( @Nonnull WritableBufferedByteChannel out )
{
this( out, NORMAL_RUN );
}
- protected LegacyMasterProcessChannelEncoder( @Nonnull WritableBufferedByteChannel out, @Nonnull RunMode runMode )
+ /**
+ * The encoder for events and any test mode.
+ *
+ * @param out the channel available for writing the events
+ * @param runMode run mode
+ */
+ public EventChannelEncoder( @Nonnull WritableBufferedByteChannel out, @Nonnull RunMode runMode )
{
- this.out = requireNonNull( out );
+ super( out );
this.runMode = requireNonNull( runMode );
}
- @Override
+ /*@Override
public MasterProcessChannelEncoder asRerunMode() // todo apply this and rework providers
{
- return new LegacyMasterProcessChannelEncoder( out, RERUN_TEST_AFTER_FAILURE );
+ return new EventChannelEncoder( streamEncoder, RERUN_TEST_AFTER_FAILURE );
}
@Override
public MasterProcessChannelEncoder asNormalMode()
{
- return new LegacyMasterProcessChannelEncoder( out, NORMAL_RUN );
- }
+ return new EventChannelEncoder( streamEncoder, NORMAL_RUN );
+ }*/
@Override
public boolean checkError()
@@ -121,13 +122,13 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn
public void onJvmExit()
{
onExit = true;
- encodeAndPrintEvent( ByteBuffer.wrap( new byte[] {'\n'} ), true );
+ write( ByteBuffer.wrap( new byte[] {'\n'} ), true );
}
@Override
- public void sendSystemProperties( Map<String, String> sysProps )
+ public void systemProperties( Map<String, String> sysProps )
{
- CharsetEncoder encoder = DEFAULT_STREAM_ENCODING.newEncoder();
+ CharsetEncoder encoder = newCharsetEncoder();
ByteBuffer result = null;
for ( Iterator<Entry<String, String>> it = sysProps.entrySet().iterator(); it.hasNext(); )
{
@@ -135,13 +136,14 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn
String key = entry.getKey();
String value = entry.getValue();
- int bufferLength = estimateBufferLength( BOOTERCODE_SYSPROPS, runMode, encoder, 0, key, value );
+ int bufferLength =
+ estimateBufferLength( BOOTERCODE_SYSPROPS.getOpcode().length(), runMode, encoder, 0, key, value );
result = result != null && result.capacity() >= bufferLength ? result : ByteBuffer.allocate( bufferLength );
result.clear();
- // :maven-surefire-event:sys-prop:rerun-test-after-failure:UTF-8:0000000000:<key>:0000000000:<value>:
+ // :maven-surefire-event:sys-prop:rerun-test-after-failure:UTF-8:<integer>:<key>:<integer>:<value>:
encode( encoder, result, BOOTERCODE_SYSPROPS, runMode, key, value );
- boolean sendImmediately = !it.hasNext();
- encodeAndPrintEvent( result, sendImmediately );
+ boolean sync = !it.hasNext();
+ write( result, sync );
}
}
@@ -210,14 +212,14 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn
private void setOutErr( ForkedProcessEventType eventType, String message )
{
ByteBuffer result = encodeMessage( eventType, runMode, message );
- encodeAndPrintEvent( result, false );
+ write( result, false );
}
@Override
public void consoleInfoLog( String message )
{
ByteBuffer result = encodeMessage( BOOTERCODE_CONSOLE_INFO, null, message );
- encodeAndPrintEvent( result, true );
+ write( result, true );
}
@Override
@@ -235,13 +237,15 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn
@Override
public void consoleErrorLog( String message, Throwable t )
{
- CharsetEncoder encoder = DEFAULT_STREAM_ENCODING.newEncoder();
+ CharsetEncoder encoder = newCharsetEncoder();
String stackTrace = t == null ? null : ConsoleLoggerUtils.toString( t );
- int bufferMaxLength = estimateBufferLength( BOOTERCODE_CONSOLE_ERROR, null, encoder, 0, message, stackTrace );
+ int bufferMaxLength = estimateBufferLength( BOOTERCODE_CONSOLE_ERROR.getOpcode().length(), null, encoder, 0,
+ message, stackTrace );
ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
- encodeHeader( encoder, result, BOOTERCODE_CONSOLE_ERROR, null );
+ encodeHeader( result, BOOTERCODE_CONSOLE_ERROR, null );
+ encodeCharset( result );
encode( encoder, result, message, null, stackTrace );
- encodeAndPrintEvent( result, true );
+ write( result, true );
}
@Override
@@ -254,14 +258,14 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn
public void consoleDebugLog( String message )
{
ByteBuffer result = encodeMessage( BOOTERCODE_CONSOLE_DEBUG, null, message );
- encodeAndPrintEvent( result, true );
+ write( result, true );
}
@Override
public void consoleWarningLog( String message )
{
ByteBuffer result = encodeMessage( BOOTERCODE_CONSOLE_WARNING, null, message );
- encodeAndPrintEvent( result, true );
+ write( result, true );
}
@Override
@@ -289,58 +293,51 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn
}
private void error( StackTraceWriter stackTraceWriter, boolean trimStackTraces, ForkedProcessEventType eventType,
- @SuppressWarnings( "SameParameterValue" ) boolean sendImmediately )
+ @SuppressWarnings( "SameParameterValue" ) boolean sync )
{
- CharsetEncoder encoder = DEFAULT_STREAM_ENCODING.newEncoder();
+ CharsetEncoder encoder = newCharsetEncoder();
StackTrace stackTraceWrapper = new StackTrace( stackTraceWriter, trimStackTraces );
- int bufferMaxLength = estimateBufferLength( eventType, null, encoder, 0,
+ int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), null, encoder, 0,
stackTraceWrapper.message, stackTraceWrapper.smartTrimmedStackTrace, stackTraceWrapper.stackTrace );
ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
- encodeHeader( encoder, result, eventType, null );
+ encodeHeader( result, eventType, null );
+ encodeCharset( result );
encode( encoder, result, stackTraceWrapper );
- encodeAndPrintEvent( result, sendImmediately );
+ write( result, sync );
}
- /**
- * :maven-surefire-event:testset-starting:rerun-test-after-failure:UTF-8:0000000000:SourceName:0000000000:SourceText:0000000000:Name:0000000000:NameText:0000000000:Group:0000000000:Message:0000000000:ElapsedTime:0000000000:LocalizedMessage:0000000000:SmartTrimmedStackTrace:0000000000:toStackTrace( stw, trimStackTraces ):0000000000:
- *
- */
+ // example
+ // :maven-surefire-event:testset-starting:rerun-test-after-failure:UTF-8:<integer>:SourceName:<integer>:SourceText:<integer>:Name:<integer>:NameText:<integer>:Group:<integer>:Message:<integer>:ElapsedTime:<integer>:LocalizedMessage:<integer>:SmartTrimmedStackTrace:<integer>:toStackTrace( stw, trimStackTraces ):<integer>:
private void encode( ForkedProcessEventType operation, RunMode runMode, ReportEntry reportEntry,
- boolean trimStackTraces, @SuppressWarnings( "SameParameterValue" ) boolean sendImmediately )
+ boolean trimStackTraces, @SuppressWarnings( "SameParameterValue" ) boolean sync )
{
ByteBuffer result = encode( operation, runMode, reportEntry, trimStackTraces );
- encodeAndPrintEvent( result, sendImmediately );
+ write( result, sync );
}
- private void encodeOpcode( ForkedProcessEventType eventType, boolean sendImmediately )
+ private void encodeOpcode( ForkedProcessEventType eventType, boolean sync )
{
- int bufferMaxLength = estimateBufferLength( eventType, null, null, 0 );
+ int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), null, null, 0 );
ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
- encodeOpcode( result, eventType, null );
- encodeAndPrintEvent( result, sendImmediately );
+ encodeHeader( result, eventType, null );
+ write( result, sync );
}
- private void encodeAndPrintEvent( ByteBuffer frame, boolean sendImmediately )
+ @Override
+ protected void write( ByteBuffer frame, boolean sync )
{
final boolean wasInterrupted = Thread.interrupted();
try
{
- if ( sendImmediately )
- {
- out.write( frame );
- }
- else
- {
- out.writeBuffered( frame );
- }
+ super.write( frame, sync );
}
catch ( ClosedChannelException e )
{
if ( !onExit )
{
String event = new String( frame.array(), frame.arrayOffset() + frame.position(), frame.remaining(),
- DEFAULT_STREAM_ENCODING );
+ getCharset() );
DumpErrorSingleton.getSingleton()
.dumpException( e, "Channel closed while writing the event '" + event + "'." );
@@ -363,23 +360,13 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn
}
}
- static void encode( CharsetEncoder encoder, ByteBuffer result,
- ForkedProcessEventType operation, RunMode runMode, String... messages )
- {
- encodeHeader( encoder, result, operation, runMode );
- for ( String message : messages )
- {
- encodeString( encoder, result, message );
- }
- }
-
- static void encode( CharsetEncoder encoder, ByteBuffer result, StackTrace stw )
+ private void encode( CharsetEncoder encoder, ByteBuffer result, StackTrace stw )
{
encode( encoder, result, stw.message, stw.smartTrimmedStackTrace, stw.stackTrace );
}
- private static void encode( CharsetEncoder encoder, ByteBuffer result,
- String message, String smartStackTrace, String stackTrace )
+ private void encode( CharsetEncoder encoder, ByteBuffer result,
+ String message, String smartStackTrace, String stackTrace )
{
encodeString( encoder, result, message );
encodeString( encoder, result, smartStackTrace );
@@ -399,21 +386,22 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn
* <li>{@link ForkedProcessEventType#BOOTERCODE_TEST_ASSUMPTIONFAILURE}.</li>
* </ul>
*/
- static ByteBuffer encode( ForkedProcessEventType operation, RunMode runMode, ReportEntry reportEntry,
- boolean trimStackTraces )
+ ByteBuffer encode( ForkedProcessEventType operation, RunMode runMode, ReportEntry reportEntry,
+ boolean trimStackTraces )
{
StackTrace stackTraceWrapper = new StackTrace( reportEntry.getStackTraceWriter(), trimStackTraces );
- CharsetEncoder encoder = DEFAULT_STREAM_ENCODING.newEncoder();
+ CharsetEncoder encoder = newCharsetEncoder();
- int bufferMaxLength = estimateBufferLength( operation, runMode, encoder, 1, reportEntry.getSourceName(),
- reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
- reportEntry.getMessage(), stackTraceWrapper.message, stackTraceWrapper.smartTrimmedStackTrace,
- stackTraceWrapper.stackTrace );
+ int bufferMaxLength = estimateBufferLength( operation.getOpcode().length(), runMode, encoder, 1,
+ reportEntry.getSourceName(), reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(),
+ reportEntry.getGroup(), reportEntry.getMessage(), stackTraceWrapper.message,
+ stackTraceWrapper.smartTrimmedStackTrace, stackTraceWrapper.stackTrace );
ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
- encodeHeader( encoder, result, operation, runMode );
+ encodeHeader( result, operation, runMode );
+ encodeCharset( result );
encodeString( encoder, result, reportEntry.getSourceName() );
encodeString( encoder, result, reportEntry.getSourceText() );
@@ -428,87 +416,15 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn
return result;
}
- static ByteBuffer encodeMessage( ForkedProcessEventType eventType, RunMode runMode, String message )
+ ByteBuffer encodeMessage( ForkedProcessEventType eventType, RunMode runMode, String message )
{
- CharsetEncoder encoder = DEFAULT_STREAM_ENCODING.newEncoder();
- int bufferMaxLength = estimateBufferLength( eventType, runMode, encoder, 0, message );
+ CharsetEncoder encoder = newCharsetEncoder();
+ int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), runMode, encoder, 0, message );
ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
- encodeHeader( encoder, result, eventType, runMode );
- encodeString( encoder, result, message );
+ encode( encoder, result, eventType, runMode, message );
return result;
}
- private static void encodeString( CharsetEncoder encoder, ByteBuffer result, String string )
- {
- String nonNullString = nonNull( string );
-
- int counterPosition = result.position();
-
- result.put( INT_BINARY ).put( (byte) ':' );
-
- int msgStart = result.position();
- encoder.encode( wrap( nonNullString ), result, true );
- int msgEnd = result.position();
- int encodedMsgSize = msgEnd - msgStart;
- result.putInt( counterPosition, encodedMsgSize );
-
- result.position( msgEnd );
-
- result.put( (byte) ':' );
- }
-
- private static void encodeInteger( ByteBuffer result, Integer i )
- {
- if ( i == null )
- {
- result.put( BOOLEAN_NULL_OBJECT );
- }
- else
- {
- result.put( BOOLEAN_NON_NULL_OBJECT ).putInt( i );
- }
- result.put( (byte) ':' );
- }
-
- static void encodeHeader( CharsetEncoder encoder, ByteBuffer result, ForkedProcessEventType operation,
- RunMode runMode )
- {
- encodeOpcode( result, operation, runMode );
- String charsetName = encoder.charset().name();
- result.put( (byte) charsetName.length() );
- result.put( (byte) ':' );
- result.put( DEFAULT_STREAM_ENCODING_BYTES );
- result.put( (byte) ':' );
- }
-
- /**
- * Used in {@link #bye()}, {@link #stopOnNextTest()} and {@link #encodeOpcode(ForkedProcessEventType, boolean)}
- * and private methods extending the buffer.
- *
- * @param operation opcode
- * @param runMode run mode
- */
- static void encodeOpcode( ByteBuffer result, ForkedProcessEventType operation, RunMode runMode )
- {
- result.put( (byte) ':' );
- result.put( MAGIC_NUMBER_BYTES );
- result.put( (byte) ':' );
- byte[] opcode = operation.getOpcodeBinary();
- result.put( (byte) opcode.length );
- result.put( (byte) ':' );
- result.put( opcode );
- result.put( (byte) ':' );
-
- if ( runMode != null )
- {
- byte[] runmode = runMode.getRunmodeBinary();
- result.put( (byte) runmode.length );
- result.put( (byte) ':' );
- result.put( runmode );
- result.put( (byte) ':' );
- }
- }
-
private static String toStackTrace( StackTraceWriter stw, boolean trimStackTraces )
{
if ( stw == null )
@@ -519,46 +435,6 @@ public class LegacyMasterProcessChannelEncoder implements MasterProcessChannelEn
return trimStackTraces ? stw.writeTrimmedTraceToString() : stw.writeTraceToString();
}
- static String nonNull( String msg )
- {
- return msg == null ? "\u0000" : msg;
- }
-
- static int estimateBufferLength( ForkedProcessEventType eventType, RunMode runMode, CharsetEncoder encoder,
- int integersCounter, String... strings )
- {
- assert !( encoder == null && strings.length != 0 );
-
- // one delimiter character ':' + <string> + one delimiter character ':' +
- // one byte + one delimiter character ':' + <string> + one delimiter character ':'
- int lengthOfMetadata = 1 + MAGIC_NUMBER_BYTES.length + 1 + 1 + 1 + eventType.getOpcode().length() + 1;
-
- if ( runMode != null )
- {
- // one byte of length + one delimiter character ':' + <string> + one delimiter character ':'
- lengthOfMetadata += 1 + 1 + runMode.geRunmode().length() + 1;
- }
-
- if ( encoder != null )
- {
- // one byte of length + one delimiter character ':' + <string> + one delimiter character ':'
- lengthOfMetadata += 1 + 1 + encoder.charset().name().length() + 1;
- }
-
- // one byte (0x00 if NULL) + 4 bytes for integer + one delimiter character ':'
- int lengthOfData = ( 1 + 4 + 1 ) * integersCounter;
-
- for ( String string : strings )
- {
- String s = string == null ? "\u0000" : string;
- // 4 bytes of string length + one delimiter character ':' + <string> + one delimiter character ':'
- lengthOfData += 4 + 1 + (int) ceil( encoder.maxBytesPerChar() * s.length() ) + 1;
- }
-
-
- return lengthOfMetadata + lengthOfData;
- }
-
private static final class StackTrace
{
final String message;
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoder.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoder.java
deleted file mode 100644
index 9d29495..0000000
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoder.java
+++ /dev/null
@@ -1,190 +0,0 @@
-package org.apache.maven.surefire.booter.spi;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.apache.maven.surefire.api.booter.Command;
-import org.apache.maven.surefire.api.booter.DumpErrorSingleton;
-import org.apache.maven.surefire.api.booter.MasterProcessCommand;
-import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
-import org.apache.maven.surefire.api.util.internal.ImmutableMap;
-
-import javax.annotation.Nonnull;
-import java.io.EOFException;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.BYE_ACK;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.MAGIC_NUMBER;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.NOOP;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.RUN_CLASS;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.SHUTDOWN;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
-import static org.apache.maven.surefire.api.booter.MasterProcessCommand.TEST_SET_FINISHED;
-
-/**
- * magic number : opcode [: opcode specific data]*
- * <br>
- *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 3.0.0-M5
- */
-public class LegacyMasterProcessChannelDecoder implements MasterProcessChannelDecoder
-{
- private static final Map<String, MasterProcessCommand> COMMAND_OPCODES = stringsToOpcodes();
-
- private final ReadableByteChannel channel;
-
- public LegacyMasterProcessChannelDecoder( @Nonnull ReadableByteChannel channel )
- {
- this.channel = channel;
- }
-
- @Override
- @Nonnull
- @SuppressWarnings( "checkstyle:innerassignment" )
- public Command decode() throws IOException
- {
- List<String> tokens = new ArrayList<>( 3 );
- StringBuilder token = new StringBuilder( MAGIC_NUMBER.length() );
- ByteBuffer buffer = ByteBuffer.allocate( 1 );
-
- start:
- do
- {
- boolean endOfStream;
- tokens.clear();
- token.setLength( 0 );
- FrameCompletion completion = null;
- for ( boolean frameStarted = false; !( endOfStream = channel.read( buffer ) == -1 ); completion = null )
- {
- buffer.flip();
- char c = (char) buffer.get();
- buffer.clear();
-
- if ( !frameStarted )
- {
- if ( c == ':' )
- {
- frameStarted = true;
- token.setLength( 0 );
- tokens.clear();
- }
- }
- else
- {
- if ( c == ':' )
- {
- tokens.add( token.toString() );
- token.setLength( 0 );
- completion = frameCompleteness( tokens );
- if ( completion == FrameCompletion.COMPLETE )
- {
- break;
- }
- else if ( completion == FrameCompletion.MALFORMED )
- {
- DumpErrorSingleton.getSingleton()
- .dumpStreamText( "Malformed frame with tokens " + tokens );
- continue start;
- }
- }
- else
- {
- token.append( c );
- }
- }
- }
-
- if ( completion == FrameCompletion.COMPLETE )
- {
- MasterProcessCommand cmd = COMMAND_OPCODES.get( tokens.get( 1 ) );
- if ( tokens.size() == 2 )
- {
- return new Command( cmd );
- }
- else if ( tokens.size() == 3 )
- {
- return new Command( cmd, tokens.get( 2 ) );
- }
- }
-
- if ( endOfStream )
- {
- throw new EOFException();
- }
- }
- while ( true );
- }
-
- private static FrameCompletion frameCompleteness( List<String> tokens )
- {
- if ( !tokens.isEmpty() && !MAGIC_NUMBER.equals( tokens.get( 0 ) ) )
- {
- return FrameCompletion.MALFORMED;
- }
-
- if ( tokens.size() >= 2 )
- {
- String opcode = tokens.get( 1 );
- MasterProcessCommand cmd = COMMAND_OPCODES.get( opcode );
- if ( cmd == null )
- {
- return FrameCompletion.MALFORMED;
- }
- else if ( cmd.hasDataType() == ( tokens.size() == 3 ) )
- {
- return FrameCompletion.COMPLETE;
- }
- }
- return FrameCompletion.NOT_COMPLETE;
- }
-
- @Override
- public void close()
- {
- }
-
- /**
- * Determines whether the frame is complete or malformed.
- */
- private enum FrameCompletion
- {
- NOT_COMPLETE,
- COMPLETE,
- MALFORMED
- }
-
- private static Map<String, MasterProcessCommand> stringsToOpcodes()
- {
- Map<String, MasterProcessCommand> opcodes = new HashMap<>();
- opcodes.put( "run-testclass", RUN_CLASS );
- opcodes.put( "testset-finished", TEST_SET_FINISHED );
- opcodes.put( "skip-since-next-test", SKIP_SINCE_NEXT_TEST );
- opcodes.put( "shutdown", SHUTDOWN );
- opcodes.put( "noop", NOOP );
- opcodes.put( "bye-ack", BYE_ACK );
- return new ImmutableMap<>( opcodes );
- }
-}
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelProcessorFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelProcessorFactory.java
index 6e28764..3d96f06 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelProcessorFactory.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelProcessorFactory.java
@@ -21,8 +21,10 @@ package org.apache.maven.surefire.booter.spi;
import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
+import javax.annotation.Nonnull;
import java.io.IOException;
import java.net.MalformedURLException;
@@ -51,21 +53,21 @@ public class LegacyMasterProcessChannelProcessorFactory
{
if ( !canUse( channelConfig ) )
{
- throw new MalformedURLException( "Unknown chanel string " + channelConfig );
+ throw new MalformedURLException( "Unknown channel string " + channelConfig );
}
}
@Override
- public MasterProcessChannelDecoder createDecoder()
+ public MasterProcessChannelDecoder createDecoder( @Nonnull ForkNodeArguments forkingArguments )
{
- return new LegacyMasterProcessChannelDecoder( newBufferedChannel( System.in ) );
+ return new CommandChannelDecoder( newBufferedChannel( System.in ), forkingArguments );
}
@Override
- public MasterProcessChannelEncoder createEncoder()
+ public MasterProcessChannelEncoder createEncoder( @Nonnull ForkNodeArguments forkingArguments )
{
WritableBufferedByteChannel channel = newBufferedChannel( System.out );
schedulePeriodicFlusher( FLUSH_PERIOD_MILLIS, channel );
- return new LegacyMasterProcessChannelEncoder( channel );
+ return new EventChannelEncoder( channel );
}
}
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
index 0bebeb4..6232209 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
@@ -21,8 +21,10 @@ package org.apache.maven.surefire.booter.spi;
import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
+import javax.annotation.Nonnull;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
@@ -31,6 +33,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
+import java.nio.channels.ReadableByteChannel;
import java.util.StringTokenizer;
import java.util.concurrent.ExecutionException;
@@ -69,7 +72,7 @@ public class SurefireMasterProcessChannelProcessorFactory
{
if ( !canUse( channelConfig ) )
{
- throw new MalformedURLException( "Unknown chanel string " + channelConfig );
+ throw new MalformedURLException( "Unknown channel string " + channelConfig );
}
try
@@ -97,17 +100,18 @@ public class SurefireMasterProcessChannelProcessorFactory
}
@Override
- public MasterProcessChannelDecoder createDecoder()
+ public MasterProcessChannelDecoder createDecoder( @Nonnull ForkNodeArguments forkingArguments )
{
- return new LegacyMasterProcessChannelDecoder( newBufferedChannel( newInputStream( clientSocketChannel ) ) );
+ ReadableByteChannel bufferedChannel = newBufferedChannel( newInputStream( clientSocketChannel ) );
+ return new CommandChannelDecoder( bufferedChannel, forkingArguments );
}
@Override
- public MasterProcessChannelEncoder createEncoder()
+ public MasterProcessChannelEncoder createEncoder( @Nonnull ForkNodeArguments forkingArguments )
{
WritableBufferedByteChannel channel = newBufferedChannel( newOutputStream( clientSocketChannel ) );
schedulePeriodicFlusher( FLUSH_PERIOD_MILLIS, channel );
- return new LegacyMasterProcessChannelEncoder( channel );
+ return new EventChannelEncoder( channel );
}
@Override
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
new file mode 100644
index 0000000..b660147
--- /dev/null
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/CommandDecoder.java
@@ -0,0 +1,276 @@
+package org.apache.maven.surefire.booter.stream;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.api.booter.Command;
+import org.apache.maven.surefire.api.booter.MasterProcessCommand;
+import org.apache.maven.surefire.api.booter.Shutdown;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.RunMode;
+import org.apache.maven.surefire.api.stream.AbstractStreamDecoder;
+import org.apache.maven.surefire.api.stream.MalformedChannelException;
+import org.apache.maven.surefire.api.stream.SegmentType;
+
+import javax.annotation.Nonnull;
+import java.io.BufferedOutputStream;
+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;
+import static org.apache.maven.surefire.api.booter.Command.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.api.booter.Command.TEST_SET_FINISHED;
+import static org.apache.maven.surefire.api.booter.Command.toRunClass;
+import static org.apache.maven.surefire.api.booter.Command.toShutdown;
+import static org.apache.maven.surefire.api.booter.Constants.MAGIC_NUMBER_FOR_COMMANDS_BYTES;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.COMMAND_TYPES;
+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;
+
+/**
+ *
+ */
+public class CommandDecoder extends AbstractStreamDecoder<Command, MasterProcessCommand, SegmentType>
+{
+ private static final int DEBUG_SINK_BUFFER_SIZE = 64 * 1024;
+ private static final int NO_POSITION = -1;
+
+ private static final SegmentType[] COMMAND_WITHOUT_DATA = new SegmentType[] {
+ 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,
+ END_OF_FRAME
+ };
+
+ 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
+ public Command decode( @Nonnull Memento memento ) throws IOException, MalformedChannelException
+ {
+ try
+ {
+ MasterProcessCommand commandType = readMessageType( memento );
+ if ( commandType == null )
+ {
+ throw new MalformedFrameException( memento.getLine().getPositionByteBuffer(),
+ memento.getByteBuffer().position() );
+ }
+ RunMode runMode = null;
+ for ( SegmentType segmentType : nextSegmentType( commandType ) )
+ {
+ switch ( segmentType )
+ {
+ case RUN_MODE:
+ runMode = RUN_MODES.get( readSegment( memento ) );
+ break;
+ case STRING_ENCODING:
+ memento.setCharset( readCharset( memento ) );
+ break;
+ case DATA_STRING:
+ memento.getData().add( readString( memento ) );
+ break;
+ case DATA_INTEGER:
+ memento.getData().add( readInteger( memento ) );
+ break;
+ case END_OF_FRAME:
+ memento.getLine().setPositionByteBuffer( memento.getByteBuffer().position() );
+ return toMessage( commandType, runMode, memento );
+ default:
+ memento.getLine().setPositionByteBuffer( NO_POSITION );
+ arguments.dumpStreamText( "Unknown enum ("
+ + SegmentType.class.getSimpleName()
+ + ") "
+ + segmentType );
+ }
+ }
+ }
+ catch ( MalformedFrameException e )
+ {
+ if ( e.hasValidPositions() )
+ {
+ int length = e.readTo() - e.readFrom();
+ memento.getLine().write( memento.getByteBuffer(), e.readFrom(), length );
+ }
+ return null;
+ }
+ catch ( RuntimeException e )
+ {
+ getArguments().dumpStreamException( e );
+ return null;
+ }
+ catch ( IOException e )
+ {
+ if ( !( e.getCause() instanceof InterruptedException ) )
+ {
+ printRemainingStream( memento );
+ }
+ throw e;
+ }
+ finally
+ {
+ memento.reset();
+ }
+
+ throw new MalformedChannelException();
+ }
+
+ @Nonnull
+ @Override
+ protected final byte[] getEncodedMagicNumber()
+ {
+ return MAGIC_NUMBER_FOR_COMMANDS_BYTES;
+ }
+
+ @Nonnull
+ @Override
+ protected SegmentType[] nextSegmentType( @Nonnull MasterProcessCommand commandType )
+ {
+ switch ( commandType )
+ {
+ case NOOP:
+ case BYE_ACK:
+ case SKIP_SINCE_NEXT_TEST:
+ case TEST_SET_FINISHED:
+ return COMMAND_WITHOUT_DATA;
+ case RUN_CLASS:
+ return COMMAND_WITH_RUNNABLE_STRING;
+ case SHUTDOWN:
+ return COMMAND_WITH_ONE_STRING;
+ default:
+ throw new IllegalArgumentException( "Unknown enum " + commandType );
+ }
+ }
+
+ @Nonnull
+ @Override
+ protected Command toMessage( @Nonnull MasterProcessCommand commandType, RunMode runMode, @Nonnull Memento memento )
+ throws MalformedFrameException
+ {
+ switch ( commandType )
+ {
+ case NOOP:
+ checkArguments( memento, 0 );
+ return NOOP;
+ case BYE_ACK:
+ checkArguments( memento, 0 );
+ return BYE_ACK;
+ case SKIP_SINCE_NEXT_TEST:
+ checkArguments( memento, 0 );
+ return SKIP_SINCE_NEXT_TEST;
+ case TEST_SET_FINISHED:
+ checkArguments( memento, 0 );
+ return TEST_SET_FINISHED;
+ case RUN_CLASS:
+ checkArguments( memento, 1 );
+ return toRunClass( (String) memento.getData().get( 0 ) );
+ case SHUTDOWN:
+ checkArguments( memento, 1 );
+ return toShutdown( Shutdown.parameterOf( (String) memento.getData().get( 0 ) ) );
+ default:
+ 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
+ {
+ OutputStream fos = new FileOutputStream( sink, true );
+ final OutputStream os = new BufferedOutputStream( fos, DEBUG_SINK_BUFFER_SIZE );
+ 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;
+ }
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ if ( debugSink != null )
+ {
+ debugSink.close();
+ }
+ }
+}
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/EventEncoder.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/EventEncoder.java
new file mode 100644
index 0000000..f982017
--- /dev/null
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/EventEncoder.java
@@ -0,0 +1,78 @@
+package org.apache.maven.surefire.booter.stream;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.api.booter.ForkedProcessEventType;
+import org.apache.maven.surefire.api.stream.AbstractStreamEncoder;
+import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
+
+import javax.annotation.Nonnull;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING;
+import static org.apache.maven.surefire.api.booter.Constants.DEFAULT_STREAM_ENCODING_BYTES;
+import static org.apache.maven.surefire.api.booter.Constants.MAGIC_NUMBER_FOR_EVENTS_BYTES;
+
+/**
+ *
+ */
+public class EventEncoder extends AbstractStreamEncoder<ForkedProcessEventType>
+{
+ public EventEncoder( WritableBufferedByteChannel out )
+ {
+ super( out );
+ }
+
+ @Nonnull
+ @Override
+ protected final byte[] getEncodedMagicNumber()
+ {
+ return MAGIC_NUMBER_FOR_EVENTS_BYTES;
+ }
+
+ @Nonnull
+ @Override
+ protected final byte[] enumToByteArray( @Nonnull ForkedProcessEventType e )
+ {
+ return e.getOpcodeBinary();
+ }
+
+ @Nonnull
+ @Override
+ protected final byte[] getEncodedCharsetName()
+ {
+ return DEFAULT_STREAM_ENCODING_BYTES;
+ }
+
+ @Nonnull
+ @Override
+ protected final Charset getCharset()
+ {
+ return DEFAULT_STREAM_ENCODING;
+ }
+
+ @Nonnull
+ @Override
+ protected final CharsetEncoder newCharsetEncoder()
+ {
+ return getCharset().newEncoder();
+ }
+}
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 5bc5ffe..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
@@ -23,8 +23,9 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
import org.apache.maven.surefire.api.booter.Shutdown;
-import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelDecoder;
-import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.booter.spi.CommandChannelDecoder;
+import org.apache.maven.surefire.booter.spi.EventChannelEncoder;
import org.apache.maven.surefire.api.testset.TestSetFailedException;
import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
import org.junit.After;
@@ -45,6 +46,7 @@ import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
import static org.apache.maven.surefire.api.util.internal.Channels.newChannel;
import static org.fest.assertions.Assertions.assertThat;
@@ -61,6 +63,7 @@ import static org.junit.Assert.fail;
* @since 2.19
*/
@RunWith( NewClassLoaderRunner.class )
+@SuppressWarnings( "checkstyle:magicnumber" )
public class CommandReaderTest
{
private static final long DELAY = 200L;
@@ -93,7 +96,9 @@ public class CommandReaderTest
InputStream realInputStream = new SystemInputStream();
addTestToPipeline( getClass().getName() );
ConsoleLogger logger = new NullConsoleLogger();
- MasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( realInputStream ) );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ MasterProcessChannelDecoder decoder =
+ new CommandChannelDecoder( newChannel( realInputStream ), args );
reader = new CommandReader( decoder, Shutdown.DEFAULT, logger );
}
@@ -106,7 +111,7 @@ public class CommandReaderTest
@Test
public void readJustOneClass()
{
- Iterator<String> it = reader.getIterableClasses( new LegacyMasterProcessChannelEncoder( nul() ) ).iterator();
+ Iterator<String> it = reader.getIterableClasses( new EventChannelEncoder( nul() ) ).iterator();
assertTrue( it.hasNext() );
assertThat( it.next(), is( getClass().getName() ) );
reader.stop();
@@ -125,7 +130,7 @@ public class CommandReaderTest
@Test
public void manyClasses()
{
- Iterator<String> it1 = reader.getIterableClasses( new LegacyMasterProcessChannelEncoder( nul() ) ).iterator();
+ Iterator<String> it1 = reader.getIterableClasses( new EventChannelEncoder( nul() ) ).iterator();
assertThat( it1.next(), is( getClass().getName() ) );
addTestToPipeline( A.class.getName() );
assertThat( it1.next(), is( A.class.getName() ) );
@@ -141,7 +146,7 @@ public class CommandReaderTest
@Test
public void twoIterators() throws Exception
{
- Iterator<String> it1 = reader.getIterableClasses( new LegacyMasterProcessChannelEncoder( nul() ) ).iterator();
+ Iterator<String> it1 = reader.getIterableClasses( new EventChannelEncoder( nul() ) ).iterator();
assertThat( it1.next(), is( getClass().getName() ) );
addTestToPipeline( A.class.getName() );
@@ -174,8 +179,7 @@ public class CommandReaderTest
@Override
public void run()
{
- Iterator<String> it =
- reader.getIterableClasses( new LegacyMasterProcessChannelEncoder( nul() ) ).iterator();
+ Iterator<String> it = reader.getIterableClasses( new EventChannelEncoder( nul() ) ).iterator();
assertThat( it.next(), is( CommandReaderTest.class.getName() ) );
}
};
@@ -203,7 +207,7 @@ public class CommandReaderTest
public void run()
{
Iterator<String> it =
- reader.getIterableClasses( new LegacyMasterProcessChannelEncoder( nul() ) ).iterator();
+ reader.getIterableClasses( new EventChannelEncoder( nul() ) ).iterator();
assertThat( it.next(), is( CommandReaderTest.class.getName() ) );
counter.countDown();
assertThat( it.next(), is( Foo.class.getName() ) );
@@ -250,7 +254,25 @@ public class CommandReaderTest
private void addTestToPipeline( String cls )
{
- for ( byte cmdByte : ( ":maven-surefire-command:run-testclass:" + cls + ":" ).getBytes() )
+ int clsLength = cls.length();
+ String cmd = new StringBuilder( 512 )
+ .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 ) )
+ .append( (char) ( ( clsLength >> 16 ) & 0xff ) )
+ .append( (char) ( ( clsLength >> 8 ) & 0xff ) )
+ .append( (char) ( clsLength & 0xff ) )
+ .append( ":" )
+ .append( cls )
+ .append( ":" )
+ .toString();
+
+ for ( byte cmdByte : cmd.getBytes( US_ASCII ) )
{
blockingStream.add( cmdByte );
}
@@ -258,7 +280,7 @@ public class CommandReaderTest
private void addEndOfPipeline()
{
- for ( byte cmdByte : ":maven-surefire-command:testset-finished:".getBytes() )
+ for ( byte cmdByte : ( ":maven-surefire-command:" + (char) 16 + ":testset-finished:" ).getBytes( US_ASCII ) )
{
blockingStream.add( cmdByte );
}
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 c1140c8..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
@@ -21,11 +21,12 @@ package org.apache.maven.surefire.booter;
import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
import org.apache.maven.surefire.api.report.StackTraceWriter;
import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
import org.apache.maven.surefire.booter.spi.AbstractMasterProcessChannelProcessorFactory;
-import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelDecoder;
-import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder;
+import org.apache.maven.surefire.booter.spi.CommandChannelDecoder;
+import org.apache.maven.surefire.booter.spi.EventChannelEncoder;
import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelProcessorFactory;
import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
import org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils;
@@ -44,6 +45,7 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
+import javax.annotation.Nonnull;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
@@ -86,7 +88,7 @@ import static org.powermock.reflect.Whitebox.setInternalState;
@PrepareForTest( {
PpidChecker.class,
ForkedBooter.class,
- LegacyMasterProcessChannelEncoder.class,
+ EventChannelEncoder.class,
ShutdownHookUtils.class
} )
@PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
@@ -105,7 +107,7 @@ public class ForkedBooterMockTest
private MasterProcessChannelProcessorFactory channelProcessorFactory;
@Mock
- private LegacyMasterProcessChannelEncoder eventChannel;
+ private EventChannelEncoder eventChannel;
@Captor
private ArgumentCaptor<StackTraceWriter> capturedStackTraceWriter;
@@ -292,10 +294,11 @@ public class ForkedBooterMockTest
factory.connect( "pipe://3" );
- MasterProcessChannelDecoder decoder = factory.createDecoder();
- assertThat( decoder ).isInstanceOf( LegacyMasterProcessChannelDecoder.class );
- MasterProcessChannelEncoder encoder = factory.createEncoder();
- assertThat( encoder ).isInstanceOf( LegacyMasterProcessChannelEncoder.class );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ MasterProcessChannelDecoder decoder = factory.createDecoder( args );
+ assertThat( decoder ).isInstanceOf( CommandChannelDecoder.class );
+ MasterProcessChannelEncoder encoder = factory.createEncoder( args );
+ assertThat( encoder ).isInstanceOf( EventChannelEncoder.class );
}
}
@@ -318,13 +321,13 @@ public class ForkedBooterMockTest
}
@Override
- public MasterProcessChannelDecoder createDecoder()
+ public MasterProcessChannelDecoder createDecoder( @Nonnull ForkNodeArguments args )
{
return null;
}
@Override
- public MasterProcessChannelEncoder createEncoder()
+ public MasterProcessChannelEncoder createEncoder( @Nonnull ForkNodeArguments args )
{
return null;
}
@@ -411,13 +414,13 @@ public class ForkedBooterMockTest
} );
factory.connect( "tcp://localhost:" + serverPort );
-
- MasterProcessChannelDecoder decoder = factory.createDecoder();
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ MasterProcessChannelDecoder decoder = factory.createDecoder( args );
assertThat( decoder )
- .isInstanceOf( LegacyMasterProcessChannelDecoder.class );
- MasterProcessChannelEncoder encoder = factory.createEncoder();
+ .isInstanceOf( CommandChannelDecoder.class );
+ MasterProcessChannelEncoder encoder = factory.createEncoder( args );
assertThat( encoder )
- .isInstanceOf( LegacyMasterProcessChannelEncoder.class );
+ .isInstanceOf( EventChannelEncoder.class );
}
}
}
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
index 280774e..6c14e0e 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
@@ -23,8 +23,8 @@ import junit.framework.JUnit4TestAdapter;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
-import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelDecoderTest;
-import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoderTest;
+import org.apache.maven.surefire.booter.spi.CommandChannelDecoderTest;
+import org.apache.maven.surefire.booter.spi.EventChannelEncoderTest;
/**
* Adapt the JUnit4 tests which use only annotations to the JUnit3 test suite.
@@ -46,8 +46,8 @@ public class JUnit4SuiteTest extends TestCase
suite.addTest( new JUnit4TestAdapter( BooterDeserializerTest.class ) );
suite.addTestSuite( ClasspathTest.class );
suite.addTestSuite( PropertiesWrapperTest.class );
- suite.addTest( new JUnit4TestAdapter( LegacyMasterProcessChannelDecoderTest.class ) );
- suite.addTest( new JUnit4TestAdapter( LegacyMasterProcessChannelEncoderTest.class ) );
+ suite.addTest( new JUnit4TestAdapter( CommandChannelDecoderTest.class ) );
+ suite.addTest( new JUnit4TestAdapter( EventChannelEncoderTest.class ) );
suite.addTestSuite( SurefireReflectorTest.class );
return suite;
}
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
new file mode 100644
index 0000000..f31d9d8
--- /dev/null
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java
@@ -0,0 +1,519 @@
+package org.apache.maven.surefire.booter.spi;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * 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;
+import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.booter.ForkedNodeArg;
+import org.junit.Before;
+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;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.BYE_ACK;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.NOOP;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.RUN_CLASS;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.SHUTDOWN;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.api.booter.MasterProcessCommand.TEST_SET_FINISHED;
+import static org.apache.maven.surefire.api.booter.Shutdown.DEFAULT;
+import static org.apache.maven.surefire.api.booter.Shutdown.EXIT;
+import static org.apache.maven.surefire.api.booter.Shutdown.KILL;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests for {@link CommandChannelDecoder}.
+ */
+@SuppressWarnings( "checkstyle:magicnumber" )
+public class CommandChannelDecoderTest
+{
+ @Rule
+ public final TemporaryFolder tempFolder = new TemporaryFolder();
+
+ private File reportsDir;
+ private String dumpFileName;
+
+ @Before
+ public void initTmpFile() throws IOException
+ {
+ File tmp = tempFolder.newFile();
+ reportsDir = tmp.getParentFile();
+ dumpFileName = tmp.getName();
+ }
+
+ @Test
+ public void testDecoderRunClass() throws IOException
+ {
+ assertEquals( String.class, RUN_CLASS.getDataType() );
+ byte[] encoded = new StringBuilder( 512 )
+ .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 )
+ .append( (char) 0 )
+ .append( (char) 0 )
+ .append( (char) 8 )
+ .append( ":" )
+ .append( "pkg.Test" )
+ .append( ":" )
+ .toString()
+ .getBytes( UTF_8 );
+ InputStream is = new ByteArrayInputStream( encoded );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ Command command = decoder.decode();
+ assertThat( command.getCommandType() ).isSameAs( RUN_CLASS );
+ assertThat( command.getData() ).isEqualTo( "pkg.Test" );
+ }
+
+ @Test
+ public void testDecoderTestsetFinished() throws IOException
+ {
+ Command command = Command.TEST_SET_FINISHED;
+ assertThat( command.getCommandType() ).isSameAs( TEST_SET_FINISHED );
+ assertEquals( Void.class, TEST_SET_FINISHED.getDataType() );
+ byte[] encoded = ":maven-surefire-command:\u0010:testset-finished:".getBytes();
+ ByteArrayInputStream is = new ByteArrayInputStream( encoded );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ command = decoder.decode();
+ assertThat( command.getCommandType() ).isSameAs( TEST_SET_FINISHED );
+ assertNull( command.getData() );
+ }
+
+ @Test
+ public void testDecoderSkipSinceNextTest() throws IOException
+ {
+ Command command = Command.SKIP_SINCE_NEXT_TEST;
+ assertThat( command.getCommandType() ).isSameAs( SKIP_SINCE_NEXT_TEST );
+ assertEquals( Void.class, SKIP_SINCE_NEXT_TEST.getDataType() );
+ 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, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ command = decoder.decode();
+ assertThat( command.getCommandType() ).isSameAs( SKIP_SINCE_NEXT_TEST );
+ assertNull( command.getData() );
+ }
+
+ @Test
+ public void testDecoderShutdownWithExit() throws IOException
+ {
+ Shutdown shutdownType = EXIT;
+ assertEquals( String.class, SHUTDOWN.getDataType() );
+ byte[] encoded = ( ":maven-surefire-command:\u0008:shutdown:\u0005:UTF-8:\u0000\u0000\u0000\u0004:"
+ + shutdownType.getParam() + ":" ).getBytes();
+ ByteArrayInputStream is = new ByteArrayInputStream( encoded );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ Command command = decoder.decode();
+ assertThat( command.getCommandType() ).isSameAs( SHUTDOWN );
+ assertThat( command.getData() ).isEqualTo( shutdownType.name() );
+ }
+
+ @Test
+ public void testDecoderShutdownWithKill() throws IOException
+ {
+ Shutdown shutdownType = KILL;
+ assertEquals( String.class, SHUTDOWN.getDataType() );
+ byte[] encoded = ( ":maven-surefire-command:\u0008:shutdown:\u0005:UTF-8:\u0000\u0000\u0000\u0004:"
+ + shutdownType.getParam() + ":" ).getBytes();
+ ByteArrayInputStream is = new ByteArrayInputStream( encoded );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ Command command = decoder.decode();
+ assertThat( command.getCommandType() ).isSameAs( SHUTDOWN );
+ assertThat( command.getData() ).isEqualTo( shutdownType.name() );
+ }
+
+ @Test
+ public void testDecoderShutdownWithDefault() throws IOException
+ {
+ Shutdown shutdownType = DEFAULT;
+ assertEquals( String.class, SHUTDOWN.getDataType() );
+ byte[] encoded = ( ":maven-surefire-command:\u0008:shutdown:\u0005:UTF-8:\u0000\u0000\u0000\u0007:"
+ + shutdownType.getParam() + ":" ).getBytes();
+ ByteArrayInputStream is = new ByteArrayInputStream( encoded );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ Command command = decoder.decode();
+ assertThat( command.getCommandType() ).isSameAs( SHUTDOWN );
+ assertThat( command.getData() ).isEqualTo( shutdownType.name() );
+ }
+
+ @Test
+ public void testDecoderNoop() throws IOException
+ {
+ assertThat( NOOP ).isSameAs( Command.NOOP.getCommandType() );
+ assertEquals( Void.class, NOOP.getDataType() );
+ byte[] encoded = ":maven-surefire-command:\u0004:noop:".getBytes();
+ ByteArrayInputStream is = new ByteArrayInputStream( encoded );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ 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 shouldIgnoreDamagedStream() throws IOException
+ {
+ assertThat( BYE_ACK ).isSameAs( Command.BYE_ACK.getCommandType() );
+ assertEquals( Void.class, BYE_ACK.getDataType() );
+ byte[] encoded = ":maven-surefire-command:\u0007:bye-ack:".getBytes();
+ byte[] streamContent = ( "<something>" + new String( encoded ) + "<damaged>" ).getBytes();
+ ByteArrayInputStream is = new ByteArrayInputStream( streamContent );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ Command command = decoder.decode();
+ assertThat( command.getCommandType() ).isSameAs( BYE_ACK );
+ assertNull( command.getData() );
+ }
+
+ @Test
+ public void shouldIgnoreDamagedHeader() throws IOException
+ {
+ assertThat( BYE_ACK ).isSameAs( Command.BYE_ACK.getCommandType() );
+ assertEquals( Void.class, BYE_ACK.getDataType() );
+ byte[] encoded = ":maven-surefire-command:\u0007:bye-ack:".getBytes();
+ byte[] streamContent = ( ":<damaged>:" + new String( encoded ) ).getBytes();
+ ByteArrayInputStream is = new ByteArrayInputStream( streamContent );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ Command command = decoder.decode();
+ assertThat( command.getCommandType() ).isSameAs( BYE_ACK );
+ assertNull( command.getData() );
+ }
+
+ @Test
+ public void testDecoderByeAck() throws IOException
+ {
+ assertThat( BYE_ACK ).isSameAs( Command.BYE_ACK.getCommandType() );
+ assertEquals( Void.class, BYE_ACK.getDataType() );
+ byte[] encoded = ":maven-surefire-command:\u0007:bye-ack:".getBytes();
+ ByteArrayInputStream is = new ByteArrayInputStream( encoded );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ Command command = decoder.decode();
+ assertThat( command.getCommandType() ).isSameAs( BYE_ACK );
+ assertNull( command.getData() );
+ }
+
+ @Test
+ public void shouldDecodeTwoCommands() throws IOException
+ {
+ 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, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+
+ Command command = decoder.decode();
+ assertThat( command.getCommandType() ).isEqualTo( BYE_ACK );
+ assertThat( command.getData() ).isNull();
+
+ command = decoder.decode();
+ assertThat( command.getCommandType() ).isEqualTo( BYE_ACK );
+ assertThat( command.getData() ).isNull();
+
+ decoder.close();
+ }
+
+ @Test( expected = EOFException.class )
+ public void testIncompleteCommand() throws IOException
+ {
+
+ ByteArrayInputStream is = new ByteArrayInputStream( ":maven-surefire-command:".getBytes() );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ decoder.decode();
+ fail();
+ }
+
+ @Test( expected = EOFException.class )
+ public void testIncompleteCommandStart() throws IOException
+ {
+
+ ByteArrayInputStream is = new ByteArrayInputStream( new byte[] {':', '\r'} );
+ DumpErrorSingleton.getSingleton().init( reportsDir, dumpFileName );
+ ForkNodeArguments args = new ForkedNodeArg( 1, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+ decoder.decode();
+ fail();
+ }
+
+ @Test( expected = EOFException.class )
+ public void shouldNotDecodeCorruptedCommand() throws IOException
+ {
+ 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, false );
+ CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( is ), args );
+
+ decoder.decode();
+ }
+
+ @Test
+ public void shouldSkipCorruptedCommand() throws IOException
+ {
+ 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, 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/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java
similarity index 81%
rename from surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoderTest.java
rename to surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java
index 11f2d16..44abbf9 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoderTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java
@@ -29,152 +29,33 @@ import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.LineNumberReader;
import java.io.PrintStream;
-import java.io.StringReader;
import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
import java.util.Map;
import java.util.Map.Entry;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.copyOfRange;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_BYE;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_DEBUG;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_ERROR;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_INFO;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_WARNING;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_JVM_EXIT_ERROR;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_NEXT_TEST;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDERR;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDERR_NEW_LINE;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT_NEW_LINE;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STOP_ON_NEXT_TEST;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_SYSPROPS;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_COMPLETED;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_STARTING;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ASSUMPTIONFAILURE;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ERROR;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_FAILED;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SKIPPED;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_STARTING;
-import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SUCCEEDED;
import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
-import static org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder.encode;
-import static org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder.encodeHeader;
-import static org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder.encodeMessage;
-import static org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder.encodeOpcode;
-import static org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder.estimateBufferLength;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
- * Test for {@link LegacyMasterProcessChannelEncoder}.
+ * Test for {@link EventChannelEncoder}.
*
* @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
* @since 3.0.0-M4
*/
@SuppressWarnings( { "checkstyle:linelength", "checkstyle:magicnumber" } )
-public class LegacyMasterProcessChannelEncoderTest
+public class EventChannelEncoderTest
{
private static final int ELAPSED_TIME = 102;
private static final byte[] ELAPSED_TIME_HEXA = new byte[] {0, 0, 0, 0x66};
@Test
- public void shouldComputeStreamPreemptiveLength()
- {
- CharsetEncoder encoder = UTF_8.newEncoder();
-
- // :maven-surefire-event:8:sys-prop:10:normal-run:5:UTF-8:0001:kkk:0001:vvv:
- assertThat( estimateBufferLength( BOOTERCODE_SYSPROPS, NORMAL_RUN, encoder, 0, "k", "v" ) )
- .isEqualTo( 72 );
-
- // :maven-surefire-event:16:testset-starting:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_TESTSET_STARTING, NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
- .isEqualTo( 149 );
-
- // :maven-surefire-event:17:testset-completed:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_TESTSET_COMPLETED, NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
- .isEqualTo( 150 );
-
- // :maven-surefire-event:13:test-starting:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_TEST_STARTING, NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
- .isEqualTo( 146 );
-
- // :maven-surefire-event:14:test-succeeded:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_TEST_SUCCEEDED, NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
- .isEqualTo( 147 );
-
- // :maven-surefire-event:11:test-failed:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_TEST_FAILED, NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
- .isEqualTo( 144 );
-
- // :maven-surefire-event:12:test-skipped:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_TEST_SKIPPED, NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
- .isEqualTo( 145 );
-
- // :maven-surefire-event:10:test-error:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_TEST_ERROR, NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
- .isEqualTo( 143 );
-
- // :maven-surefire-event:23:test-assumption-failure:10:normal-run:5:UTF-8:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:0001:sss:X0001:0001:sss:0001:sss:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_TEST_ASSUMPTIONFAILURE, NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
- .isEqualTo( 156 );
-
- // :maven-surefire-event:14:std-out-stream:10:normal-run:5:UTF-8:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_STDOUT, NORMAL_RUN, encoder, 0, "s" ) )
- .isEqualTo( 69 );
-
- // :maven-surefire-event:23:std-out-stream-new-line:10:normal-run:5:UTF-8:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_STDOUT_NEW_LINE, NORMAL_RUN, encoder, 0, "s" ) )
- .isEqualTo( 78 );
-
- // :maven-surefire-event:14:std-err-stream:10:normal-run:5:UTF-8:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_STDERR, NORMAL_RUN, encoder, 0, "s" ) )
- .isEqualTo( 69 );
-
- // :maven-surefire-event:23:std-err-stream-new-line:10:normal-run:5:UTF-8:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_STDERR_NEW_LINE, NORMAL_RUN, encoder, 0, "s" ) )
- .isEqualTo( 78 );
-
- // :maven-surefire-event:16:console-info-log:5:UTF-8:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_CONSOLE_INFO, null, encoder, 0, "s" ) )
- .isEqualTo( 58 );
-
- // :maven-surefire-event:17:console-debug-log:5:UTF-8:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_CONSOLE_DEBUG, null, encoder, 0, "s" ) )
- .isEqualTo( 59 );
-
- // :maven-surefire-event:19:console-warning-log:5:UTF-8:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_CONSOLE_WARNING, null, encoder, 0, "s" ) )
- .isEqualTo( 61 );
-
- // :maven-surefire-event:17:console-error-log:5:UTF-8:0001:sss:
- assertThat( estimateBufferLength( BOOTERCODE_CONSOLE_ERROR, null, encoder, 0, "s" ) )
- .isEqualTo( 59 );
-
- // :maven-surefire-event:3:bye:
- assertThat( estimateBufferLength( BOOTERCODE_BYE, null, null, 0 ) )
- .isEqualTo( 28 );
-
- // :maven-surefire-event:17:stop-on-next-test:
- assertThat( estimateBufferLength( BOOTERCODE_STOP_ON_NEXT_TEST, null, null, 0 ) )
- .isEqualTo( 42 );
-
- // :maven-surefire-event:9:next-test:
- assertThat( estimateBufferLength( BOOTERCODE_NEXT_TEST, null, null, 0 ) )
- .isEqualTo( 34 );
-
- // :maven-surefire-event:14:jvm-exit-error:
- assertThat( estimateBufferLength( BOOTERCODE_JVM_EXIT_ERROR, null, null, 0 ) )
- .isEqualTo( 39 );
- }
-
- @Test
public void reportEntry() throws IOException
{
final String exceptionMessage = "msg";
@@ -198,7 +79,9 @@ public class LegacyMasterProcessChannelEncoderTest
when( reportEntry.getSourceName() ).thenReturn( "pkg.MyTest" );
when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
- ByteBuffer encoded = encode( BOOTERCODE_TEST_ERROR, NORMAL_RUN, reportEntry, false );
+ Stream out = Stream.newStream();
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
+ ByteBuffer encoded = encoder.encode( BOOTERCODE_TEST_ERROR, NORMAL_RUN, reportEntry, false );
ByteArrayOutputStream expectedFrame = new ByteArrayOutputStream();
expectedFrame.write( ":maven-surefire-event:".getBytes( UTF_8 ) );
expectedFrame.write( (byte) 10 );
@@ -251,7 +134,9 @@ public class LegacyMasterProcessChannelEncoderTest
assertThat( toArray( encoded ) )
.isEqualTo( expectedFrame.toByteArray() );
- encoded = encode( BOOTERCODE_TEST_ERROR, NORMAL_RUN, reportEntry, true );
+ out = Stream.newStream();
+ encoder = new EventChannelEncoder( newBufferedChannel( out ) );
+ encoded = encoder.encode( BOOTERCODE_TEST_ERROR, NORMAL_RUN, reportEntry, true );
expectedFrame = new ByteArrayOutputStream();
expectedFrame.write( ":maven-surefire-event:".getBytes( UTF_8 ) );
expectedFrame.write( (byte) 10 );
@@ -304,9 +189,8 @@ public class LegacyMasterProcessChannelEncoderTest
assertThat( toArray( encoded ) )
.isEqualTo( expectedFrame.toByteArray() );
- Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
-
+ out = Stream.newStream();
+ encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.testSetStarting( reportEntry, true );
expectedFrame = new ByteArrayOutputStream();
expectedFrame.write( ":maven-surefire-event:".getBytes( UTF_8 ) );
@@ -359,7 +243,7 @@ public class LegacyMasterProcessChannelEncoderTest
.isEqualTo( expectedFrame.toByteArray() );
out = Stream.newStream();
- encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.testSetStarting( reportEntry, false );
expectedFrame = new ByteArrayOutputStream();
@@ -438,7 +322,7 @@ public class LegacyMasterProcessChannelEncoderTest
when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.testSetCompleted( reportEntry, false );
ByteArrayOutputStream expectedFrame = new ByteArrayOutputStream();
@@ -517,7 +401,7 @@ public class LegacyMasterProcessChannelEncoderTest
when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.testStarting( reportEntry, true );
@@ -597,7 +481,7 @@ public class LegacyMasterProcessChannelEncoderTest
when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.testSucceeded( reportEntry, true );
ByteArrayOutputStream expectedFrame = new ByteArrayOutputStream();
@@ -676,7 +560,7 @@ public class LegacyMasterProcessChannelEncoderTest
when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.testFailed( reportEntry, false );
ByteArrayOutputStream expectedFrame = new ByteArrayOutputStream();
@@ -754,7 +638,7 @@ public class LegacyMasterProcessChannelEncoderTest
when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.testSkipped( reportEntry, false );
ByteArrayOutputStream expectedFrame = new ByteArrayOutputStream();
@@ -832,7 +716,7 @@ public class LegacyMasterProcessChannelEncoderTest
when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.testError( reportEntry, false );
ByteArrayOutputStream expectedFrame = new ByteArrayOutputStream();
expectedFrame.write( ":maven-surefire-event:".getBytes( UTF_8 ) );
@@ -909,7 +793,7 @@ public class LegacyMasterProcessChannelEncoderTest
when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.testAssumptionFailure( reportEntry, false );
ByteArrayOutputStream expectedFrame = new ByteArrayOutputStream();
@@ -966,7 +850,7 @@ public class LegacyMasterProcessChannelEncoderTest
public void testBye()
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.bye();
@@ -980,7 +864,7 @@ public class LegacyMasterProcessChannelEncoderTest
public void testStopOnNextTest()
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.stopOnNextTest();
@@ -993,7 +877,7 @@ public class LegacyMasterProcessChannelEncoderTest
public void testAcquireNextTest()
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.acquireNextTest();
@@ -1005,41 +889,30 @@ public class LegacyMasterProcessChannelEncoderTest
@Test
public void testSendOpcode()
{
- CharsetEncoder encoder = UTF_8.newEncoder();
- ByteBuffer result = ByteBuffer.allocate( 128 );
- encodeOpcode( result, BOOTERCODE_TEST_ERROR, NORMAL_RUN );
- assertThat( toString( result ) )
- .isEqualTo( ":maven-surefire-event:" + (char) 10 + ":test-error:" + (char) 10 + ":normal-run:" );
-
- result = ByteBuffer.allocate( 1024 );
- encodeHeader( encoder, result, BOOTERCODE_TEST_ERROR, NORMAL_RUN );
- assertThat( toString( result ) )
- .isEqualTo( ":maven-surefire-event:" + (char) 10 + ":test-error:" + (char) 10 + ":normal-run:"
- + (char) 5 + ":UTF-8:" );
-
- result = encodeMessage( BOOTERCODE_TEST_ERROR, NORMAL_RUN, "msg" );
- assertThat( toString( result ) )
- .isEqualTo( ":maven-surefire-event:" + (char) 10 + ":test-error:" + (char) 10 + ":normal-run:"
- + (char) 5 + ":UTF-8:\u0000\u0000\u0000\u0003:msg:" );
-
Channel channel = new Channel();
- new LegacyMasterProcessChannelEncoder( channel ).stdOut( "msg", false );
+ new EventChannelEncoder( channel ).stdOut( "msg", false );
assertThat( toString( channel.src ) )
.isEqualTo( ":maven-surefire-event:" + (char) 14 + ":std-out-stream:" + (char) 10 + ":normal-run:"
+ (char) 5 + ":UTF-8:\u0000\u0000\u0000\u0003:msg:" );
channel = new Channel();
- new LegacyMasterProcessChannelEncoder( channel ).stdErr( null, false );
+ new EventChannelEncoder( channel ).stdErr( null, false );
assertThat( toString( channel.src ) )
.isEqualTo( ":maven-surefire-event:" + (char) 14 + ":std-err-stream:" + (char) 10 + ":normal-run:"
+ (char) 5 + ":UTF-8:\u0000\u0000\u0000\u0001:\u0000:" );
+
+ ByteBuffer result = new EventChannelEncoder( new Channel() )
+ .encodeMessage( BOOTERCODE_TEST_ERROR, NORMAL_RUN, "msg" );
+ assertThat( toString( result ) )
+ .isEqualTo( ":maven-surefire-event:" + (char) 10 + ":test-error:" + (char) 10 + ":normal-run:"
+ + (char) 5 + ":UTF-8:\u0000\u0000\u0000\u0003:msg:" );
}
@Test
public void testConsoleInfo()
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.consoleInfoLog( "msg" );
@@ -1055,7 +928,7 @@ public class LegacyMasterProcessChannelEncoderTest
public void testConsoleError()
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.consoleErrorLog( "msg" );
@@ -1074,7 +947,7 @@ public class LegacyMasterProcessChannelEncoderTest
public void testConsoleErrorLog1() throws IOException
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
Exception e = new Exception( "msg" );
encoder.consoleErrorLog( e );
@@ -1112,7 +985,7 @@ public class LegacyMasterProcessChannelEncoderTest
public void testConsoleErrorLog2() throws IOException
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
Exception e = new Exception( "msg" );
encoder.consoleErrorLog( "msg2", e );
@@ -1150,7 +1023,7 @@ public class LegacyMasterProcessChannelEncoderTest
public void testConsoleErrorLog3()
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
StackTraceWriter stackTraceWriter = mock( StackTraceWriter.class );
when( stackTraceWriter.getThrowable() ).thenReturn( new SafeThrowable( "1" ) );
@@ -1168,7 +1041,7 @@ public class LegacyMasterProcessChannelEncoderTest
public void testConsoleDebug()
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.consoleDebugLog( "msg" );
@@ -1184,7 +1057,7 @@ public class LegacyMasterProcessChannelEncoderTest
public void testConsoleWarning()
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
encoder.consoleWarningLog( "msg" );
@@ -1201,7 +1074,7 @@ public class LegacyMasterProcessChannelEncoderTest
{
Stream out = Stream.newStream();
WritableBufferedByteChannel channel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( channel );
+ EventChannelEncoder encoder = new EventChannelEncoder( channel );
encoder.stdOut( "msg", false );
channel.close();
@@ -1218,7 +1091,7 @@ public class LegacyMasterProcessChannelEncoderTest
{
Stream out = Stream.newStream();
WritableBufferedByteChannel channel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( channel );
+ EventChannelEncoder encoder = new EventChannelEncoder( channel );
encoder.stdOut( "msg", true );
channel.close();
@@ -1235,7 +1108,7 @@ public class LegacyMasterProcessChannelEncoderTest
{
Stream out = Stream.newStream();
WritableBufferedByteChannel channel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( channel );
+ EventChannelEncoder encoder = new EventChannelEncoder( channel );
encoder.stdErr( "msg", false );
channel.close();
@@ -1252,7 +1125,7 @@ public class LegacyMasterProcessChannelEncoderTest
{
Stream out = Stream.newStream();
WritableBufferedByteChannel channel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( channel );
+ EventChannelEncoder encoder = new EventChannelEncoder( channel );
encoder.stdErr( "msg", true );
channel.close();
@@ -1270,10 +1143,10 @@ public class LegacyMasterProcessChannelEncoderTest
{
Stream stream = Stream.newStream();
WritableBufferedByteChannel channel = newBufferedChannel( stream );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( channel );
+ EventChannelEncoder encoder = new EventChannelEncoder( channel );
Map<String, String> sysProps = ObjectUtils.systemProps();
- encoder.sendSystemProperties( sysProps );
+ encoder.systemProperties( sysProps );
channel.close();
for ( Entry<String, String> entry : sysProps.entrySet() )
@@ -1304,7 +1177,7 @@ public class LegacyMasterProcessChannelEncoderTest
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
StackTraceWriter stackTraceWriter = mock( StackTraceWriter.class );
when( stackTraceWriter.getThrowable() ).thenReturn( new SafeThrowable( "1" ) );
when( stackTraceWriter.smartTrimmedStackTrace() ).thenReturn( "2" );
@@ -1321,7 +1194,7 @@ public class LegacyMasterProcessChannelEncoderTest
{
Stream out = Stream.newStream();
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
+ EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
StackTraceWriter stackTraceWriter = mock( StackTraceWriter.class );
when( stackTraceWriter.getThrowable() ).thenReturn( new SafeThrowable( "1" ) );
when( stackTraceWriter.smartTrimmedStackTrace() ).thenReturn( "2" );
@@ -1338,7 +1211,7 @@ public class LegacyMasterProcessChannelEncoderTest
{
Stream out = Stream.newStream();
WritableBufferedByteChannel channel = newBufferedChannel( out );
- LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( channel );
+ EventChannelEncoder encoder = new EventChannelEncoder( channel );
Thread.currentThread().interrupt();
try
@@ -1373,11 +1246,6 @@ public class LegacyMasterProcessChannelEncoderTest
return out.toByteArray();
}
- LineNumberReader newReader( Charset streamCharset )
- {
- return new LineNumberReader( new StringReader( new String( toByteArray(), streamCharset ) ) );
- }
-
static Stream newStream()
{
return new Stream( new ByteArrayOutputStream() );
@@ -1412,7 +1280,7 @@ public class LegacyMasterProcessChannelEncoderTest
ByteBuffer src;
@Override
... 376 lines suppressed ...