This is an automated email from the ASF dual-hosted git repository.
tibordigana pushed a commit to branch base64
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
The following commit(s) were added to refs/heads/base64 by this push:
new a7fede2 the main work in this class is done
a7fede2 is described below
commit a7fede24ed7171b1d8c241b0cce97a9a8d12a36f
Author: tibordigana <[email protected]>
AuthorDate: Fri Sep 11 21:03:14 2020 +0200
the main work in this class is done
---
.../surefire/extensions/EventConsumerThread.java | 856 +++++++++++----------
1 file changed, 465 insertions(+), 391 deletions(-)
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 b74cb5e..61c0e8c 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
@@ -52,7 +52,9 @@ import org.apache.maven.surefire.extensions.EventHandler;
import org.apache.maven.surefire.extensions.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
+import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
+import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -60,6 +62,7 @@ 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;
@@ -68,6 +71,7 @@ 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.EOF;
@@ -76,7 +80,6 @@ import static
org.apache.maven.plugin.surefire.extensions.EventConsumerThread.St
import static org.apache.maven.surefire.api.booter.Constants.MAGIC_NUMBER;
import static org.apache.maven.surefire.api.booter.Constants.STREAM_ENCODING;
import static
org.apache.maven.surefire.api.report.CategorizedReportEntry.reportEntry;
-import static org.apache.maven.surefire.api.report.RunMode.MODES;
/**
*
@@ -91,11 +94,65 @@ public class EventConsumerThread extends
CloseableDaemonThread
"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[] MAGIC_NUMBER_BYTES = MAGIC_NUMBER.getBytes(
US_ASCII );
+ private static final byte[] STREAM_ENCODING_BYTES =
STREAM_ENCODING.name().getBytes( US_ASCII );
private static final int DELIMINATOR_LENGTH = 1;
private static final int BYTE_LENGTH = 1;
private static final int INT_LENGTH = 4;
+ public static final int NO_POSITION = -1;
private final ReadableByteChannel channel;
private final EventHandler<Event> eventHandler;
@@ -145,197 +202,228 @@ public class EventConsumerThread extends
CloseableDaemonThread
@SuppressWarnings( "checkstyle:innerassignment" )
private void decode() throws IOException
{
- Map<Segment, ForkedProcessEventType> events = mapEventTypes();
+ Map<Segment, ForkedProcessEventType> eventTypes = mapEventTypes();
+ Map<Segment, RunMode> runModes = mapRunModes();
Memento memento = new Memento();
- BufferedStream line = new BufferedStream( 32 );
memento.bb.limit( 0 );
- boolean streamContinues = true;
- start:
do
{
- if ( !streamContinues )
+ try
{
- printExistingLine( line );
- return;
- }
+ memento.eventType = readEventType( eventTypes, memento );
- line.reset();
- memento.segment.reset();
-
- int readCount =
- DELIMINATOR_LENGTH + MAGIC_NUMBER_BYTES.length +
DELIMINATOR_LENGTH + BYTE_LENGTH + DELIMINATOR_LENGTH;
- streamContinues = read( memento.bb, readCount ) != EOF;
- if ( memento.bb.remaining() < readCount )
- {
- //todo throw exception - broken stream
+ for ( SegmentType segmentType : nextSegmentType(
memento.eventType ) )
+ {
+ if ( segmentType == null )
+ {
+ break;
+ }
+
+ switch ( segmentType )
+ {
+ case RUN_MODE:
+ memento.runMode = runModes.get( readSegment(
memento ) );
+ break;
+ case STRING_ENCODING:
+ memento.charset = readCharset( memento );
+ break;
+ case DATA_STRING:
+ readString( memento );
+ break;
+ case DATA_INT:
+ memento.data.add( readInt( memento ) );
+ break;
+ case END_OF_FRAME:
+ memento.line.positionByteBuffer =
memento.bb.position();
+ eventHandler.handleEvent( toEvent(
memento.eventType, memento.runMode, memento.data ) );
+ break;
+ default:
+ memento.line.positionByteBuffer = NO_POSITION;
+ arguments.dumpStreamText( "Unknown enum ("
+ + ForkedProcessEventType.class.getSimpleName()
+ + ") "
+ + segmentType );
+ }
+ }
+ memento.reset();
}
- checkHeader( memento.bb, memento.segment );
-
- memento.eventType = events.get( readSegment( memento.bb ) );
-
- for ( SegmentType segmentType : nextSegmentType( memento ) )
+ catch ( MalformedFrameException e )
{
- if ( segmentType == null )
+ if ( e.hasValidPositions() )
{
- break;
+ int length = e.readTo - e.readFrom;
+ memento.line.write( memento.bb, e.readFrom, length );
}
+ }
+ }
+ while ( true );
+ }
- switch ( segmentType )
- {
- case RUN_MODE:
- memento.runMode = MODES.get( readSegment( memento ) );
- break;
- case STRING_ENCODING:
- //todo handle exceptions
- memento.charset = Charset.forName( readSegment(
memento ) );
- break;
- case BYTES_INT_COUNTER:
- memento.bytesCounter = readInt( memento.bb );
- break;
- case DATA_STRING:
- memento.cb.clear();
- int bytesCounter = memento.bytesCounter;
- if ( bytesCounter == 0 )
- {
- memento.data.add( "" );
- }
- else if ( bytesCounter == 1 )
- {
- // handle the returned boolean
- read( memento.bb, 1 );
- byte oneChar = memento.bb.get();
- memento.data.add( oneChar == 0 ? null :
String.valueOf( (char) oneChar ) );
- }
- else
- {
- memento.data.add( readString( memento ) );
- }
- memento.bytesCounter = 0;
- break;
- case DATA_INT:
- memento.data.add( readInt( memento.bb ) );
- break;
- case END_OF_FRAME:
- eventHandler.handleEvent( toEvent( memento ) );
- continue start;
- default:
- throw new IllegalArgumentException( "Unknown enum " +
segmentType );
- }
+ protected ForkedProcessEventType readEventType( Map<Segment,
ForkedProcessEventType> eventTypes, Memento memento )
+ throws IOException, MalformedFrameException
+ {
+ int readCount = DELIMINATOR_LENGTH + MAGIC_NUMBER_BYTES.length +
DELIMINATOR_LENGTH
+ + BYTE_LENGTH + DELIMINATOR_LENGTH;
+ if ( read( memento, readCount ) == EOF )
+ {
+ throw new EOFException();
+ }
+ checkHeader( memento );
- memento.cb.clear();
+ return eventTypes.get( readSegment( memento ) );
+ }
- read( memento.bb, 1 );
- b = 0xff & memento.bb.get();
- if ( Character.isDefined( b ) && b != ':' )
- {
- //memento.segmentCompletion = SegmentCompletion.;
- //MalformedStreamException
- continue start;
- }
- }
+ protected void readString( Memento memento ) throws IOException,
MalformedFrameException
+ {
+ memento.cb.clear();
+ if ( read( memento, INT_LENGTH + DELIMINATOR_LENGTH ) == EOF )
+ {
+ throw new EOFException();
+ }
- memento.bb.flip();
- line.write( memento.bb );
- memento.reset();
+ int readCount = readInt( memento );
+ if ( read( memento, readCount + DELIMINATOR_LENGTH ) == EOF )
+ {
+ throw new EOFException();
}
- while ( true );
+
+ if ( readCount == 0 )
+ {
+ memento.data.add( "" );
+ }
+ else if ( readCount == 1 )
+ {
+ if ( read( memento, 1 ) == EOF )
+ {
+ throw new EOFException();
+ }
+ byte oneChar = memento.bb.get();
+ memento.data.add( oneChar == 0 ? null : String.valueOf( (char)
oneChar ) );
+ }
+ else
+ {
+ memento.data.add( readString( memento, readCount ) );
+ }
+
+ checkDelimiter( memento );
}
@Nonnull
- private static Segment readSegment( ByteBuffer bb )
+ protected Segment readSegment( Memento memento ) throws IOException,
MalformedFrameException
{
- int readCount = bb.get() & 0xff;
+ if ( read( memento, BYTE_LENGTH + DELIMINATOR_LENGTH ) == EOF )
+ {
+ throw new EOFException();
+ }
+ ByteBuffer bb = memento.bb;
+ int readCount = readByte( memento ) & 0xff;
+ if ( read( memento, readCount + DELIMINATOR_LENGTH ) == EOF )
+ {
+ throw new EOFException();
+ }
Segment segment = new Segment( bb.array(), bb.arrayOffset() +
bb.position(), readCount );
bb.position( bb.position() + readCount );
- checkDelimiter( bb );
+ checkDelimiter( memento );
return segment;
}
- private static void checkHeader( ByteBuffer bb, BufferedStream segment )
+ @Nonnull
+ protected Charset readCharset( Memento memento ) throws IOException,
MalformedFrameException
{
- checkDelimiter( bb );
+ if ( read( memento, BYTE_LENGTH + DELIMINATOR_LENGTH ) == EOF )
+ {
+ throw new EOFException();
+ }
+ ByteBuffer bb = memento.bb;
+ int length = readByte( memento ) & 0xff;
+ if ( read( memento, length + DELIMINATOR_LENGTH ) == EOF )
+ {
+ throw new EOFException();
+ }
+ byte[] array = bb.array();
+ int offset = bb.arrayOffset() + bb.position();
+ bb.position( bb.position() + length );
+ boolean isDefaultEncoding = false;
+ if ( length == STREAM_ENCODING_BYTES.length )
+ {
+ isDefaultEncoding = true;
+ for ( int i = 0; i < length; i++ )
+ {
+ isDefaultEncoding &= STREAM_ENCODING_BYTES[i] == array[offset
+ i];
+ }
+ }
- segment.reset();
+ try
+ {
+ Charset charset =
+ isDefaultEncoding ? 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;
- for ( int start = bb.arrayOffset() + bb.position(), length =
MAGIC_NUMBER_BYTES.length;
- shift < length; shift++ )
+ try
{
- if ( bb.array()[shift + start] != MAGIC_NUMBER_BYTES[shift] )
+ for ( int start = bb.arrayOffset() + bb.position(), length =
MAGIC_NUMBER_BYTES.length;
+ shift < length; shift++ )
{
- //todo throw exception - broken stream
+ if ( bb.array()[shift + start] != MAGIC_NUMBER_BYTES[shift] )
+ {
+ throw new MalformedFrameException(
memento.line.positionByteBuffer, bb.position() + shift );
+ }
}
}
- bb.position( bb.position() + shift );
+ finally
+ {
+ bb.position( bb.position() + shift );
+ }
- checkDelimiter( bb );
+ checkDelimiter( memento );
}
- private static void checkDelimiter( ByteBuffer bb )
+ private static void checkDelimiter( Memento memento ) throws
MalformedFrameException
{
+ ByteBuffer bb = memento.bb;
if ( ( 0xff & bb.get() ) != ':' )
{
- //todo throw exception - broken stream
+ throw new MalformedFrameException(
memento.line.positionByteBuffer, bb.position() );
}
}
- private static SegmentType[] nextSegmentType( Memento memento )
+ protected SegmentType[] nextSegmentType( ForkedProcessEventType eventType )
{
- switch ( memento.eventType )
+ switch ( eventType )
{
case BOOTERCODE_BYE:
case BOOTERCODE_STOP_ON_NEXT_TEST:
case BOOTERCODE_NEXT_TEST:
- return new SegmentType[] {SegmentType.END_OF_FRAME};
+ return EVENT_WITHOUT_DATA;
case BOOTERCODE_CONSOLE_ERROR:
case BOOTERCODE_JVM_EXIT_ERROR:
- return new SegmentType[] {
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.STRING_ENCODING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.END_OF_FRAME
- };
+ return EVENT_WITH_ERROR_TRACE;
case BOOTERCODE_CONSOLE_INFO:
case BOOTERCODE_CONSOLE_DEBUG:
case BOOTERCODE_CONSOLE_WARNING:
- return new SegmentType[] {
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.STRING_ENCODING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.END_OF_FRAME
- };
+ return EVENT_WITH_ONE_STRING;
case BOOTERCODE_STDOUT:
case BOOTERCODE_STDOUT_NEW_LINE:
case BOOTERCODE_STDERR:
case BOOTERCODE_STDERR_NEW_LINE:
- return new SegmentType[] {
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.RUN_MODE,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.STRING_ENCODING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.END_OF_FRAME
- };
+ return EVENT_WITH_RUNMODE_AND_ONE_STRING;
case BOOTERCODE_SYSPROPS:
- return new SegmentType[] {
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.RUN_MODE,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.STRING_ENCODING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.END_OF_FRAME
- };
+ return EVENT_WITH_RUNMODE_AND_TWO_STRINGS;
case BOOTERCODE_TESTSET_STARTING:
case BOOTERCODE_TESTSET_COMPLETED:
case BOOTERCODE_TEST_STARTING:
@@ -344,39 +432,15 @@ public class EventConsumerThread extends
CloseableDaemonThread
case BOOTERCODE_TEST_SKIPPED:
case BOOTERCODE_TEST_ERROR:
case BOOTERCODE_TEST_ASSUMPTIONFAILURE:
- return new SegmentType[] {
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.RUN_MODE,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.STRING_ENCODING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.DATA_INT,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.BYTES_INT_COUNTER,
- SegmentType.DATA_STRING,
- SegmentType.END_OF_FRAME
- };
+ return EVENT_TEST_CONTROL;
default:
- throw new IllegalArgumentException( "Unknown enum " +
memento.eventType );
+ throw new IllegalArgumentException( "Unknown enum " +
eventType );
}
}
- private StreamReadStatus read( ByteBuffer buffer, int recommendedCount )
throws IOException
+ protected StreamReadStatus read( Memento memento, int recommendedCount )
throws IOException
{
+ ByteBuffer buffer = memento.bb;
if ( buffer.remaining() >= recommendedCount && buffer.position() != 0 )
{
return OVERFLOW;
@@ -386,6 +450,7 @@ public class EventConsumerThread extends
CloseableDaemonThread
if ( buffer.position() != 0 && recommendedCount >
buffer.capacity() - buffer.position() )
{
buffer.compact().flip();
+ memento.line.positionByteBuffer = 0;
}
int mark = buffer.position();
buffer.position( buffer.limit() );
@@ -397,161 +462,92 @@ public class EventConsumerThread extends
CloseableDaemonThread
}
buffer.limit( buffer.position() );
buffer.position( mark );
- return isEnd ? EOF : ( buffer.remaining() >= recommendedCount ?
OVERFLOW : UNDERFLOW );
+ int readBytes = buffer.remaining();
+ return readBytes == 0 && isEnd ? EOF : ( readBytes >=
recommendedCount ? OVERFLOW : UNDERFLOW );
}
}
- private void printExistingLine( BufferedStream line )
+ protected Event toEvent( ForkedProcessEventType eventType, RunMode
runMode, List<Object> args )
{
- if ( line.isEmpty() )
- {
- return;
- }
- ConsoleLogger logger = arguments.getConsoleLogger();
- String s = line.toString( STREAM_ENCODING ).trim();
- 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
+ switch ( eventType )
{
- if ( isJvmError( s ) )
- {
- logger.error( 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() );
-
- if ( logger.isDebugEnabled() )
- {
- logger.debug( s );
- }
+ 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 Event toEvent( Memento memento )
+ @Nonnull
+ private static TestSetReportEntry toReportEntry( List<Object> args )
{
- ForkedProcessEventType event = memento.eventType;
- if ( event.isControlCategory() )
- {
- switch ( event )
- {
- case BOOTERCODE_BYE:
- return new ControlByeEvent();
- case BOOTERCODE_STOP_ON_NEXT_TEST:
- return new ControlStopOnNextTestEvent();
- case BOOTERCODE_NEXT_TEST:
- return new ControlNextTestEvent();
- default:
- throw new IllegalStateException( "Unknown enum " + event );
- }
- }
- else if ( event.isConsoleErrorCategory() || event.isJvmExitError() )
- {
- String traceMessage = (String) memento.data.get( 0 );
- String smartTrimmedStackTrace = (String) memento.data.get( 1 );
- String stackTrace = (String) memento.data.get( 2 );
- StackTraceWriter stackTraceWriter = newTrace( traceMessage,
smartTrimmedStackTrace, stackTrace );
- return event.isConsoleErrorCategory()
- ? new ConsoleErrorEvent( stackTraceWriter )
- : new JvmExitErrorEvent( stackTraceWriter );
- }
- else if ( event.isConsoleCategory() )
- {
- String msg = (String) memento.data.get( 0 );
- switch ( event )
- {
- case BOOTERCODE_CONSOLE_INFO:
- return new ConsoleInfoEvent( msg );
- case BOOTERCODE_CONSOLE_DEBUG:
- return new ConsoleDebugEvent( msg );
- case BOOTERCODE_CONSOLE_WARNING:
- return new ConsoleWarningEvent( msg );
- default:
- throw new IllegalStateException( "Unknown enum " + event );
- }
- }
- else if ( event.isStandardStreamCategory() )
- {
- String output = (String) memento.data.get( 0 );
- switch ( event )
- {
- case BOOTERCODE_STDOUT:
- return new StandardStreamOutEvent( memento.runMode, output
);
- case BOOTERCODE_STDOUT_NEW_LINE:
- return new StandardStreamOutWithNewLineEvent(
memento.runMode, output );
- case BOOTERCODE_STDERR:
- return new StandardStreamErrEvent( memento.runMode, output
);
- case BOOTERCODE_STDERR_NEW_LINE:
- return new StandardStreamErrWithNewLineEvent(
memento.runMode, output );
- default:
- throw new IllegalStateException( "Unknown enum " + event );
- }
- }
- else if ( event.isSysPropCategory() )
- {
- String key = (String) memento.data.get( 0 );
- String value = (String) memento.data.get( 1 );
- return new SystemPropertyEvent( memento.runMode, key, value );
- }
- else if ( event.isTestCategory() )
- {
- // ReportEntry:
- String source = (String) memento.data.get( 0 );
- String sourceText = (String) memento.data.get( 1 );
- String name = (String) memento.data.get( 2 );
- String nameText = (String) memento.data.get( 3 );
- String group = (String) memento.data.get( 4 );
- String message = (String) memento.data.get( 5 );
- Integer timeElapsed = (Integer) memento.data.get( 6 );
- // StackTraceWriter:
- String traceMessage = (String) memento.data.get( 7 );
- String smartTrimmedStackTrace = (String) memento.data.get( 8 );
- String stackTrace = (String) memento.data.get( 9 );
- TestSetReportEntry reportEntry = newReportEntry( source,
sourceText, name, nameText, group, message,
- timeElapsed, traceMessage, smartTrimmedStackTrace, stackTrace
);
-
- switch ( event )
- {
- case BOOTERCODE_TESTSET_STARTING:
- return new TestsetStartingEvent( memento.runMode,
reportEntry );
- case BOOTERCODE_TESTSET_COMPLETED:
- return new TestsetCompletedEvent( memento.runMode,
reportEntry );
- case BOOTERCODE_TEST_STARTING:
- return new TestStartingEvent( memento.runMode, reportEntry
);
- case BOOTERCODE_TEST_SUCCEEDED:
- return new TestSucceededEvent( memento.runMode,
reportEntry );
- case BOOTERCODE_TEST_FAILED:
- return new TestFailedEvent( memento.runMode, reportEntry );
- case BOOTERCODE_TEST_SKIPPED:
- return new TestSkippedEvent( memento.runMode, reportEntry
);
- case BOOTERCODE_TEST_ERROR:
- return new TestErrorEvent( memento.runMode, reportEntry );
- case BOOTERCODE_TEST_ASSUMPTIONFAILURE:
- return new TestAssumptionFailureEvent( memento.runMode,
reportEntry );
- default:
- throw new IllegalStateException( "Unknown enum " + event );
- }
- }
+ // 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 );
+ }
- throw new IllegalStateException( "Missing a branch for the event type
" + event );
+ 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 newTrace( String traceMessage, String
smartTrimmedStackTrace, String 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;
@@ -566,7 +562,7 @@ public class EventConsumerThread extends
CloseableDaemonThread
String smartTrimmedStackTrace,
String stackTrace )
throws NumberFormatException
{
- StackTraceWriter stackTraceWriter = newTrace( traceMessage,
smartTrimmedStackTrace, stackTrace );
+ StackTraceWriter stackTraceWriter = toTrace( traceMessage,
smartTrimmedStackTrace, stackTrace );
return reportEntry( source, sourceText, name, nameText, group,
stackTraceWriter, timeElapsed, message,
Collections.<String, String>emptyMap() );
}
@@ -584,80 +580,84 @@ public class EventConsumerThread extends
CloseableDaemonThread
return false;
}
- private String readSegment( Memento memento ) throws IOException
+ private static int decode( @Nonnull CharsetDecoder decoder, @Nonnull
ByteBuffer input, @Nonnull CharBuffer output,
+ @Nonnegative int bytesToDecode, boolean
endOfInput, @Nonnegative int errorStreamFrom )
+ throws MalformedFrameException
{
- int startsWithPosition = memento.bb.position();
- boolean streamContinues;
+ int limit = input.limit();
+ input.limit( input.position() + bytesToDecode );
- do
+ CoderResult result = decoder.decode( input, output, endOfInput );
+ if ( result.isError() || result.isMalformed() )
{
- streamContinues = read( memento.bb, 1 ) != EOF;
+ throw new MalformedFrameException( errorStreamFrom,
input.position() );
}
- while ( ( 0xff & memento.bb.get() ) != ':' );
-
- int endsWithLimit = memento.bb.limit();
- memento.bb.limit( memento.bb.position() - 1 );
- memento.bb.position( startsWithPosition );
-
- memento.cb.clear();
-
- memento.decoder
- .reset()
- .decode( memento.bb, memento.cb, true );
-
- memento.bb.position( memento.bb.position() + 1 );
- memento.bb.limit( endsWithLimit );
-
- String segment = memento.cb.flip().toString();
- memento.cb.clear();
- return segment;
+
+ int decodedBytes = bytesToDecode - input.remaining();
+ input.limit( limit );
+ return decodedBytes;
}
- String readString( Memento memento ) throws IOException
+ String readString( @Nonnull final Memento memento, @Nonnegative final int
totalBytes )
+ throws IOException, MalformedFrameException
{
- final CharBuffer chars = memento.cb;
- final ByteBuffer buffer = memento.bb;
- final int totalBytes = memento.bytesCounter;
-
+ memento.decoder.reset();
+ final CharBuffer output = memento.cb;
+ output.clear();
+ final ByteBuffer input = memento.bb;
+ final List<String> strings = new ArrayList<>();
int countDecodedBytes = 0;
- int countReadBytes = 0;
+ for ( boolean endOfInput = false; !endOfInput; )
+ {
+ final int bytesToRead = totalBytes - countDecodedBytes;
- buffer.clear();
+ if ( read( memento, bytesToRead - input.remaining() ) == EOF )
+ {
+ throw new EOFException();
+ }
- int positionChars = chars.position();
- int startPosition;
- List<String> strings = new ArrayList<>();
- do
- {
- startPosition = buffer.position();
- buffer.limit( startPosition );
- read( buffer, totalBytes - countReadBytes );
- memento.decoder
- .reset()
- .decode( buffer, chars, countDecodedBytes + buffer.remaining()
>= totalBytes );
- final boolean hasDecodedNewBytes = chars.position() !=
positionChars;
- if ( hasDecodedNewBytes )
+ int bytesToDecode = min( input.remaining(), bytesToRead );
+ final boolean isLastChunk = bytesToDecode == bytesToRead;
+
+ endOfInput = countDecodedBytes + bytesToDecode >= totalBytes;
+ do
{
- countDecodedBytes += buffer.position() - startPosition;
- positionChars = chars.position();
+ boolean endOfChunk = output.remaining() >= bytesToRead;
+ boolean endOfOutput = isLastChunk && endOfChunk;
+ int readInputBytes = decode( memento.decoder, input, output,
bytesToDecode, endOfOutput,
+ memento.line.positionByteBuffer );
+ bytesToDecode -= readInputBytes;
+ countDecodedBytes += readInputBytes;
}
- countReadBytes += buffer.limit() - startPosition;
- if ( buffer.hasRemaining() )
+ while ( isLastChunk && bytesToDecode > 0 && output.hasRemaining()
);
+
+ if ( isLastChunk || !output.hasRemaining() )
{
- buffer.compact();
+ strings.add( output.flip().toString() );
+ output.clear();
}
- strings.add( chars.flip().toString() );
- chars.clear();
}
- while ( countReadBytes < totalBytes );
+
+ memento.decoder.reset();
+ output.clear();
return toString( strings );
}
- int readInt( ByteBuffer bb ) throws IOException
+ protected byte readByte( Memento memento ) throws IOException,
MalformedFrameException
+ {
+ read( memento, BYTE_LENGTH );
+ byte b = memento.bb.get();
+ checkDelimiter( memento );
+ return b;
+ }
+
+ protected int readInt( Memento memento ) throws IOException,
MalformedFrameException
{
- read( bb, 4 );
- return bb.getInt();
+ read( memento, INT_LENGTH );
+ int i = memento.bb.getInt();
+ checkDelimiter( memento );
+ return i;
}
private static String toString( List<String> strings )
@@ -666,7 +666,7 @@ public class EventConsumerThread extends
CloseableDaemonThread
{
return strings.get( 0 );
}
- StringBuilder concatenated = new StringBuilder();
+ StringBuilder concatenated = new StringBuilder( strings.size() *
BUFFER_SIZE );
for ( String s : strings )
{
concatenated.append( s );
@@ -679,7 +679,18 @@ public class EventConsumerThread extends
CloseableDaemonThread
Map<Segment, ForkedProcessEventType> map = new HashMap<>();
for (ForkedProcessEventType e : ForkedProcessEventType.values() )
{
- byte[] array = e.getOpcode().getBytes();
+ byte[] array = e.getOpcode().getBytes( US_ASCII );
+ map.put( new Segment( array, 0, array.length ), e );
+ }
+ return map;
+ }
+
+ private static Map<Segment, RunMode> mapRunModes()
+ {
+ Map<Segment, RunMode> map = new HashMap<>();
+ for ( RunMode e : RunMode.values() )
+ {
+ byte[] array = e.geRunName().getBytes( US_ASCII );
map.put( new Segment( array, 0, array.length ), e );
}
return map;
@@ -706,7 +717,6 @@ public class EventConsumerThread extends
CloseableDaemonThread
{
RUN_MODE,
STRING_ENCODING,
- BYTES_INT_COUNTER,
DATA_STRING,
DATA_INT,
END_OF_FRAME
@@ -715,33 +725,41 @@ public class EventConsumerThread extends
CloseableDaemonThread
/**
* This class avoids locking which gains the performance of this decoder.
*/
- private static class BufferedStream
+ private class BufferedStream
{
private byte[] buffer;
private int count;
+ private int positionByteBuffer;
+ private boolean isNewLine;
public BufferedStream( int capacity )
{
this.buffer = new byte[capacity];
}
- void write( int b )
- {
- ensureCapacity( 1 );
- buffer[count++] = (byte) b;
- }
-
- void write( ByteBuffer bb )
+ void write( ByteBuffer bb, int position, int length )
{
- int size = bb.remaining();
- if ( size > 0 )
+ ensureCapacity( length );
+ byte[] array = bb.array();
+ int pos = bb.arrayOffset() + position;
+ while ( length-- > 0 )
{
- ensureCapacity( size );
- byte[] b = bb.array();
- int pos = bb.arrayOffset() + bb.position();
- System.arraycopy( b, pos, buffer, count, size );
- count+= size;
- bb.position( bb.position() + size );
+ positionByteBuffer++;
+ byte b = array[pos++];
+ if ( b == '\r' || b == '\n' )
+ {
+ if ( !isNewLine )
+ {
+ printExistingLine();
+ count = 0;
+ }
+ isNewLine = true;
+ }
+ else
+ {
+ buffer[count++] = b;
+ isNewLine = false;
+ }
}
}
@@ -750,11 +768,6 @@ public class EventConsumerThread extends
CloseableDaemonThread
return count != 0;
}
- void reset()
- {
- count = 0;
- }
-
String toString( Charset charset )
{
return new String( buffer, 0, count, charset );
@@ -775,20 +788,62 @@ public class EventConsumerThread extends
CloseableDaemonThread
buffer = Arrays.copyOf( buffer, max( newCapacity,
exactCapacity ) );
}
}
+
+ private void printExistingLine()
+ {
+ if ( isEmpty() )
+ {
+ return;
+ }
+ ConsoleLogger logger = arguments.getConsoleLogger();
+ String s = toString( STREAM_ENCODING );
+ 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 );
+ }
+ 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() );
+
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( s );
+ }
+ }
+ }
}
- static class Memento
+ class Memento
{
final CharsetDecoder decoder;
- final BufferedStream segment = new BufferedStream(
MAGIC_NUMBER.length() );
+ final BufferedStream line = new BufferedStream( 32 );
final List<Object> data = new ArrayList<>();
- final CharBuffer cb = CharBuffer.allocate( 1024 );
- final ByteBuffer bb = ByteBuffer.allocate( 1024 );
+ final CharBuffer cb = CharBuffer.allocate( BUFFER_SIZE );
+ final ByteBuffer bb = ByteBuffer.allocate( BUFFER_SIZE );
FrameCompletion frameCompletion;
ForkedProcessEventType eventType;
RunMode runMode;
Charset charset;
- int bytesCounter;
Memento()
{
@@ -799,8 +854,10 @@ public class EventConsumerThread extends
CloseableDaemonThread
void reset()
{
- segment.reset();
frameCompletion = null;
+ eventType = null;
+ runMode = null;
+ charset = null;
}
}
@@ -857,4 +914,21 @@ public class EventConsumerThread extends
CloseableDaemonThread
return true;
}
}
+
+ public static class MalformedFrameException extends Exception
+ {
+ 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;
+ }
+ }
}