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 2022/03/04 02:51:43 UTC

[maven-surefire] 02/02: [SUREFIRE-2014] Implement testRunId and RunMode in the EventEncoder and EventDecoder

This is an automated email from the ASF dual-hosted git repository.

tibordigana pushed a commit to branch SUREFIRE-2015
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git

commit 7422201928edfa9f42fa371af58c14f46903d9f5
Author: tibordigana <ti...@apache.org>
AuthorDate: Fri Mar 4 03:50:35 2022 +0100

    [SUREFIRE-2014] Implement testRunId and RunMode in the EventEncoder and EventDecoder
---
 .../surefire/booterclient/output/ForkClient.java   |  16 +-
 .../output/ForkedProcessEventNotifier.java         |   3 +-
 .../output/ForkedProcessReportEventListener.java   |   3 +-
 .../maven/surefire/stream/CommandEncoder.java      |  27 ++-
 .../apache/maven/surefire/stream/EventDecoder.java | 123 +++++++------
 .../booterclient/ForkingRunListenerTest.java       |  20 ++-
 .../booterclient/TestSetMockReporterFactory.java   |   3 +-
 .../booterclient/output/ForkClientTest.java        |  56 ++++--
 .../output/ThreadedStreamConsumerTest.java         |   2 +-
 .../maven/plugin/surefire/extensions/E2ETest.java  |   4 +-
 .../extensions/EventConsumerThreadTest.java        |   1 +
 .../extensions/ForkedProcessEventNotifierTest.java |  18 +-
 .../surefire/extensions/StreamFeederTest.java      |   2 -
 .../maven/surefire/stream/EventDecoderTest.java    | 196 ++++++++++++---------
 .../api/booter/ForkedProcessEventType.java         |  34 ++--
 .../api/event/AbstractStandardStreamEvent.java     |  10 +-
 .../api/event/AbstractTestControlEvent.java        |  10 +-
 .../surefire/api/event/StandardStreamErrEvent.java |   4 +-
 .../event/StandardStreamErrWithNewLineEvent.java   |   4 +-
 .../surefire/api/event/StandardStreamOutEvent.java |   4 +-
 .../event/StandardStreamOutWithNewLineEvent.java   |   4 +-
 .../surefire/api/event/SystemPropertyEvent.java    |   9 +-
 .../api/event/TestAssumptionFailureEvent.java      |   5 +-
 .../maven/surefire/api/event/TestErrorEvent.java   |   5 +-
 .../maven/surefire/api/event/TestFailedEvent.java  |   5 +-
 .../maven/surefire/api/event/TestSkippedEvent.java |   5 +-
 .../surefire/api/event/TestStartingEvent.java      |   5 +-
 .../surefire/api/event/TestSucceededEvent.java     |   5 +-
 .../surefire/api/event/TestsetCompletedEvent.java  |   5 +-
 .../surefire/api/event/TestsetStartingEvent.java   |   5 +-
 .../surefire/api/stream/AbstractStreamDecoder.java |  45 +++--
 .../surefire/api/stream/AbstractStreamEncoder.java |  54 ++++--
 .../maven/surefire/api/stream/SegmentType.java     |   1 +
 .../api/stream/AbstractStreamDecoderTest.java      |   5 +-
 .../api/stream/AbstractStreamEncoderTest.java      | 114 ++++++------
 .../surefire/booter/spi/EventChannelEncoder.java   | 112 +++++-------
 .../surefire/booter/stream/CommandDecoder.java     |  20 +--
 .../maven/surefire/booter/CommandReaderTest.java   |   2 -
 .../booter/spi/CommandChannelDecoderTest.java      |   3 +-
 .../booter/spi/EventChannelEncoderTest.java        | 116 ++++++++++--
 .../resources/binary-commands/75171711-encoder.bin | Bin 851 -> 838 bytes
 41 files changed, 604 insertions(+), 461 deletions(-)

diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
index fc622ef..f89209b 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
@@ -125,7 +125,7 @@ public final class ForkClient
             implements ForkedProcessReportEventListener<TestSetReportEntry>
     {
         @Override
-        public void handle( RunMode runMode, TestSetReportEntry reportEntry )
+        public void handle( TestSetReportEntry reportEntry )
         {
             getTestSetReporter().testSetStarting( reportEntry );
             setCurrentStartTime();
@@ -136,7 +136,7 @@ public final class ForkClient
             implements ForkedProcessReportEventListener<TestSetReportEntry>
     {
         @Override
-        public void handle( RunMode runMode, TestSetReportEntry reportEntry )
+        public void handle( TestSetReportEntry reportEntry )
         {
             testsInProgress.clear();
             TestSetReportEntry entry = reportEntry( reportEntry.getRunMode(), reportEntry.getTestRunId(),
@@ -151,7 +151,7 @@ public final class ForkClient
     private final class TestStartingListener implements ForkedProcessReportEventListener<ReportEntry>
     {
         @Override
-        public void handle( RunMode runMode, ReportEntry reportEntry )
+        public void handle( ReportEntry reportEntry )
         {
             testsInProgress.offer( reportEntry.getSourceName() );
             getTestSetReporter().testStarting( reportEntry );
@@ -161,7 +161,7 @@ public final class ForkClient
     private final class TestSucceededListener implements ForkedProcessReportEventListener<ReportEntry>
     {
         @Override
-        public void handle( RunMode runMode, ReportEntry reportEntry )
+        public void handle( ReportEntry reportEntry )
         {
             testsInProgress.remove( reportEntry.getSourceName() );
             getTestSetReporter().testSucceeded( reportEntry );
@@ -171,7 +171,7 @@ public final class ForkClient
     private final class TestFailedListener implements ForkedProcessReportEventListener<ReportEntry>
     {
         @Override
-        public void handle( RunMode runMode, ReportEntry reportEntry )
+        public void handle( ReportEntry reportEntry )
         {
             testsInProgress.remove( reportEntry.getSourceName() );
             getTestSetReporter().testFailed( reportEntry );
@@ -181,7 +181,7 @@ public final class ForkClient
     private final class TestSkippedListener implements ForkedProcessReportEventListener<ReportEntry>
     {
         @Override
-        public void handle( RunMode runMode, ReportEntry reportEntry )
+        public void handle( ReportEntry reportEntry )
         {
             testsInProgress.remove( reportEntry.getSourceName() );
             getTestSetReporter().testSkipped( reportEntry );
@@ -191,7 +191,7 @@ public final class ForkClient
     private final class TestErrorListener implements ForkedProcessReportEventListener<ReportEntry>
     {
         @Override
-        public void handle( RunMode runMode, ReportEntry reportEntry )
+        public void handle( ReportEntry reportEntry )
         {
             testsInProgress.remove( reportEntry.getSourceName() );
             getTestSetReporter().testError( reportEntry );
@@ -201,7 +201,7 @@ public final class ForkClient
     private final class TestAssumptionFailureListener implements ForkedProcessReportEventListener<ReportEntry>
     {
         @Override
-        public void handle( RunMode runMode, ReportEntry reportEntry )
+        public void handle( ReportEntry reportEntry )
         {
             testsInProgress.remove( reportEntry.getSourceName() );
             getTestSetReporter().testAssumptionFailure( reportEntry );
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventNotifier.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventNotifier.java
index 1c47afb..2e2a174 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventNotifier.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventNotifier.java
@@ -225,11 +225,10 @@ public final class ForkedProcessEventNotifier
         {
             ForkedProcessReportEventListener listener = reportEventListeners.get( eventType );
             AbstractTestControlEvent testControlEvent = (AbstractTestControlEvent) event;
-            RunMode mode = testControlEvent.getRunMode();
             ReportEntry reportEntry = testControlEvent.getReportEntry();
             if ( listener != null )
             {
-                listener.handle( mode, reportEntry );
+                listener.handle( reportEntry );
             }
         }
         else if ( event.isJvmExitError() )
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessReportEventListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessReportEventListener.java
index 1eec0ba..10f393d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessReportEventListener.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessReportEventListener.java
@@ -20,7 +20,6 @@ package org.apache.maven.plugin.surefire.booterclient.output;
  */
 
 import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 
 /**
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
@@ -29,5 +28,5 @@ import org.apache.maven.surefire.api.report.RunMode;
  */
 public interface ForkedProcessReportEventListener<T extends ReportEntry>
 {
-    void handle( RunMode runMode, T reportEntry );
+    void handle( T reportEntry );
 }
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
index 81f9d77..5da1151 100644
--- 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
@@ -38,7 +38,6 @@ import static org.apache.maven.surefire.api.booter.MasterProcessCommand.RUN_CLAS
 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;
 
 /**
  *
@@ -56,52 +55,50 @@ public class CommandEncoder extends AbstractStreamEncoder<MasterProcessCommand>
     public void sendRunClass( String testClassName ) throws IOException
     {
         CharsetEncoder encoder = newCharsetEncoder();
-        int bufferMaxLength =
-            estimateBufferLength( RUN_CLASS.getOpcodeLength(), NORMAL_RUN, encoder, 0, testClassName );
+        int bufferMaxLength = estimateBufferLength( RUN_CLASS.getOpcodeLength(), null, encoder, 0, 0, testClassName );
         ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
-        encode( encoder, result, RUN_CLASS, NORMAL_RUN, testClassName );
+        encode( encoder, result, RUN_CLASS, testClassName );
         write( result, true );
     }
 
     public void sendTestSetFinished() throws IOException
     {
-        int bufferMaxLength = estimateBufferLength( TEST_SET_FINISHED.getOpcodeLength(), null, null, 0 );
+        int bufferMaxLength = estimateBufferLength( TEST_SET_FINISHED.getOpcodeLength(), null, null, 0, 0 );
         ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
-        encodeHeader( result, TEST_SET_FINISHED, null );
+        encodeHeader( result, TEST_SET_FINISHED );
         write( result, true );
     }
 
     public void sendSkipSinceNextTest() throws IOException
     {
-        int bufferMaxLength = estimateBufferLength( SKIP_SINCE_NEXT_TEST.getOpcodeLength(), null, null, 0 );
+        int bufferMaxLength = estimateBufferLength( SKIP_SINCE_NEXT_TEST.getOpcodeLength(), null, null, 0, 0 );
         ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
-        encodeHeader( result, SKIP_SINCE_NEXT_TEST, null );
+        encodeHeader( result, SKIP_SINCE_NEXT_TEST );
         write( result, true );
     }
 
     public void sendShutdown( String shutdownData ) throws IOException
     {
         CharsetEncoder encoder = newCharsetEncoder();
-        int bufferMaxLength =
-            estimateBufferLength( SHUTDOWN.getOpcodeLength(), null, encoder, 0, shutdownData );
+        int bufferMaxLength = estimateBufferLength( SHUTDOWN.getOpcodeLength(), null, encoder, 0, 0, shutdownData );
         ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
-        encode( encoder, result, SHUTDOWN, null, shutdownData );
+        encode( encoder, result, SHUTDOWN, shutdownData );
         write( result, true );
     }
 
     public void sendNoop() throws IOException
     {
-        int bufferMaxLength = estimateBufferLength( NOOP.getOpcodeLength(), null, null, 0 );
+        int bufferMaxLength = estimateBufferLength( NOOP.getOpcodeLength(), null, null, 0, 0 );
         ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
-        encodeHeader( result, NOOP, null );
+        encodeHeader( result, NOOP );
         write( result, true );
     }
 
     public void sendByeAck() throws IOException
     {
-        int bufferMaxLength = estimateBufferLength( BYE_ACK.getOpcodeLength(), null, null, 0 );
+        int bufferMaxLength = estimateBufferLength( BYE_ACK.getOpcodeLength(), null, null, 0, 0 );
         ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
-        encodeHeader( result, BYE_ACK, null );
+        encodeHeader( result, BYE_ACK );
         write( result, true );
     }
 
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
index aab8689..ea11889 100644
--- 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
@@ -66,12 +66,12 @@ import java.util.concurrent.FutureTask;
 import static java.util.Collections.emptyMap;
 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.report.RunMode.NORMAL_RUN;
 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.apache.maven.surefire.api.stream.SegmentType.TEST_RUN_ID;
 import static org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils.addShutDownHook;
 
 /**
@@ -102,15 +102,17 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
         END_OF_FRAME
     };
 
-    private static final SegmentType[] EVENT_WITH_RUNMODE_AND_ONE_STRING = new SegmentType[] {
+    private static final SegmentType[] EVENT_WITH_RUNMODE_TID_AND_ONE_STRING = new SegmentType[] {
         RUN_MODE,
+        TEST_RUN_ID,
         STRING_ENCODING,
         DATA_STRING,
         END_OF_FRAME
     };
 
-    private static final SegmentType[] EVENT_WITH_RUNMODE_AND_TWO_STRINGS = new SegmentType[] {
+    private static final SegmentType[] EVENT_WITH_RUNMODE_TID_AND_TWO_STRINGS = new SegmentType[] {
         RUN_MODE,
+        TEST_RUN_ID,
         STRING_ENCODING,
         DATA_STRING,
         DATA_STRING,
@@ -119,6 +121,7 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
 
     private static final SegmentType[] EVENT_TEST_CONTROL = new SegmentType[] {
         RUN_MODE,
+        TEST_RUN_ID,
         STRING_ENCODING,
         DATA_STRING,
         DATA_STRING,
@@ -155,13 +158,16 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
                 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 ) );
+                        memento.getData().add( RUN_MODES.get( readSegment( memento ) ) );
+                        break;
+                    case TEST_RUN_ID:
+                        memento.getData().add( readLong( memento ) );
                         break;
                     case STRING_ENCODING:
                         memento.setCharset( readCharset( memento ) );
@@ -175,7 +181,7 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
                     case END_OF_FRAME:
                         memento.getLine().setPositionByteBuffer( memento.getByteBuffer().position() );
                         memento.getLine().clear();
-                        return toMessage( eventType, runMode, memento );
+                        return toMessage( eventType, memento );
                     default:
                         memento.getLine().setPositionByteBuffer( NO_POSITION );
                         getArguments()
@@ -244,9 +250,9 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
             case BOOTERCODE_STDOUT_NEW_LINE:
             case BOOTERCODE_STDERR:
             case BOOTERCODE_STDERR_NEW_LINE:
-                return EVENT_WITH_RUNMODE_AND_ONE_STRING;
+                return EVENT_WITH_RUNMODE_TID_AND_ONE_STRING;
             case BOOTERCODE_SYSPROPS:
-                return EVENT_WITH_RUNMODE_AND_TWO_STRINGS;
+                return EVENT_WITH_RUNMODE_TID_AND_TWO_STRINGS;
             case BOOTERCODE_TESTSET_STARTING:
             case BOOTERCODE_TESTSET_COMPLETED:
             case BOOTERCODE_TEST_STARTING:
@@ -263,9 +269,7 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
 
     @Override
     @Nonnull
-    protected final Event toMessage( @Nonnull ForkedProcessEventType eventType,
-                                     RunMode runMode,
-                                     @Nonnull Memento memento )
+    protected final Event toMessage( @Nonnull ForkedProcessEventType eventType, @Nonnull Memento memento )
         throws MalformedFrameException
     {
         switch ( eventType )
@@ -279,12 +283,12 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
             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_ERROR:
+                checkArguments( memento, 3 );
+                return new ConsoleErrorEvent( toStackTraceWriter( memento.getData() ) );
             case BOOTERCODE_CONSOLE_INFO:
                 checkArguments( memento, 1 );
                 return new ConsoleInfoEvent( (String) memento.getData().get( 0 ) );
@@ -295,46 +299,49 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
                 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 ) );
+                checkArguments( memento, 3 );
+                return new StandardStreamOutEvent( memento.ofDataAt( 0 ), memento.ofDataAt( 1 ),
+                    memento.ofDataAt( 2 ) );
             case BOOTERCODE_STDOUT_NEW_LINE:
-                checkArguments( runMode, memento, 1 );
-                return new StandardStreamOutWithNewLineEvent( runMode, (String) memento.getData().get( 0 ) );
+                checkArguments( memento, 3 );
+                return new StandardStreamOutWithNewLineEvent( memento.ofDataAt( 0 ), memento.ofDataAt( 1 ),
+                    memento.ofDataAt( 2 ) );
             case BOOTERCODE_STDERR:
-                checkArguments( runMode, memento, 1 );
-                return new StandardStreamErrEvent( runMode, (String) memento.getData().get( 0 ) );
+                checkArguments( memento, 3 );
+                return new StandardStreamErrEvent( memento.ofDataAt( 0 ), memento.ofDataAt( 1 ),
+                    memento.ofDataAt( 2 ) );
             case BOOTERCODE_STDERR_NEW_LINE:
-                checkArguments( runMode, memento, 1 );
-                return new StandardStreamErrWithNewLineEvent( runMode, (String) memento.getData().get( 0 ) );
+                checkArguments( memento, 3 );
+                return new StandardStreamErrWithNewLineEvent( memento.ofDataAt( 0 ), memento.ofDataAt( 1 ),
+                    memento.ofDataAt( 2 ) );
             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 );
+                checkArguments( memento, 4 );
+                return new SystemPropertyEvent( memento.ofDataAt( 0 ), memento.ofDataAt( 1 ),
+                    memento.ofDataAt( 2 ), memento.ofDataAt( 3 ) );
             case BOOTERCODE_TESTSET_STARTING:
-                checkArguments( runMode, memento, 10 );
-                return new TestsetStartingEvent( runMode, toReportEntry( memento.getData() ) );
+                checkArguments( memento, 12 );
+                return new TestsetStartingEvent( toReportEntry( memento.getData() ) );
             case BOOTERCODE_TESTSET_COMPLETED:
-                checkArguments( runMode, memento, 10 );
-                return new TestsetCompletedEvent( runMode, toReportEntry( memento.getData() ) );
+                checkArguments( memento, 12 );
+                return new TestsetCompletedEvent( toReportEntry( memento.getData() ) );
             case BOOTERCODE_TEST_STARTING:
-                checkArguments( runMode, memento, 10 );
-                return new TestStartingEvent( runMode, toReportEntry( memento.getData() ) );
+                checkArguments( memento, 12 );
+                return new TestStartingEvent( toReportEntry( memento.getData() ) );
             case BOOTERCODE_TEST_SUCCEEDED:
-                checkArguments( runMode, memento, 10 );
-                return new TestSucceededEvent( runMode, toReportEntry( memento.getData() ) );
+                checkArguments( memento, 12 );
+                return new TestSucceededEvent( toReportEntry( memento.getData() ) );
             case BOOTERCODE_TEST_FAILED:
-                checkArguments( runMode, memento, 10 );
-                return new TestFailedEvent( runMode, toReportEntry( memento.getData() ) );
+                checkArguments( memento, 12 );
+                return new TestFailedEvent( toReportEntry( memento.getData() ) );
             case BOOTERCODE_TEST_SKIPPED:
-                checkArguments( runMode, memento, 10 );
-                return new TestSkippedEvent( runMode, toReportEntry( memento.getData() ) );
+                checkArguments( memento, 12 );
+                return new TestSkippedEvent( toReportEntry( memento.getData() ) );
             case BOOTERCODE_TEST_ERROR:
-                checkArguments( runMode, memento, 10 );
-                return new TestErrorEvent( runMode, toReportEntry( memento.getData() ) );
+                checkArguments( memento, 12 );
+                return new TestErrorEvent( toReportEntry( memento.getData() ) );
             case BOOTERCODE_TEST_ASSUMPTIONFAILURE:
-                checkArguments( runMode, memento, 10 );
-                return new TestAssumptionFailureEvent( runMode, toReportEntry( memento.getData() ) );
+                checkArguments( memento, 12 );
+                return new TestAssumptionFailureEvent( toReportEntry( memento.getData() ) );
             default:
                 throw new IllegalArgumentException( "Missing a branch for the event type " + eventType );
         }
@@ -343,19 +350,21 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
     @Nonnull
     private static TestSetReportEntry toReportEntry( List<Object> args )
     {
+        RunMode runMode = (RunMode) args.get( 0 );
+        long testRunId = (long) args.get( 1 );
         // 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 );
+        String source = (String) args.get( 2 );
+        String sourceText = (String) args.get( 3 );
+        String name = (String) args.get( 4 );
+        String nameText = (String) args.get( 5 );
+        String group = (String) args.get( 6 );
+        String message = (String) args.get( 7 );
+        Integer timeElapsed = (Integer) args.get( 8 );
         // 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,
+        String traceMessage = (String) args.get( 9 );
+        String smartTrimmedStackTrace = (String) args.get( 10 );
+        String stackTrace = (String) args.get( 11 );
+        return newReportEntry( runMode, testRunId, source, sourceText, name, nameText, group, message, timeElapsed,
             traceMessage, smartTrimmedStackTrace, stackTrace );
     }
 
@@ -374,8 +383,8 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
     }
 
     static TestSetReportEntry newReportEntry( // ReportEntry:
-                                              String source, String sourceText, String name,
-                                              String nameText, String group, String message,
+                                              RunMode runMode, long testRunId, String source, String sourceText,
+                                              String name, String nameText, String group, String message,
                                               Integer timeElapsed,
                                               // StackTraceWriter:
                                               String traceMessage,
@@ -383,8 +392,8 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
         throws NumberFormatException
     {
         StackTraceWriter stackTraceWriter = toTrace( traceMessage, smartTrimmedStackTrace, stackTrace );
-        return reportEntry( NORMAL_RUN /*todo*/, 0L /*todo*/, source, sourceText, name, nameText, group,
-            stackTraceWriter, timeElapsed, message, emptyMap() );
+        return reportEntry( runMode, testRunId, source, sourceText, name, nameText, group, stackTraceWriter,
+            timeElapsed, message, emptyMap() );
     }
 
     private static Map<Segment, ForkedProcessEventType> segmentsToEvents()
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 8eae70a..e595cba 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
@@ -185,7 +185,7 @@ public class ForkingRunListenerTest
     {
         final StandardTestRun standardTestRun = new StandardTestRun();
         TestOutputReceiver<TestOutputReportEntry> directConsoleReporter = standardTestRun.run();
-        directConsoleReporter.writeTestOutput( (TestOutputReportEntry) stdOut( "HeyYou" ) );
+        directConsoleReporter.writeTestOutput( new TestOutputReportEntry( stdOut( "HeyYou" ), NORMAL_RUN, 1L )  );
         standardTestRun.assertExpected( MockReporter.STDOUT, "HeyYou" );
     }
 
@@ -200,14 +200,18 @@ public class ForkingRunListenerTest
         TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
         ForkClient forkStreamClient = new ForkClient( providerReporterFactory, new MockNotifiableTestStream(), 1 );
 
-        byte[] cmd = ( ":maven-surefire-event:\u0008:sys-prop:" + (char) 10 + ":normal-run:\u0005:UTF-8:"
-            + "\u0000\u0000\u0000\u0002:k1:\u0000\u0000\u0000\u0002:v1:\n" ).getBytes();
+        byte[] cmd = ( ":maven-surefire-event:\u0008:sys-prop:" + (char) 10 + ":normal-run:"
+            + "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
+            + "\u0005:UTF-8:"
+            + "\u0000\u0000\u0000\u0002:k1:\u0000\u0000\u0000\u0002:v1:" ).getBytes();
         for ( Event e : streamToEvent( cmd ) )
         {
             forkStreamClient.handleEvent( e );
         }
-        cmd = ( "\n:maven-surefire-event:\u0008:sys-prop:" + (char) 10 + ":normal-run:\u0005:UTF-8:"
-            + "\u0000\u0000\u0000\u0002:k2:\u0000\u0000\u0000\u0002:v2:\n" ).getBytes();
+        cmd = ( "\n:maven-surefire-event:\u0008:sys-prop:" + (char) 10 + ":normal-run:"
+            + "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
+            + "\u0005:UTF-8:"
+            + "\u0000\u0000\u0000\u0002:k2:\u0000\u0000\u0000\u0002:v2:" ).getBytes();
         for ( Event e : streamToEvent( cmd ) )
         {
             forkStreamClient.handleEvent( e );
@@ -279,7 +283,7 @@ public class ForkingRunListenerTest
 
         MockReporter reporter = (MockReporter) forkStreamClient.getReporter();
         assertThat( reporter.getFirstEvent() ).isEqualTo( MockReporter.TEST_STARTING );
-        //assertThat( reporter.getFirstData() ).isEqualTo( expected ); /*todo uncomment in SUREFIRE-2014*/
+        assertThat( reporter.getFirstData() ).isEqualTo( expected );
         assertThat( reporter.getEvents() ).hasSize( 1 );
 
         forkStreamClient = new ForkClient( providerReporterFactory, notifiableTestStream, 2 );
@@ -289,7 +293,7 @@ public class ForkingRunListenerTest
         }
         MockReporter reporter2 = (MockReporter) forkStreamClient.getReporter();
         assertThat( reporter2.getFirstEvent() ).isEqualTo( MockReporter.TEST_SKIPPED );
-        //assertThat( reporter2.getFirstData() ).isEqualTo( secondExpected ); /*todo uncomment in SUREFIRE-2014*/
+        assertThat( reporter2.getFirstData() ).isEqualTo( secondExpected );
         assertThat( reporter2.getEvents() ).hasSize( 1 );
     }
 
@@ -464,7 +468,7 @@ public class ForkingRunListenerTest
         {
             StackTraceWriter stackTraceWriter =
                 new LegacyPojoStackTraceWriter( "org.apache.tests.TestClass", "testMethod11", e );
-            return new CategorizedReportEntry( NORMAL_RUN, 0L,
+            return new CategorizedReportEntry( NORMAL_RUN, 1L,
                 "com.abc.TestClass", "testMethod", "aGroup", stackTraceWriter, 77 );
         }
     }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
index ff0bbb3..e46be43 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
@@ -25,6 +25,7 @@ import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 
 import java.io.File;
@@ -43,7 +44,7 @@ public class TestSetMockReporterFactory
     }
 
     @Override
-    public TestReportListener createTestReportListener()
+    public TestReportListener<TestOutputReportEntry> createTestReportListener()
     {
         return new MockReporter();
     }
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 5d8251c..393d5c9 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
@@ -474,7 +474,7 @@ public class ForkClientTest
                 .thenReturn( receiver );
         NotifiableTestStream notifiableTestStream = mock( NotifiableTestStream.class );
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        client.handleEvent( new StandardStreamOutEvent( NORMAL_RUN, "msg" ) );
+        client.handleEvent( new StandardStreamOutEvent( NORMAL_RUN, 1L, "msg" ) );
         verifyZeroInteractions( notifiableTestStream );
         verify( factory, times( 1 ) )
                 .createTestReportListener();
@@ -516,7 +516,7 @@ public class ForkClientTest
                 .thenReturn( receiver );
         NotifiableTestStream notifiableTestStream = mock( NotifiableTestStream.class );
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        client.handleEvent( new StandardStreamOutWithNewLineEvent( NORMAL_RUN, "msg" ) );
+        client.handleEvent( new StandardStreamOutWithNewLineEvent( NORMAL_RUN, 1L, "msg" ) );
         verifyZeroInteractions( notifiableTestStream );
         verify( factory, times( 1 ) )
                 .createTestReportListener();
@@ -558,7 +558,7 @@ public class ForkClientTest
                 .thenReturn( receiver );
         NotifiableTestStream notifiableTestStream = mock( NotifiableTestStream.class );
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        client.handleEvent( new StandardStreamErrEvent( NORMAL_RUN, "msg" ) );
+        client.handleEvent( new StandardStreamErrEvent( NORMAL_RUN, 1L, "msg" ) );
         verifyZeroInteractions( notifiableTestStream );
         verify( factory, times( 1 ) )
                 .createTestReportListener();
@@ -600,7 +600,7 @@ public class ForkClientTest
                 .thenReturn( receiver );
         NotifiableTestStream notifiableTestStream = mock( NotifiableTestStream.class );
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        client.handleEvent( new StandardStreamErrWithNewLineEvent( NORMAL_RUN, "msg" ) );
+        client.handleEvent( new StandardStreamErrWithNewLineEvent( NORMAL_RUN, 1L, "msg" ) );
         verifyZeroInteractions( notifiableTestStream );
         verify( factory, times( 1 ) )
                 .createTestReportListener();
@@ -856,7 +856,7 @@ public class ForkClientTest
                 .thenReturn( receiver );
         NotifiableTestStream notifiableTestStream = mock( NotifiableTestStream.class );
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        client.handleEvent( new SystemPropertyEvent( NORMAL_RUN, "k1", "v1" ) );
+        client.handleEvent( new SystemPropertyEvent( NORMAL_RUN, 1L, "k1", "v1" ) );
         verifyZeroInteractions( notifiableTestStream );
         verifyZeroInteractions( factory );
         assertThat( client.getReporter() )
@@ -909,6 +909,8 @@ public class ForkClientTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         TestSetReportEntry reportEntry = mock( TestSetReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
@@ -918,7 +920,7 @@ public class ForkClientTest
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        client.handleEvent( new TestsetStartingEvent( NORMAL_RUN, reportEntry ) );
+        client.handleEvent( new TestsetStartingEvent( reportEntry ) );
 
         client.tryToTimeout( System.currentTimeMillis() + 1000L, 1 );
 
@@ -1004,6 +1006,8 @@ public class ForkClientTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         TestSetReportEntry reportEntry = mock( TestSetReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
@@ -1015,7 +1019,7 @@ public class ForkClientTest
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        client.handleEvent( new TestsetStartingEvent( NORMAL_RUN, reportEntry ) );
+        client.handleEvent( new TestsetStartingEvent( reportEntry ) );
         client.tryToTimeout( System.currentTimeMillis(), 1 );
 
         verifyZeroInteractions( notifiableTestStream );
@@ -1100,6 +1104,8 @@ public class ForkClientTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         TestSetReportEntry reportEntry = mock( TestSetReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
@@ -1109,7 +1115,7 @@ public class ForkClientTest
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        client.handleEvent( new TestsetCompletedEvent( NORMAL_RUN, reportEntry ) );
+        client.handleEvent( new TestsetCompletedEvent( reportEntry ) );
 
         verifyZeroInteractions( notifiableTestStream );
         verify( factory ).createTestReportListener();
@@ -1193,6 +1199,8 @@ public class ForkClientTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
@@ -1202,7 +1210,7 @@ public class ForkClientTest
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        client.handleEvent( new TestStartingEvent( NORMAL_RUN, reportEntry ) );
+        client.handleEvent( new TestStartingEvent( reportEntry ) );
 
         verifyZeroInteractions( notifiableTestStream );
         verify( factory ).createTestReportListener();
@@ -1287,6 +1295,8 @@ public class ForkClientTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
@@ -1298,13 +1308,13 @@ public class ForkClientTest
         ForkClient client = new ForkClient( factory, notifiableTestStream,  0 );
         SimpleReportEntry testStarted =
             new SimpleReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(), null, null, null );
-        client.handleEvent( new TestStartingEvent( NORMAL_RUN, testStarted ) );
+        client.handleEvent( new TestStartingEvent( testStarted ) );
 
         assertThat( client.testsInProgress() )
                 .hasSize( 1 )
                 .contains( "pkg.MyTest" );
 
-        client.handleEvent( new TestSucceededEvent( NORMAL_RUN, reportEntry ) );
+        client.handleEvent( new TestSucceededEvent( reportEntry ) );
 
         verifyZeroInteractions( notifiableTestStream );
         verify( factory ).createTestReportListener();
@@ -1392,6 +1402,8 @@ public class ForkClientTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
@@ -1403,13 +1415,13 @@ public class ForkClientTest
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
         SimpleReportEntry testClass =
             new SimpleReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(), null, null, null );
-        client.handleEvent( new TestStartingEvent( NORMAL_RUN, testClass ) );
+        client.handleEvent( new TestStartingEvent( testClass ) );
 
         assertThat( client.testsInProgress() )
                 .hasSize( 1 )
                 .contains( "pkg.MyTest" );
 
-        client.handleEvent( new TestFailedEvent( NORMAL_RUN, reportEntry ) );
+        client.handleEvent( new TestFailedEvent( reportEntry ) );
 
         verifyZeroInteractions( notifiableTestStream );
         verify( factory ).createTestReportListener();
@@ -1503,6 +1515,8 @@ public class ForkClientTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
@@ -1514,13 +1528,13 @@ public class ForkClientTest
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
         SimpleReportEntry testStarted =
             new SimpleReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(), null, null, null );
-        client.handleEvent( new TestStartingEvent( NORMAL_RUN, testStarted ) );
+        client.handleEvent( new TestStartingEvent( testStarted ) );
 
         assertThat( client.testsInProgress() )
                 .hasSize( 1 )
                 .contains( "pkg.MyTest" );
 
-        client.handleEvent( new TestSkippedEvent( NORMAL_RUN, reportEntry ) );
+        client.handleEvent( new TestSkippedEvent( reportEntry ) );
 
         verifyZeroInteractions( notifiableTestStream );
         verify( factory ).createTestReportListener();
@@ -1612,6 +1626,8 @@ public class ForkClientTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
@@ -1624,13 +1640,13 @@ public class ForkClientTest
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
         SimpleReportEntry testStarted = new SimpleReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(),
             reportEntry.getSourceText(), null, null );
-        client.handleEvent( new TestStartingEvent( NORMAL_RUN, testStarted ) );
+        client.handleEvent( new TestStartingEvent( testStarted ) );
 
         assertThat( client.testsInProgress() )
                 .hasSize( 1 )
                 .contains( "pkg.MyTest" );
 
-        client.handleEvent( new TestErrorEvent( NORMAL_RUN, reportEntry ) );
+        client.handleEvent( new TestErrorEvent( reportEntry ) );
 
         verifyZeroInteractions( notifiableTestStream );
         verify( factory ).createTestReportListener();
@@ -1718,6 +1734,8 @@ public class ForkClientTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
@@ -1730,13 +1748,13 @@ public class ForkClientTest
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
         SimpleReportEntry testStarted =
             new SimpleReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(), null, null, null );
-        client.handleEvent( new TestStartingEvent( NORMAL_RUN, testStarted ) );
+        client.handleEvent( new TestStartingEvent( testStarted ) );
 
         assertThat( client.testsInProgress() )
                 .hasSize( 1 )
                 .contains( "pkg.MyTest" );
 
-        client.handleEvent( new TestAssumptionFailureEvent( NORMAL_RUN, reportEntry ) );
+        client.handleEvent( new TestAssumptionFailureEvent( reportEntry ) );
 
         verifyZeroInteractions( notifiableTestStream );
         verify( factory ).createTestReportListener();
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumerTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumerTest.java
index 5b5c3fa..804b76d 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumerTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumerTest.java
@@ -107,7 +107,7 @@ public class ThreadedStreamConsumerTest
 
         long t1 = System.currentTimeMillis();
 
-        Event event = new StandardStreamOutWithNewLineEvent( NORMAL_RUN, "" );
+        Event event = new StandardStreamOutWithNewLineEvent( NORMAL_RUN, 1L, "" );
         for ( int i = 0; i < 5_000_000; i++ )
         {
             streamConsumer.handleEvent( event );
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 304ed57..0904045 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
@@ -43,6 +43,7 @@ import org.apache.maven.surefire.api.event.Event;
 import org.apache.maven.surefire.api.fork.ForkNodeArguments;
 import org.apache.maven.surefire.api.report.OutputReportEntry;
 import org.apache.maven.surefire.api.report.TestOutputReceiver;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.booter.spi.EventChannelEncoder;
 import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
 import org.apache.maven.surefire.extensions.CommandReader;
@@ -54,6 +55,7 @@ import org.junit.rules.ExpectedException;
 
 import static java.util.concurrent.TimeUnit.HOURS;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.apache.maven.surefire.api.report.TestOutputReportEntry.stdOutln;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
@@ -156,7 +158,7 @@ public class E2ETest
                     for ( int i = 0; i < totalCalls; i++ )
                     {
                         //System.out.println( LONG_STRING );
-                        encoder.testOutput( stdOutln( LONG_STRING ) );
+                        encoder.testOutput( new TestOutputReportEntry( stdOutln( LONG_STRING ), NORMAL_RUN, 1L ) );
                     }
                     long t2 = System.currentTimeMillis();
                     long spent = t2 - t1;
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 b22f0f8..3fac0e4 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
@@ -77,6 +77,7 @@ public class EventConsumerThreadTest
         event.put( ":std-out-stream:".getBytes( UTF_8 ) );
         event.put( (byte) 10 );
         event.put( ":normal-run:".getBytes( UTF_8 ) );
+        event.put( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:".getBytes( UTF_8 ) );
         event.put( (byte) 5 );
         event.put( ":UTF-8:".getBytes( UTF_8 ) );
         event.putInt( 100 );
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 a252077..f59c235 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
@@ -216,6 +216,8 @@ public class ForkedProcessEventNotifierTest
             when( stackTraceWriter.writeTraceToString() ).thenReturn( exceptionStackTrace );
 
             ReportEntry reportEntry = mock( ReportEntry.class );
+            when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+            when( reportEntry.getTestRunId() ).thenReturn( 1L );
             when( reportEntry.getElapsed() ).thenReturn( 7 );
             when( reportEntry.getGroup() ).thenReturn( null );
             when( reportEntry.getMessage() ).thenReturn( null );
@@ -531,7 +533,7 @@ public class ForkedProcessEventNotifierTest
             final Stream out = Stream.newStream();
             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
             EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
-            encoder.testOutput( (TestOutputReportEntry) stdOut( "msg" ) );
+            encoder.testOutput( new TestOutputReportEntry( stdOut( "msg" ), NORMAL_RUN, 1L ) );
             wChannel.close();
 
             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -570,7 +572,7 @@ public class ForkedProcessEventNotifierTest
             final Stream out = Stream.newStream();
             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
             EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
-            encoder.testOutput( (TestOutputReportEntry) stdOut( "" ) );
+            encoder.testOutput( new TestOutputReportEntry( stdOut( "" ), NORMAL_RUN, 1L ) );
             wChannel.close();
 
             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -609,7 +611,7 @@ public class ForkedProcessEventNotifierTest
             final Stream out = Stream.newStream();
             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
             EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
-            encoder.testOutput( (TestOutputReportEntry) stdOut( null ) );
+            encoder.testOutput( new TestOutputReportEntry( stdOut( null ), NORMAL_RUN, 1L ) );
             wChannel.close();
 
             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -648,7 +650,7 @@ public class ForkedProcessEventNotifierTest
             final Stream out = Stream.newStream();
             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
             EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
-            encoder.testOutput( stdOutln( "" ) );
+            encoder.testOutput( new TestOutputReportEntry( stdOutln( "" ), NORMAL_RUN, 1L ) );
             wChannel.close();
 
             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -687,7 +689,7 @@ public class ForkedProcessEventNotifierTest
             final Stream out = Stream.newStream();
             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
             EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
-            encoder.testOutput( stdOutln( null ) );
+            encoder.testOutput( new TestOutputReportEntry( stdOutln( null ), NORMAL_RUN, 1L ) );
             wChannel.close();
 
             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -726,7 +728,7 @@ public class ForkedProcessEventNotifierTest
             final Stream out = Stream.newStream();
             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
             EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
-            encoder.testOutput( stdErr( "msg" ) );
+            encoder.testOutput( new TestOutputReportEntry( stdErr( "msg" ), NORMAL_RUN, 1L ) );
             wChannel.close();
 
             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -922,6 +924,8 @@ public class ForkedProcessEventNotifierTest
             }
 
             TestSetReportEntry reportEntry = mock( TestSetReportEntry.class );
+            when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+            when( reportEntry.getTestRunId() ).thenReturn( 1L );
             when( reportEntry.getElapsed() ).thenReturn( elapsed );
             when( reportEntry.getGroup() ).thenReturn( "this group" );
             when( reportEntry.getMessage() ).thenReturn( reportedMessage );
@@ -1100,7 +1104,7 @@ public class ForkedProcessEventNotifierTest
             this.hasStackTrace = hasStackTrace;
         }
 
-        public void handle( RunMode runMode, ReportEntry reportEntry )
+        public void handle( ReportEntry reportEntry )
         {
             called.set( true );
             assertThat( reportEntry.getSourceName() ).isEqualTo( this.reportEntry.getSourceName() );
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 696286c..c7e3cd5 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
@@ -108,8 +108,6 @@ public class StreamFeederTest
             .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 )
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java
index 9f65264..efae6ee 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java
@@ -91,6 +91,8 @@ 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.apache.maven.surefire.api.stream.SegmentType.TEST_RUN_ID;
+import static org.apache.maven.surefire.stream.EventDecoder.newReportEntry;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.powermock.api.mockito.PowerMockito.mock;
 import static org.powermock.api.mockito.PowerMockito.when;
@@ -177,83 +179,84 @@ public class EventDecoderTest
 
         segmentTypes = decoder.nextSegmentType( BOOTERCODE_STDOUT );
         assertThat( segmentTypes )
-            .hasSize( 4 )
-            .isEqualTo( new SegmentType[] { RUN_MODE, STRING_ENCODING, DATA_STRING, END_OF_FRAME } );
+            .hasSize( 5 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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 } );
+            .hasSize( 5 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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 } );
+            .hasSize( 5 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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 } );
+            .hasSize( 5 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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 } );
+            .hasSize( 6 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, STRING_ENCODING, DATA_STRING, DATA_STRING,
+                END_OF_FRAME } );
 
         segmentTypes = decoder.nextSegmentType( BOOTERCODE_TESTSET_STARTING );
         assertThat( segmentTypes )
-            .hasSize( 13 )
+            .hasSize( 14 )
             .isEqualTo( new SegmentType[] {
-                RUN_MODE, STRING_ENCODING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING, DATA_STRING,
+                RUN_MODE, TEST_RUN_ID, 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,
+            .hasSize( 14 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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,
+            .hasSize( 14 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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,
+            .hasSize( 14 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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,
+            .hasSize( 14 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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,
+            .hasSize( 14 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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,
+            .hasSize( 14 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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,
+            .hasSize( 14 )
+            .isEqualTo( new SegmentType[] { RUN_MODE, TEST_RUN_ID, 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 } );
     }
 
@@ -264,21 +267,21 @@ public class EventDecoderTest
         Channel channel = new Channel( stream, 1 );
         EventDecoder decoder = new EventDecoder( channel, new MockForkNodeArguments() );
 
-        Event event = decoder.toMessage( BOOTERCODE_BYE, NORMAL_RUN, decoder.new Memento() );
+        Event event = decoder.toMessage( BOOTERCODE_BYE, decoder.new Memento() );
         assertThat( event )
             .isInstanceOf( ControlByeEvent.class );
 
-        event = decoder.toMessage( BOOTERCODE_STOP_ON_NEXT_TEST, NORMAL_RUN, decoder.new Memento() );
+        event = decoder.toMessage( BOOTERCODE_STOP_ON_NEXT_TEST, decoder.new Memento() );
         assertThat( event )
             .isInstanceOf( ControlStopOnNextTestEvent.class );
 
-        event = decoder.toMessage( BOOTERCODE_NEXT_TEST, NORMAL_RUN, decoder.new Memento() );
+        event = decoder.toMessage( BOOTERCODE_NEXT_TEST, 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 );
+        event = decoder.toMessage( BOOTERCODE_CONSOLE_ERROR, memento );
         assertThat( event )
             .isInstanceOf( ConsoleErrorEvent.class );
         ConsoleErrorEvent consoleErrorEvent = (ConsoleErrorEvent) event;
@@ -291,7 +294,7 @@ public class EventDecoderTest
 
         memento = decoder.new Memento();
         memento.getData().addAll( asList( null, null, null ) );
-        event = decoder.toMessage( BOOTERCODE_CONSOLE_ERROR, NORMAL_RUN, memento );
+        event = decoder.toMessage( BOOTERCODE_CONSOLE_ERROR, memento );
         assertThat( event )
             .isInstanceOf( ConsoleErrorEvent.class );
         consoleErrorEvent = (ConsoleErrorEvent) event;
@@ -300,7 +303,7 @@ public class EventDecoderTest
 
         memento = decoder.new Memento();
         memento.getData().addAll( asList( "1", "2", "3" ) );
-        event = decoder.toMessage( BOOTERCODE_JVM_EXIT_ERROR, NORMAL_RUN, memento );
+        event = decoder.toMessage( BOOTERCODE_JVM_EXIT_ERROR, memento );
         assertThat( event )
             .isInstanceOf( JvmExitErrorEvent.class );
         JvmExitErrorEvent jvmExitErrorEvent = (JvmExitErrorEvent) event;
@@ -313,7 +316,7 @@ public class EventDecoderTest
 
         memento = decoder.new Memento();
         memento.getData().addAll( asList( null, null, null ) );
-        event = decoder.toMessage( BOOTERCODE_JVM_EXIT_ERROR, NORMAL_RUN, memento );
+        event = decoder.toMessage( BOOTERCODE_JVM_EXIT_ERROR, memento );
         assertThat( event )
             .isInstanceOf( JvmExitErrorEvent.class );
         jvmExitErrorEvent = (JvmExitErrorEvent) event;
@@ -322,64 +325,70 @@ public class EventDecoderTest
 
         memento = decoder.new Memento();
         memento.getData().addAll( singletonList( "m" ) );
-        event = decoder.toMessage( BOOTERCODE_CONSOLE_INFO, NORMAL_RUN, memento );
+        event = decoder.toMessage( BOOTERCODE_CONSOLE_INFO, 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 );
+        event = decoder.toMessage( BOOTERCODE_CONSOLE_WARNING, 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 );
+        event = decoder.toMessage( BOOTERCODE_CONSOLE_DEBUG, 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 );
+        memento.getData().addAll( asList( NORMAL_RUN, 1L, "m" ) );
+        event = decoder.toMessage( BOOTERCODE_STDOUT, memento );
         assertThat( event ).isInstanceOf( StandardStreamOutEvent.class );
         assertThat( ( (StandardStreamOutEvent) event ).getMessage() ).isEqualTo( "m" );
         assertThat( ( (StandardStreamOutEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (StandardStreamOutEvent) event ).getTestRunId() ).isEqualTo( 1L );
 
         memento = decoder.new Memento();
-        memento.getData().addAll( singletonList( null ) );
-        event = decoder.toMessage( BOOTERCODE_STDOUT_NEW_LINE, RERUN_TEST_AFTER_FAILURE, memento );
+        memento.getData().addAll( asList( RERUN_TEST_AFTER_FAILURE, 1L, null ) );
+        event = decoder.toMessage( BOOTERCODE_STDOUT_NEW_LINE, memento );
         assertThat( event ).isInstanceOf( StandardStreamOutWithNewLineEvent.class );
         assertThat( ( (StandardStreamOutWithNewLineEvent) event ).getMessage() ).isNull();
         assertThat( ( (StandardStreamOutWithNewLineEvent) event ).getRunMode() ).isEqualTo( RERUN_TEST_AFTER_FAILURE );
+        assertThat( ( (StandardStreamOutWithNewLineEvent) event ).getTestRunId() ).isEqualTo( 1L );
 
         memento = decoder.new Memento();
-        memento.getData().addAll( singletonList( null ) );
-        event = decoder.toMessage( BOOTERCODE_STDERR, RERUN_TEST_AFTER_FAILURE, memento );
+        memento.getData().addAll( asList( RERUN_TEST_AFTER_FAILURE, 1L, null ) );
+        event = decoder.toMessage( BOOTERCODE_STDERR, memento );
         assertThat( event ).isInstanceOf( StandardStreamErrEvent.class );
         assertThat( ( (StandardStreamErrEvent) event ).getMessage() ).isNull();
         assertThat( ( (StandardStreamErrEvent) event ).getRunMode() ).isEqualTo( RERUN_TEST_AFTER_FAILURE );
+        assertThat( ( (StandardStreamErrEvent) event ).getTestRunId() ).isEqualTo( 1L );
 
         memento = decoder.new Memento();
-        memento.getData().addAll( singletonList( "abc" ) );
-        event = decoder.toMessage( BOOTERCODE_STDERR_NEW_LINE, NORMAL_RUN, memento );
+        memento.getData().addAll( asList( NORMAL_RUN, 1L, "abc" ) );
+        event = decoder.toMessage( BOOTERCODE_STDERR_NEW_LINE, memento );
         assertThat( event ).isInstanceOf( StandardStreamErrWithNewLineEvent.class );
         assertThat( ( (StandardStreamErrWithNewLineEvent) event ).getMessage() ).isEqualTo( "abc" );
         assertThat( ( (StandardStreamErrWithNewLineEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (StandardStreamErrWithNewLineEvent) event ).getTestRunId() ).isEqualTo( 1L );
 
         memento = decoder.new Memento();
-        memento.getData().addAll( asList( "key", "value" ) );
-        event = decoder.toMessage( BOOTERCODE_SYSPROPS, NORMAL_RUN, memento );
+        memento.getData().addAll( asList( NORMAL_RUN, 1L, "key", "value" ) );
+        event = decoder.toMessage( BOOTERCODE_SYSPROPS, memento );
         assertThat( event ).isInstanceOf( SystemPropertyEvent.class );
+        assertThat( ( (SystemPropertyEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (SystemPropertyEvent) event ).getTestRunId() ).isEqualTo( 1L );
         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 );
+        memento.getData().addAll( asList( NORMAL_RUN , 1L, "source", "sourceText", "name", "nameText", "group",
+            "message", 5, "traceMessage", "smartTrimmedStackTrace", "stackTrace" ) );
+        event = decoder.toMessage( BOOTERCODE_TESTSET_STARTING, memento );
         assertThat( event ).isInstanceOf( TestsetStartingEvent.class );
-        assertThat( ( (TestsetStartingEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getTestRunId() ).isEqualTo( 1L );
         assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
         assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
         assertThat( ( (TestsetStartingEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
@@ -396,11 +405,12 @@ public class EventDecoderTest
             .isEqualTo( "stackTrace" );
 
         memento = decoder.new Memento();
-        memento.getData().addAll( asList( "source", "sourceText", "name", "nameText", "group", null, 5,
+        memento.getData().addAll( asList( NORMAL_RUN, 1L, "source", "sourceText", "name", "nameText", "group", null, 5,
             "traceMessage", "smartTrimmedStackTrace", "stackTrace" ) );
-        event = decoder.toMessage( BOOTERCODE_TESTSET_COMPLETED, NORMAL_RUN, memento );
+        event = decoder.toMessage( BOOTERCODE_TESTSET_COMPLETED, memento );
         assertThat( event ).isInstanceOf( TestsetCompletedEvent.class );
-        assertThat( ( (TestsetCompletedEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getTestRunId() ).isEqualTo( 1L );
         assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
         assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
         assertThat( ( (TestsetCompletedEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
@@ -417,11 +427,12 @@ public class EventDecoderTest
             .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 );
+        memento.getData().addAll( asList( NORMAL_RUN, 1L, "source", "sourceText", "name", "nameText", "group",
+            "message", 5, null, "smartTrimmedStackTrace", "stackTrace" ) );
+        event = decoder.toMessage( BOOTERCODE_TEST_STARTING, memento );
         assertThat( event ).isInstanceOf( TestStartingEvent.class );
-        assertThat( ( (TestStartingEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestStartingEvent) event ).getReportEntry().getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestStartingEvent) event ).getReportEntry().getTestRunId() ).isEqualTo( 1L );
         assertThat( ( (TestStartingEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
         assertThat( ( (TestStartingEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
         assertThat( ( (TestStartingEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
@@ -438,11 +449,12 @@ public class EventDecoderTest
             .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 );
+        memento.getData().addAll( asList( NORMAL_RUN, 1L, "source", "sourceText", "name", "nameText", "group",
+            "message", 5, null, null, null ) );
+        event = decoder.toMessage( BOOTERCODE_TEST_SUCCEEDED, memento );
         assertThat( event ).isInstanceOf( TestSucceededEvent.class );
-        assertThat( ( (TestSucceededEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestSucceededEvent) event ).getReportEntry().getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestSucceededEvent) event ).getReportEntry().getTestRunId() ).isEqualTo( 1L );
         assertThat( ( (TestSucceededEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
         assertThat( ( (TestSucceededEvent) event ).getReportEntry().getSourceText() ).isEqualTo( "sourceText" );
         assertThat( ( (TestSucceededEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
@@ -453,11 +465,12 @@ public class EventDecoderTest
         assertThat( ( (TestSucceededEvent) event ).getReportEntry().getStackTraceWriter() ).isNull();
 
         memento = decoder.new Memento();
-        memento.getData().addAll( asList( "source", null, "name", null, "group", null, 5,
+        memento.getData().addAll( asList( RERUN_TEST_AFTER_FAILURE, 1L, "source", null, "name", null, "group", null, 5,
             "traceMessage", "smartTrimmedStackTrace", "stackTrace" ) );
-        event = decoder.toMessage( BOOTERCODE_TEST_FAILED, RERUN_TEST_AFTER_FAILURE, memento );
+        event = decoder.toMessage( BOOTERCODE_TEST_FAILED, memento );
         assertThat( event ).isInstanceOf( TestFailedEvent.class );
-        assertThat( ( (TestFailedEvent) event ).getRunMode() ).isEqualTo( RERUN_TEST_AFTER_FAILURE );
+        assertThat( ( (TestFailedEvent) event ).getReportEntry().getRunMode() ).isEqualTo( RERUN_TEST_AFTER_FAILURE );
+        assertThat( ( (TestFailedEvent) event ).getReportEntry().getTestRunId() ).isEqualTo( 1L );
         assertThat( ( (TestFailedEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
         assertThat( ( (TestFailedEvent) event ).getReportEntry().getSourceText() ).isNull();
         assertThat( ( (TestFailedEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
@@ -474,10 +487,12 @@ public class EventDecoderTest
             .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 );
+        memento.getData().addAll( asList( NORMAL_RUN, 1L, "source", null, "name", null, null, null, 5, null, null,
+            "stackTrace" ) );
+        event = decoder.toMessage( BOOTERCODE_TEST_SKIPPED, memento );
         assertThat( event ).isInstanceOf( TestSkippedEvent.class );
-        assertThat( ( (TestSkippedEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestSkippedEvent) event ).getReportEntry().getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestSkippedEvent) event ).getReportEntry().getTestRunId() ).isEqualTo( 1L );
         assertThat( ( (TestSkippedEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
         assertThat( ( (TestSkippedEvent) event ).getReportEntry().getSourceText() ).isNull();
         assertThat( ( (TestSkippedEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
@@ -496,11 +511,12 @@ public class EventDecoderTest
             .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 );
+        memento.getData().addAll( asList( NORMAL_RUN, 1L, "source", null, "name", "nameText", null, null, 0, null, null,
+            "stackTrace" ) );
+        event = decoder.toMessage( BOOTERCODE_TEST_ERROR, memento );
         assertThat( event ).isInstanceOf( TestErrorEvent.class );
-        assertThat( ( (TestErrorEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestErrorEvent) event ).getReportEntry().getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestErrorEvent) event ).getReportEntry().getTestRunId() ).isEqualTo( 1L );
         assertThat( ( (TestErrorEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
         assertThat( ( (TestErrorEvent) event ).getReportEntry().getSourceText() ).isNull();
         assertThat( ( (TestErrorEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
@@ -519,10 +535,12 @@ public class EventDecoderTest
             .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 );
+        memento.getData().addAll( asList( NORMAL_RUN, 1L, "source", null, "name", null, "group", null, 5, null, null,
+            "stackTrace" ) );
+        event = decoder.toMessage( BOOTERCODE_TEST_ASSUMPTIONFAILURE, memento );
         assertThat( event ).isInstanceOf( TestAssumptionFailureEvent.class );
-        assertThat( ( (TestAssumptionFailureEvent) event ).getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getTestRunId() ).isEqualTo( 1L );
         assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getSourceName() ).isEqualTo( "source" );
         assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getSourceText() ).isNull();
         assertThat( ( (TestAssumptionFailureEvent) event ).getReportEntry().getName() ).isEqualTo( "name" );
@@ -544,8 +562,10 @@ public class EventDecoderTest
     @Test
     public void shouldRecognizeEmptyStream4ReportEntry()
     {
-        ReportEntry reportEntry = EventDecoder.newReportEntry( "", "", "", "", "", "", null, "", "", "" );
+        ReportEntry reportEntry = newReportEntry( NORMAL_RUN, 1L, "", "", "", "", "", "", null, "", "", "" );
         assertThat( reportEntry ).isNotNull();
+        assertThat( reportEntry.getRunMode() ).isEqualTo( NORMAL_RUN );
+        assertThat( reportEntry.getTestRunId() ).isEqualTo( 1L );
         assertThat( reportEntry.getStackTraceWriter() ).isNotNull();
         assertThat( reportEntry.getStackTraceWriter().smartTrimmedStackTrace() ).isEmpty();
         assertThat( reportEntry.getStackTraceWriter().writeTraceToString() ).isEmpty();
@@ -587,7 +607,7 @@ public class EventDecoderTest
         when( reportEntry.getSourceText() ).thenReturn( "test class display name" );
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
-        ReportEntry decodedReportEntry = EventDecoder.newReportEntry( reportEntry.getSourceName(),
+        ReportEntry decodedReportEntry = newReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(),
             reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
             reportEntry.getMessage(), null, null, null, null );
 
@@ -600,7 +620,7 @@ public class EventDecoderTest
         assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
         assertThat( decodedReportEntry.getStackTraceWriter() ).isNull();
 
-        decodedReportEntry = EventDecoder.newReportEntry( reportEntry.getSourceName(),
+        decodedReportEntry = newReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(),
             reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
             reportEntry.getMessage(), null, exceptionMessage, smartStackTrace, null );
 
@@ -620,7 +640,7 @@ public class EventDecoderTest
         assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() )
             .isNull();
 
-        decodedReportEntry = EventDecoder.newReportEntry( reportEntry.getSourceName(),
+        decodedReportEntry = newReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(),
             reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
             reportEntry.getMessage(), 1003, exceptionMessage, smartStackTrace, null );
 
@@ -640,7 +660,7 @@ public class EventDecoderTest
         assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() )
             .isNull();
 
-        decodedReportEntry = EventDecoder.newReportEntry( reportEntry.getSourceName(),
+        decodedReportEntry = newReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(),
             reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
             reportEntry.getMessage(), 1003, exceptionMessage, smartStackTrace, stackTrace );
 
@@ -661,7 +681,7 @@ public class EventDecoderTest
         assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() ).isEqualTo( stackTrace );
         assertThat( decodedReportEntry.getStackTraceWriter().writeTrimmedTraceToString() ).isEqualTo( stackTrace );
 
-        decodedReportEntry = EventDecoder.newReportEntry( reportEntry.getSourceName(),
+        decodedReportEntry = newReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(),
             reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(),
             reportEntry.getMessage(), 1003, exceptionMessage, smartStackTrace, trimmedStackTrace );
 
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkedProcessEventType.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkedProcessEventType.java
index 94da335..f82bb51 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkedProcessEventType.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkedProcessEventType.java
@@ -33,7 +33,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "sys-prop". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:sys-prop:RunMode:UTF-8:0xFFFFFFFF:key:0xFFFFFFFF:value:
+     * :maven-surefire-event:sys-prop:RunMode:0x0000000100000000:5:UTF-8:0xFFFFFFFF:key:0xFFFFFFFF:value:
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -45,7 +45,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "testset-starting". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:testset-starting:RunMode:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
+     * :maven-surefire-event:testset-starting:RunMode:0x0000000100000000:5:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -57,7 +57,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "testset-completed". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:testset-completed:RunMode:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
+     * :maven-surefire-event:testset-completed:RunMode:0x0000000100000000:5:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -69,7 +69,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "test-starting". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:test-starting:RunMode:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
+     * :maven-surefire-event:test-starting:RunMode:0x0000000100000001:5:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -81,7 +81,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "test-succeeded". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:test-succeeded:RunMode:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
+     * :maven-surefire-event:test-succeeded:RunMode:0x0000000100000001:5:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -93,7 +93,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "test-failed". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:test-failed:RunMode:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
+     * :maven-surefire-event:test-failed:RunMode:0x0000000100000001:5:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -105,7 +105,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "test-skipped". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:test-skipped:RunMode:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
+     * :maven-surefire-event:test-skipped:RunMode:0x0000000100000001:5:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -117,7 +117,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "test-error". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:test-error:RunMode:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
+     * :maven-surefire-event:test-error:RunMode:0x0000000100000001:5:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -129,7 +129,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "test-assumption-failure". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:test-assumption-failure:RunMode:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
+     * :maven-surefire-event:test-assumption-failure:RunMode:0x0000000100000001:5:UTF-8:0xFFFFFFFF:SourceName:0xFFFFFFFF:SourceText:0xFFFFFFFF:Name:0xFFFFFFFF:NameText:0xFFFFFFFF:Group:0xFFFFFFFF:Message:ElapsedTime (binary int):0xFFFFFFFF:LocalizedMessage:0xFFFFFFFF:SmartTrimmedStackTrace:0xFFFFFFFF:toStackTrace( stw, trimStackTraces ):
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -141,7 +141,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "std-out-stream". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:std-out-stream:RunMode:UTF-8:0xFFFFFFFF:line:
+     * :maven-surefire-event:std-out-stream:RunMode:0x0000000100000001:5:UTF-8:0xFFFFFFFF:line:
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -153,7 +153,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "std-out-stream-new-line". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:std-out-stream-new-line:RunMode:UTF-8:0xFFFFFFFF:line:
+     * :maven-surefire-event:std-out-stream-new-line:RunMode:0x0000000100000001:5:UTF-8:0xFFFFFFFF:line:
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -165,7 +165,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "std-err-stream". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:std-err-stream:RunMode:UTF-8:0xFFFFFFFF:line:
+     * :maven-surefire-event:std-err-stream:RunMode:0x0000000100000001:5:UTF-8:0xFFFFFFFF:line:
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -177,7 +177,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "std-err-stream-new-line". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:std-err-stream-new-line:RunMode:UTF-8:0xFFFFFFFF:line:
+     * :maven-surefire-event:std-err-stream-new-line:RunMode:0x0000000100000001:5:UTF-8:0xFFFFFFFF:line:
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -189,7 +189,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "console-info-log". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:console-info-log:RunMode:UTF-8:0xFFFFFFFF:line:
+     * :maven-surefire-event:console-info-log:RunMode:5:UTF-8:0xFFFFFFFF:line:
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -201,7 +201,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "console-debug-log". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:console-debug-log:RunMode:UTF-8:0xFFFFFFFF:line:
+     * :maven-surefire-event:console-debug-log:RunMode:5:UTF-8:0xFFFFFFFF:line:
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -213,7 +213,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "console-warning-log". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:console-warning-log:RunMode:UTF-8:0xFFFFFFFF:line:
+     * :maven-surefire-event:console-warning-log:RunMode:5:UTF-8:0xFFFFFFFF:line:
      * </pre>
      * The constructor with one argument:
      * <ul>
@@ -225,7 +225,7 @@ public enum ForkedProcessEventType
     /**
      * This is the opcode "console-error-log". The frame is composed of segments and the separator characters ':'
      * <pre>
-     * :maven-surefire-event:console-error-log:RunMode:UTF-8:0xFFFFFFFF:line:
+     * :maven-surefire-event:console-error-log:RunMode:5:UTF-8:0xFFFFFFFF:line:
      * </pre>
      * The constructor with one argument:
      * <ul>
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractStandardStreamEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractStandardStreamEvent.java
index bebb35e..4393ee3 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractStandardStreamEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractStandardStreamEvent.java
@@ -30,12 +30,15 @@ import org.apache.maven.surefire.api.report.RunMode;
 public abstract class AbstractStandardStreamEvent extends Event
 {
     private final RunMode runMode;
+    private final Long testRunId;
     private final String message;
 
-    protected AbstractStandardStreamEvent( ForkedProcessEventType eventType, RunMode runMode, String message )
+    protected AbstractStandardStreamEvent( ForkedProcessEventType eventType, RunMode runMode, Long testRunId,
+                                           String message )
     {
         super( eventType );
         this.runMode = runMode;
+        this.testRunId = testRunId;
         this.message = message;
     }
 
@@ -44,6 +47,11 @@ public abstract class AbstractStandardStreamEvent extends Event
         return runMode;
     }
 
+    public Long getTestRunId()
+    {
+        return testRunId;
+    }
+
     public String getMessage()
     {
         return message;
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractTestControlEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractTestControlEvent.java
index 747f65c..a43ed33 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractTestControlEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractTestControlEvent.java
@@ -21,7 +21,6 @@ package org.apache.maven.surefire.api.event;
 
 import org.apache.maven.surefire.api.booter.ForkedProcessEventType;
 import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 
 /**
  * The base class of an event of test control.
@@ -31,21 +30,14 @@ import org.apache.maven.surefire.api.report.RunMode;
  */
 public abstract class AbstractTestControlEvent<T extends ReportEntry> extends Event
 {
-    private final RunMode runMode;
     private final T reportEntry;
 
-    public AbstractTestControlEvent( ForkedProcessEventType eventType, RunMode runMode, T reportEntry )
+    public AbstractTestControlEvent( ForkedProcessEventType eventType, T reportEntry )
     {
         super( eventType );
-        this.runMode = runMode;
         this.reportEntry = reportEntry;
     }
 
-    public RunMode getRunMode()
-    {
-        return runMode;
-    }
-
     public T getReportEntry()
     {
         return reportEntry;
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrEvent.java
index 3367edf..7188b8b 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrEvent.java
@@ -30,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class StandardStreamErrEvent extends AbstractStandardStreamEvent
 {
-    public StandardStreamErrEvent( RunMode runMode, String message )
+    public StandardStreamErrEvent( RunMode runMode, Long testRunId, String message )
     {
-        super( BOOTERCODE_STDERR, runMode, message );
+        super( BOOTERCODE_STDERR, runMode, testRunId, message );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrWithNewLineEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrWithNewLineEvent.java
index 2974e5a..968dcb3 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrWithNewLineEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrWithNewLineEvent.java
@@ -30,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class StandardStreamErrWithNewLineEvent extends AbstractStandardStreamEvent
 {
-    public StandardStreamErrWithNewLineEvent( RunMode runMode, String message )
+    public StandardStreamErrWithNewLineEvent( RunMode runMode, Long testRunId, String message )
     {
-        super( BOOTERCODE_STDERR_NEW_LINE, runMode, message );
+        super( BOOTERCODE_STDERR_NEW_LINE, runMode, testRunId, message );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutEvent.java
index 9c77edd..1525de7 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutEvent.java
@@ -30,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class StandardStreamOutEvent extends AbstractStandardStreamEvent
 {
-    public StandardStreamOutEvent( RunMode runMode, String message )
+    public StandardStreamOutEvent( RunMode runMode, Long testRunId, String message )
     {
-        super( BOOTERCODE_STDOUT, runMode, message );
+        super( BOOTERCODE_STDOUT, runMode, testRunId, message );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutWithNewLineEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutWithNewLineEvent.java
index 34d9f8d..b566bf9 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutWithNewLineEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutWithNewLineEvent.java
@@ -30,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class StandardStreamOutWithNewLineEvent extends AbstractStandardStreamEvent
 {
-    public StandardStreamOutWithNewLineEvent( RunMode runMode, String message )
+    public StandardStreamOutWithNewLineEvent( RunMode runMode, Long testRunId, String message )
     {
-        super( BOOTERCODE_STDOUT_NEW_LINE, runMode, message );
+        super( BOOTERCODE_STDOUT_NEW_LINE, runMode, testRunId, message );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/SystemPropertyEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/SystemPropertyEvent.java
index a0ae1e4..0b293b5 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/SystemPropertyEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/SystemPropertyEvent.java
@@ -31,13 +31,15 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
 public final class SystemPropertyEvent extends Event
 {
     private final RunMode runMode;
+    private final Long testRunId;
     private final String key;
     private final String value;
 
-    public SystemPropertyEvent( RunMode runMode, String key, String value )
+    public SystemPropertyEvent( RunMode runMode, Long testRunId, String key, String value )
     {
         super( BOOTERCODE_SYSPROPS );
         this.runMode = runMode;
+        this.testRunId = testRunId;
         this.key = key;
         this.value = value;
     }
@@ -47,6 +49,11 @@ public final class SystemPropertyEvent extends Event
         return runMode;
     }
 
+    public Long getTestRunId()
+    {
+        return testRunId;
+    }
+
     public String getKey()
     {
         return key;
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestAssumptionFailureEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestAssumptionFailureEvent.java
index ee1b466..c702dc8 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestAssumptionFailureEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestAssumptionFailureEvent.java
@@ -20,7 +20,6 @@ package org.apache.maven.surefire.api.event;
  */
 
 import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 
 import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ASSUMPTIONFAILURE;
 
@@ -31,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class TestAssumptionFailureEvent extends AbstractTestControlEvent<ReportEntry>
 {
-    public TestAssumptionFailureEvent( RunMode runMode, ReportEntry reportEntry )
+    public TestAssumptionFailureEvent( ReportEntry reportEntry )
     {
-        super( BOOTERCODE_TEST_ASSUMPTIONFAILURE, runMode, reportEntry );
+        super( BOOTERCODE_TEST_ASSUMPTIONFAILURE, reportEntry );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestErrorEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestErrorEvent.java
index f85fc5d..a66f63c 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestErrorEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestErrorEvent.java
@@ -20,7 +20,6 @@ package org.apache.maven.surefire.api.event;
  */
 
 import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 
 import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ERROR;
 
@@ -31,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class TestErrorEvent extends AbstractTestControlEvent<ReportEntry>
 {
-    public TestErrorEvent( RunMode runMode, ReportEntry reportEntry )
+    public TestErrorEvent( ReportEntry reportEntry )
     {
-        super( BOOTERCODE_TEST_ERROR, runMode, reportEntry );
+        super( BOOTERCODE_TEST_ERROR, reportEntry );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestFailedEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestFailedEvent.java
index 30c8994..1905e07 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestFailedEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestFailedEvent.java
@@ -20,7 +20,6 @@ package org.apache.maven.surefire.api.event;
  */
 
 import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 
 import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_FAILED;
 
@@ -31,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class TestFailedEvent extends AbstractTestControlEvent<ReportEntry>
 {
-    public TestFailedEvent( RunMode runMode, ReportEntry reportEntry )
+    public TestFailedEvent( ReportEntry reportEntry )
     {
-        super( BOOTERCODE_TEST_FAILED, runMode, reportEntry );
+        super( BOOTERCODE_TEST_FAILED, reportEntry );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestSkippedEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestSkippedEvent.java
index 3ec406d..f33b528 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestSkippedEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestSkippedEvent.java
@@ -20,7 +20,6 @@ package org.apache.maven.surefire.api.event;
  */
 
 import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 
 import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SKIPPED;
 
@@ -31,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class TestSkippedEvent extends AbstractTestControlEvent<ReportEntry>
 {
-    public TestSkippedEvent( RunMode runMode, ReportEntry reportEntry )
+    public TestSkippedEvent( ReportEntry reportEntry )
     {
-        super( BOOTERCODE_TEST_SKIPPED, runMode, reportEntry );
+        super( BOOTERCODE_TEST_SKIPPED, reportEntry );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestStartingEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestStartingEvent.java
index 94818d0..9ef3e71 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestStartingEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestStartingEvent.java
@@ -20,7 +20,6 @@ package org.apache.maven.surefire.api.event;
  */
 
 import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 
 import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_STARTING;
 
@@ -31,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class TestStartingEvent extends AbstractTestControlEvent<ReportEntry>
 {
-    public TestStartingEvent( RunMode runMode, ReportEntry reportEntry )
+    public TestStartingEvent( ReportEntry reportEntry )
     {
-        super( BOOTERCODE_TEST_STARTING, runMode, reportEntry );
+        super( BOOTERCODE_TEST_STARTING, reportEntry );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestSucceededEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestSucceededEvent.java
index dd4005b..fad36eb 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestSucceededEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestSucceededEvent.java
@@ -20,7 +20,6 @@ package org.apache.maven.surefire.api.event;
  */
 
 import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 
 import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SUCCEEDED;
 
@@ -31,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class TestSucceededEvent extends AbstractTestControlEvent<ReportEntry>
 {
-    public TestSucceededEvent( RunMode runMode, ReportEntry reportEntry )
+    public TestSucceededEvent( ReportEntry reportEntry )
     {
-        super( BOOTERCODE_TEST_SUCCEEDED, runMode, reportEntry );
+        super( BOOTERCODE_TEST_SUCCEEDED, reportEntry );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestsetCompletedEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestsetCompletedEvent.java
index e20c44c..2d82a39 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestsetCompletedEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestsetCompletedEvent.java
@@ -19,7 +19,6 @@ package org.apache.maven.surefire.api.event;
  * under the License.
  */
 
-import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 
 import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_COMPLETED;
@@ -31,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class TestsetCompletedEvent extends AbstractTestControlEvent<TestSetReportEntry>
 {
-    public TestsetCompletedEvent( RunMode runMode, TestSetReportEntry reportEntry )
+    public TestsetCompletedEvent( TestSetReportEntry reportEntry )
     {
-        super( BOOTERCODE_TESTSET_COMPLETED, runMode, reportEntry );
+        super( BOOTERCODE_TESTSET_COMPLETED, reportEntry );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestsetStartingEvent.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestsetStartingEvent.java
index 4faf3ac..5518f0d 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestsetStartingEvent.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/TestsetStartingEvent.java
@@ -19,7 +19,6 @@ package org.apache.maven.surefire.api.event;
  * under the License.
  */
 
-import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 
 import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_STARTING;
@@ -31,8 +30,8 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
  */
 public final class TestsetStartingEvent extends AbstractTestControlEvent<TestSetReportEntry>
 {
-    public TestsetStartingEvent( RunMode runMode, TestSetReportEntry reportEntry )
+    public TestsetStartingEvent( TestSetReportEntry reportEntry )
     {
-        super( BOOTERCODE_TESTSET_STARTING, runMode, reportEntry );
+        super( BOOTERCODE_TESTSET_STARTING, reportEntry );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java
index de197d5..facf30b 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java
@@ -21,7 +21,6 @@ package org.apache.maven.surefire.api.stream;
 
 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;
@@ -73,6 +72,7 @@ public abstract class AbstractStreamDecoder<M, MT extends Enum<MT>, ST extends E
     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 LONG_LENGTH = 8;
 
     private final ReadableByteChannel channel;
     private final ForkNodeArguments arguments;
@@ -98,7 +98,7 @@ public abstract class AbstractStreamDecoder<M, MT extends Enum<MT>, ST extends E
     protected abstract ST[] nextSegmentType( @Nonnull MT messageType );
 
     @Nonnull
-    protected abstract M toMessage( @Nonnull MT messageType, RunMode runMode, @Nonnull Memento memento )
+    protected abstract M toMessage( @Nonnull MT messageType, @Nonnull Memento memento )
         throws MalformedFrameException;
 
     @Nonnull
@@ -229,6 +229,27 @@ public abstract class AbstractStreamDecoder<M, MT extends Enum<MT>, ST extends E
         return i;
     }
 
+    protected Long readLong( @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 readLongPrivate( memento );
+    }
+
+    protected long readLongPrivate( @Nonnull Memento memento ) throws IOException, MalformedFrameException
+    {
+        read( memento, LONG_LENGTH + DELIMITER_LENGTH );
+        long num = memento.getByteBuffer().getLong();
+        checkDelimiter( memento );
+        return num;
+    }
+
     @SuppressWarnings( "checkstyle:magicnumber" )
     protected final void checkDelimiter( Memento memento ) throws MalformedFrameException
     {
@@ -249,10 +270,11 @@ public abstract class AbstractStreamDecoder<M, MT extends Enum<MT>, ST extends E
         try
         {
             byte[] header = getEncodedMagicNumber();
+            byte[] bbArray = bb.array();
             for ( int start = bb.arrayOffset() + ( (Buffer) bb ).position(), length = header.length;
                   shift < length; shift++ )
             {
-                if ( bb.array()[shift + start] != header[shift] )
+                if ( bbArray[shift + start] != header[shift] )
                 {
                     throw new MalformedFrameException( memento.getLine().getPositionByteBuffer(),
                         ( (Buffer) bb ).position() + shift );
@@ -267,17 +289,6 @@ public abstract class AbstractStreamDecoder<M, MT extends Enum<MT>, ST extends E
         checkDelimiter( memento );
     }
 
-    protected void checkArguments( RunMode runMode, Memento memento, int expectedDataElements )
-        throws MalformedFrameException
-    {
-        if ( runMode == null )
-        {
-            throw new MalformedFrameException( memento.getLine().getPositionByteBuffer(),
-                ( (Buffer) memento.getByteBuffer() ).position() );
-        }
-        checkArguments( memento, expectedDataElements );
-    }
-
     protected void checkArguments( Memento memento, int expectedDataElements )
         throws MalformedFrameException
     {
@@ -555,6 +566,12 @@ public abstract class AbstractStreamDecoder<M, MT extends Enum<MT>, ST extends E
             return data;
         }
 
+        public <T> T ofDataAt( int indexOfData )
+        {
+            //noinspection unchecked
+            return (T) data.get( indexOfData );
+        }
+
         public CharBuffer getCharBuffer()
         {
             return cb;
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
index 758c1f9..9d3b765 100644
--- 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
@@ -79,7 +79,25 @@ public abstract class AbstractStreamEncoder<E extends Enum<E>>
         }
     }
 
-    public void encodeHeader( ByteBuffer result, E operation, RunMode runMode )
+    public void encodeHeader( ByteBuffer result, E operation, RunMode runMode, Long testRunId )
+    {
+        encodeHeader( result, operation );
+
+        byte[] runmode = runMode == null ? new byte[0] : runMode.getRunmodeBinary();
+        result.put( (byte) runmode.length );
+        result.put( (byte) ':' );
+        result.put( runmode );
+        result.put( (byte) ':' );
+
+        result.put( (byte) ( testRunId == null ? 0 : 1 ) );
+        if ( testRunId != null )
+        {
+            result.putLong( testRunId );
+        }
+        result.put( (byte) ':' );
+    }
+
+    public void encodeHeader( ByteBuffer result, E operation )
     {
         result.put( (byte) ':' );
         result.put( getEncodedMagicNumber() );
@@ -89,15 +107,6 @@ public abstract class AbstractStreamEncoder<E extends Enum<E>>
         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 )
@@ -141,9 +150,21 @@ public abstract class AbstractStreamEncoder<E extends Enum<E>>
         result.put( (byte) ':' );
     }
 
-    public void encode( CharsetEncoder encoder, ByteBuffer result, E operation, RunMode runMode, String... messages )
+    public void encode( CharsetEncoder encoder, ByteBuffer result, E operation, RunMode runMode, Long testRunId,
+                        String... messages )
+    {
+        encodeHeader( result, operation, runMode, testRunId );
+        encodeStringData( result, encoder, messages );
+    }
+
+    public void encode( CharsetEncoder encoder, ByteBuffer result, E operation, String... messages )
+    {
+        encodeHeader( result, operation );
+        encodeStringData( result, encoder, messages );
+    }
+
+    private void encodeStringData( ByteBuffer result, CharsetEncoder encoder, String... messages )
     {
-        encodeHeader( result, operation, runMode );
         encodeCharset( result );
         for ( String message : messages )
         {
@@ -152,7 +173,7 @@ public abstract class AbstractStreamEncoder<E extends Enum<E>>
     }
 
     public int estimateBufferLength( int opcodeLength, RunMode runMode, CharsetEncoder encoder,
-                                     int integersCounter, String... strings )
+                                     int integersCounter, int longsCounter, String... strings )
     {
         assert !( encoder == null && strings.length != 0 );
 
@@ -173,12 +194,15 @@ public abstract class AbstractStreamEncoder<E extends Enum<E>>
         }
 
         // one byte (0x00 if NULL) + 4 bytes for integer + one delimiter character ':'
-        int lengthOfData = ( 1 + 1 + 4 ) * integersCounter;
+        int lengthOfData = ( 1 + 4 + 1 ) * integersCounter;
+
+        // one byte (0x00 if NULL) + 8 bytes for long + one delimiter character ':'
+        lengthOfData += ( 1 + 8 + 1 ) * longsCounter;
 
         for ( String string : strings )
         {
             String s = nonNull( string );
-            // 4 bytes of string length + one delimiter character ':' + <string> + one delimiter character ':'
+            // 4 bytes of length of the string + one delimiter character ':' + <string> + one delimiter character ':'
             lengthOfData += 4 + 1 + (int) ceil( encoder.maxBytesPerChar() * s.length() ) + 1;
         }
 
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/SegmentType.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/SegmentType.java
index f81d2ce..97932f1 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/SegmentType.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/SegmentType.java
@@ -25,6 +25,7 @@ package org.apache.maven.surefire.api.stream;
 public enum SegmentType
 {
     RUN_MODE,
+    TEST_RUN_ID,
     STRING_ENCODING,
     DATA_STRING,
     DATA_INTEGER,
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java
index 2dd9c72..2ff06ea 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java
@@ -39,7 +39,6 @@ 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;
@@ -686,9 +685,7 @@ public class AbstractStreamDecoderTest
 
         @Nonnull
         @Override
-        protected Event toMessage(
-            @Nonnull ForkedProcessEventType messageType, RunMode runMode,
-            @Nonnull Memento memento ) throws MalformedFrameException
+        protected Event toMessage( @Nonnull ForkedProcessEventType messageType, @Nonnull Memento memento )
         {
             return null;
         }
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
index bec5a58..a94ce76 100644
--- 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
@@ -69,109 +69,109 @@ public class AbstractStreamEncoderTest
         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:
+        // :maven-surefire-event:8:sys-prop:10:normal-run:1:5:UTF-8:0003:kkk:0003:vvv:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_SYSPROPS.getOpcodeBinary().length, NORMAL_RUN,
-            encoder, 0, "k", "v" ) )
-            .isEqualTo( 72 );
+            encoder, 0, 1, "k", "v" ) )
+            .isEqualTo( 82 );
 
-        // :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:
+        // :maven-surefire-event:16:testset-starting:10:normal-run:1:5:UTF-8:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:X0003:0003:sss:0003:sss:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TESTSET_STARTING.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
-            .isEqualTo( 149 );
+            NORMAL_RUN, encoder, 1, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+            .isEqualTo( 159 );
 
-        // :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:
+        // :maven-surefire-event:17:testset-completed:10:normal-run:1:5:UTF-8:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:X0003:0003:sss:0003:sss:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TESTSET_COMPLETED.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
-            .isEqualTo( 150 );
+            NORMAL_RUN, encoder, 1, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+            .isEqualTo( 160 );
 
-        // :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:
+        // :maven-surefire-event:13:test-starting:10:normal-run:1:5:UTF-8:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:X0003:0003:sss:0003:sss:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_STARTING.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
-            .isEqualTo( 146 );
+            NORMAL_RUN, encoder, 1, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+            .isEqualTo( 156 );
 
-        // :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:
+        // :maven-surefire-event:14:test-succeeded:10:normal-run:1:5:UTF-8:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:X0003:0003:sss:0003:sss:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_SUCCEEDED.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
-            .isEqualTo( 147 );
+            NORMAL_RUN, encoder, 1, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+            .isEqualTo( 157 );
 
-        // :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:
+        // :maven-surefire-event:11:test-failed:10:normal-run:1:5:UTF-8:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:X0003:0003:sss:0003:sss:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_FAILED.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
-            .isEqualTo( 144 );
+            NORMAL_RUN, encoder, 1, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+            .isEqualTo( 154 );
 
-        // :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:
+        // :maven-surefire-event:12:test-skipped:10:normal-run:1:5:UTF-8:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:X0003:0003:sss:0003:sss:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_SKIPPED.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
-            .isEqualTo( 145 );
+            NORMAL_RUN, encoder, 1, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+            .isEqualTo( 155 );
 
-        // :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:
+        // :maven-surefire-event:10:test-error:10:normal-run:1:5:UTF-8:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:X0003:0003:sss:0003:sss:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_ERROR.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
-            .isEqualTo( 143 );
+            NORMAL_RUN, encoder, 1, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+            .isEqualTo( 153 );
 
-        // :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:
+        // :maven-surefire-event:23:test-assumption-failure:10:normal-run:1:5:UTF-8:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:0003:sss:X0003:0003:sss:0003:sss:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_TEST_ASSUMPTIONFAILURE.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
-            .isEqualTo( 156 );
+            NORMAL_RUN, encoder, 1, 1, "s", "s", "s", "s", "s", "s", "s", "s", "s" ) )
+            .isEqualTo( 166 );
 
-        // :maven-surefire-event:14:std-out-stream:10:normal-run:5:UTF-8:0001:sss:
+        // :maven-surefire-event:14:std-out-stream:10:normal-run:1:5:UTF-8:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_STDOUT.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 0, "s" ) )
-            .isEqualTo( 69 );
+            NORMAL_RUN, encoder, 0, 1, "s" ) )
+            .isEqualTo( 79 );
 
-        // :maven-surefire-event:23:std-out-stream-new-line:10:normal-run:5:UTF-8:0001:sss:
+        // :maven-surefire-event:23:std-out-stream-new-line:10:normal-run:1:5:UTF-8:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_STDOUT_NEW_LINE.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 0, "s" ) )
-            .isEqualTo( 78 );
+            NORMAL_RUN, encoder, 0, 1, "s" ) )
+            .isEqualTo( 88 );
 
-        // :maven-surefire-event:14:std-err-stream:10:normal-run:5:UTF-8:0001:sss:
+        // :maven-surefire-event:14:std-err-stream:10:normal-run:1:5:UTF-8:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_STDERR.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 0, "s" ) )
-            .isEqualTo( 69 );
+            NORMAL_RUN, encoder, 0, 1, "s" ) )
+            .isEqualTo( 79 );
 
-        // :maven-surefire-event:23:std-err-stream-new-line:10:normal-run:5:UTF-8:0001:sss:
+        // :maven-surefire-event:23:std-err-stream-new-line:10:normal-run:1:5:UTF-8:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_STDERR_NEW_LINE.getOpcodeBinary().length,
-            NORMAL_RUN, encoder, 0, "s" ) )
-            .isEqualTo( 78 );
+            NORMAL_RUN, encoder, 0, 1, "s" ) )
+            .isEqualTo( 88 );
 
-        // :maven-surefire-event:16:console-info-log:5:UTF-8:0001:sss:
+        // :maven-surefire-event:16:console-info-log:5:UTF-8:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_CONSOLE_INFO.getOpcodeBinary().length,
-            null, encoder, 0, "s" ) )
+            null, encoder, 0, 0, "s" ) )
             .isEqualTo( 58 );
 
-        // :maven-surefire-event:17:console-debug-log:5:UTF-8:0001:sss:
+        // :maven-surefire-event:17:console-debug-log:5:UTF-8:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_CONSOLE_DEBUG.getOpcodeBinary().length,
-            null, encoder, 0, "s" ) )
+            null, encoder, 0, 0, "s" ) )
             .isEqualTo( 59 );
 
-        // :maven-surefire-event:19:console-warning-log:5:UTF-8:0001:sss:
+        // :maven-surefire-event:19:console-warning-log:5:UTF-8:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_CONSOLE_WARNING.getOpcodeBinary().length,
-            null, encoder, 0, "s" ) )
+            null, encoder, 0, 0, "s" ) )
             .isEqualTo( 61 );
 
-        // :maven-surefire-event:17:console-error-log:5:UTF-8:0001:sss:
+        // :maven-surefire-event:17:console-error-log:5:UTF-8:0003:sss:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_CONSOLE_ERROR.getOpcodeBinary().length,
-            null, encoder, 0, "s" ) )
+            null, encoder, 0, 0, "s" ) )
             .isEqualTo( 59 );
 
         // :maven-surefire-event:3:bye:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_BYE.getOpcodeBinary().length,
-            null, null, 0 ) )
+            null, null, 0, 0 ) )
             .isEqualTo( 28 );
 
         // :maven-surefire-event:17:stop-on-next-test:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_STOP_ON_NEXT_TEST.getOpcodeBinary().length,
-            null, null, 0 ) )
+            null, null, 0, 0 ) )
             .isEqualTo( 42 );
 
         // :maven-surefire-event:9:next-test:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_NEXT_TEST.getOpcodeBinary().length,
-            null, null, 0 ) )
+            null, null, 0, 0 ) )
             .isEqualTo( 34 );
 
         // :maven-surefire-event:14:jvm-exit-error:
         assertThat( streamEncoder.estimateBufferLength( BOOTERCODE_JVM_EXIT_ERROR.getOpcodeBinary().length,
-            null, null, 0 ) )
+            null, null, 0, 0 ) )
             .isEqualTo( 39 );
     }
 
@@ -180,12 +180,13 @@ public class AbstractStreamEncoderTest
     {
         Encoder streamEncoder = new Encoder( new DummyChannel() );
         ByteBuffer result = ByteBuffer.allocate( 128 );
-        streamEncoder.encodeHeader( result, BOOTERCODE_TEST_ERROR, NORMAL_RUN );
+        streamEncoder.encodeHeader( result, BOOTERCODE_TEST_ERROR, NORMAL_RUN, 1L );
         assertThat( toString( result ) )
-            .isEqualTo( ":maven-surefire-event:" + (char) 10 + ":test-error:" + (char) 10 + ":normal-run:" );
+            .isEqualTo( ":maven-surefire-event:" + (char) 10 + ":test-error:" + (char) 10
+                + ":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:" );
 
         result = ByteBuffer.allocate( 1024 );
-        streamEncoder.encodeHeader( result, BOOTERCODE_CONSOLE_ERROR, null );
+        streamEncoder.encodeHeader( result, BOOTERCODE_CONSOLE_ERROR );
         streamEncoder.encodeCharset( result );
         assertThat( toString( result ) )
             .isEqualTo( ":maven-surefire-event:" + (char) 17 + ":console-error-log:" + (char) 5 + ":UTF-8:" );
@@ -196,10 +197,11 @@ public class AbstractStreamEncoderTest
     {
         Encoder streamEncoder = new Encoder( new DummyChannel() );
         ByteBuffer result = ByteBuffer.allocate( 128 );
-        streamEncoder.encode( streamEncoder.newCharsetEncoder(), result, BOOTERCODE_STDOUT, NORMAL_RUN, null, "msg" );
+        streamEncoder.encode( streamEncoder.newCharsetEncoder(), result, BOOTERCODE_STDOUT, NORMAL_RUN, 1L, "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:" );
+                + (char) 10 + ":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001"
+                + ":\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:" );
     }
 
     @Test
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/EventChannelEncoder.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/EventChannelEncoder.java
index b3fc7d7..b4a234d 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/EventChannelEncoder.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/EventChannelEncoder.java
@@ -43,7 +43,6 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import static java.util.Objects.requireNonNull;
 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;
@@ -65,7 +64,6 @@ import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTER
 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;
 
 /**
  * magic number : opcode : run mode [: opcode specific data]*
@@ -77,45 +75,20 @@ import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 @SuppressWarnings( "checkstyle:linelength" )
 public class EventChannelEncoder extends EventEncoder implements MasterProcessChannelEncoder
 {
-    private final RunMode runMode;
     private final AtomicBoolean trouble = new AtomicBoolean();
     private volatile boolean onExit;
 
     /**
-     * The encoder for events and normal test mode.
+     * The encoder for events.
      *
      * @param out     the channel available for writing the events
      */
     public EventChannelEncoder( @Nonnull WritableBufferedByteChannel out )
     {
-        this( out, NORMAL_RUN );
-    }
-
-    /**
-     * 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 )
-    {
         super( out );
-        this.runMode = requireNonNull( runMode );
-    }
-
-    /*@Override
-    public MasterProcessChannelEncoder asRerunMode() // todo apply this and rework providers
-    {
-        return new EventChannelEncoder( streamEncoder, RERUN_TEST_AFTER_FAILURE );
     }
 
     @Override
-    public MasterProcessChannelEncoder asNormalMode()
-    {
-        return new EventChannelEncoder( streamEncoder, NORMAL_RUN );
-    }*/
-
-    @Override
     public boolean checkError()
     {
         return trouble.get();
@@ -128,7 +101,7 @@ public class EventChannelEncoder extends EventEncoder implements MasterProcessCh
         write( ByteBuffer.wrap( new byte[] {'\n'} ), true );
     }
 
-    private void encodeSystemProperties( Map<String, String> sysProps, RunMode rm, Long testRunId )
+    void encodeSystemProperties( Map<String, String> sysProps, RunMode runMode, Long testRunId )
     {
         CharsetEncoder encoder = newCharsetEncoder();
         ByteBuffer result = null;
@@ -139,11 +112,11 @@ public class EventChannelEncoder extends EventEncoder implements MasterProcessCh
             String value = entry.getValue();
 
             int bufferLength =
-                estimateBufferLength( BOOTERCODE_SYSPROPS.getOpcode().length(), runMode, encoder, 0, key, value );
+                estimateBufferLength( BOOTERCODE_SYSPROPS.getOpcode().length(), runMode, encoder, 0, 1, key, value );
             result = result != null && result.capacity() >= bufferLength ? result : ByteBuffer.allocate( bufferLength );
             ( (Buffer) result ).clear();
-            // :maven-surefire-event:sys-prop:rerun-test-after-failure:UTF-8:<integer>:<key>:<integer>:<value>:
-            encode( encoder, result, BOOTERCODE_SYSPROPS, runMode, key, value );
+            // :maven-surefire-event:sys-prop:<runMode>:<testRunId>:UTF-8:<integer>:<key>:<integer>:<value>:
+            encode( encoder, result, BOOTERCODE_SYSPROPS, runMode, testRunId, key, value );
             boolean sync = !it.hasNext();
             write( result, sync );
         }
@@ -152,50 +125,50 @@ public class EventChannelEncoder extends EventEncoder implements MasterProcessCh
     @Override
     public void testSetStarting( TestSetReportEntry reportEntry, boolean trimStackTraces )
     {
-        encode( BOOTERCODE_TESTSET_STARTING, runMode, reportEntry, trimStackTraces, true );
+        encode( BOOTERCODE_TESTSET_STARTING, reportEntry, trimStackTraces, true );
     }
 
     @Override
     public void testSetCompleted( TestSetReportEntry reportEntry, boolean trimStackTraces )
     {
-        encodeSystemProperties( reportEntry.getSystemProperties(), null, null ); // todo in next commit
-        encode( BOOTERCODE_TESTSET_COMPLETED, runMode, reportEntry, trimStackTraces, true );
+        encodeSystemProperties( reportEntry.getSystemProperties(), reportEntry.getRunMode(), reportEntry.getTestRunId() );
+        encode( BOOTERCODE_TESTSET_COMPLETED, reportEntry, trimStackTraces, true );
     }
 
     @Override
     public void testStarting( ReportEntry reportEntry, boolean trimStackTraces )
     {
-        encode( BOOTERCODE_TEST_STARTING, runMode, reportEntry, trimStackTraces, true );
+        encode( BOOTERCODE_TEST_STARTING, reportEntry, trimStackTraces, true );
     }
 
     @Override
     public void testSucceeded( ReportEntry reportEntry, boolean trimStackTraces )
     {
-        encode( BOOTERCODE_TEST_SUCCEEDED, runMode, reportEntry, trimStackTraces, true );
+        encode( BOOTERCODE_TEST_SUCCEEDED, reportEntry, trimStackTraces, true );
     }
 
     @Override
     public void testFailed( ReportEntry reportEntry, boolean trimStackTraces )
     {
-        encode( BOOTERCODE_TEST_FAILED, runMode, reportEntry, trimStackTraces, true );
+        encode( BOOTERCODE_TEST_FAILED, reportEntry, trimStackTraces, true );
     }
 
     @Override
     public void testSkipped( ReportEntry reportEntry, boolean trimStackTraces )
     {
-        encode( BOOTERCODE_TEST_SKIPPED, runMode, reportEntry, trimStackTraces, true );
+        encode( BOOTERCODE_TEST_SKIPPED, reportEntry, trimStackTraces, true );
     }
 
     @Override
     public void testError( ReportEntry reportEntry, boolean trimStackTraces )
     {
-        encode( BOOTERCODE_TEST_ERROR, runMode, reportEntry, trimStackTraces, true );
+        encode( BOOTERCODE_TEST_ERROR, reportEntry, trimStackTraces, true );
     }
 
     @Override
     public void testAssumptionFailure( ReportEntry reportEntry, boolean trimStackTraces )
     {
-        encode( BOOTERCODE_TEST_ASSUMPTIONFAILURE, runMode, reportEntry, trimStackTraces, true );
+        encode( BOOTERCODE_TEST_ASSUMPTIONFAILURE, reportEntry, trimStackTraces, true );
     }
 
     @Override
@@ -207,19 +180,19 @@ public class EventChannelEncoder extends EventEncoder implements MasterProcessCh
         ForkedProcessEventType event =
             stdout ? ( newLine ? BOOTERCODE_STDOUT_NEW_LINE : BOOTERCODE_STDOUT )
                 : ( newLine ? BOOTERCODE_STDERR_NEW_LINE : BOOTERCODE_STDERR );
-        setOutErr( event, msg ); /*todo*/
+        setOutErr( event, reportEntry.getRunMode(), reportEntry.getTestRunId(), msg );
     }
 
-    private void setOutErr( ForkedProcessEventType eventType, String message )
+    private void setOutErr( ForkedProcessEventType eventType, RunMode runMode, Long testRunId, String message )
     {
-        ByteBuffer result = encodeMessage( eventType, runMode, message );
+        ByteBuffer result = encodeMessage( eventType, runMode, testRunId, message );
         write( result, false );
     }
 
     @Override
     public void consoleInfoLog( String message )
     {
-        ByteBuffer result = encodeMessage( BOOTERCODE_CONSOLE_INFO, null, message );
+        ByteBuffer result = encodeMessage( BOOTERCODE_CONSOLE_INFO, message );
         write( result, true );
     }
 
@@ -240,10 +213,10 @@ public class EventChannelEncoder extends EventEncoder implements MasterProcessCh
     {
         CharsetEncoder encoder = newCharsetEncoder();
         String stackTrace = t == null ? null : ConsoleLoggerUtils.toString( t );
-        int bufferMaxLength = estimateBufferLength( BOOTERCODE_CONSOLE_ERROR.getOpcode().length(), null, encoder, 0,
+        int bufferMaxLength = estimateBufferLength( BOOTERCODE_CONSOLE_ERROR.getOpcode().length(), null, encoder, 0, 0,
             message, stackTrace );
         ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
-        encodeHeader( result, BOOTERCODE_CONSOLE_ERROR, null );
+        encodeHeader( result, BOOTERCODE_CONSOLE_ERROR );
         encodeCharset( result );
         encode( encoder, result, message, null, stackTrace );
         write( result, true );
@@ -258,14 +231,14 @@ public class EventChannelEncoder extends EventEncoder implements MasterProcessCh
     @Override
     public void consoleDebugLog( String message )
     {
-        ByteBuffer result = encodeMessage( BOOTERCODE_CONSOLE_DEBUG, null, message );
+        ByteBuffer result = encodeMessage( BOOTERCODE_CONSOLE_DEBUG, message );
         write( result, true );
     }
 
     @Override
     public void consoleWarningLog( String message )
     {
-        ByteBuffer result = encodeMessage( BOOTERCODE_CONSOLE_WARNING, null, message );
+        ByteBuffer result = encodeMessage( BOOTERCODE_CONSOLE_WARNING, message );
         write( result, true );
     }
 
@@ -298,30 +271,30 @@ public class EventChannelEncoder extends EventEncoder implements MasterProcessCh
     {
         CharsetEncoder encoder = newCharsetEncoder();
         StackTrace stackTraceWrapper = new StackTrace( stackTraceWriter, trimStackTraces );
-        int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), null, encoder, 0,
+        int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), null, encoder, 0, 0,
             stackTraceWrapper.message, stackTraceWrapper.smartTrimmedStackTrace, stackTraceWrapper.stackTrace );
         ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
 
-        encodeHeader( result, eventType, null );
+        encodeHeader( result, eventType );
         encodeCharset( result );
         encode( encoder, result, stackTraceWrapper );
         write( result, sync );
     }
 
     // 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,
+    // :maven-surefire-event:testset-starting:rerun-test-after-failure:1:5: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, ReportEntry reportEntry,
                          boolean trimStackTraces, @SuppressWarnings( "SameParameterValue" ) boolean sync )
     {
-        ByteBuffer result = encode( operation, runMode, reportEntry, trimStackTraces );
+        ByteBuffer result = encode( operation, reportEntry, trimStackTraces );
         write( result, sync );
     }
 
     private void encodeOpcode( ForkedProcessEventType eventType, boolean sync )
     {
-        int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), null, null, 0 );
+        int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), null, null, 0, 0 );
         ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
-        encodeHeader( result, eventType, null );
+        encodeHeader( result, eventType );
         write( result, sync );
     }
 
@@ -387,21 +360,20 @@ public class EventChannelEncoder extends EventEncoder implements MasterProcessCh
      * <li>{@link ForkedProcessEventType#BOOTERCODE_TEST_ASSUMPTIONFAILURE}.</li>
      * </ul>
      */
-    ByteBuffer encode( ForkedProcessEventType operation, RunMode runMode, ReportEntry reportEntry,
-                               boolean trimStackTraces )
+    ByteBuffer encode( ForkedProcessEventType operation, ReportEntry reportEntry, boolean trimStackTraces )
     {
         StackTrace stackTraceWrapper = new StackTrace( reportEntry.getStackTraceWriter(), trimStackTraces );
 
         CharsetEncoder encoder = newCharsetEncoder();
 
-        int bufferMaxLength = estimateBufferLength( operation.getOpcode().length(), runMode, encoder, 1,
-            reportEntry.getSourceName(), reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(),
-            reportEntry.getGroup(), reportEntry.getMessage(), stackTraceWrapper.message,
+        int bufferMaxLength = estimateBufferLength( operation.getOpcode().length(), reportEntry.getRunMode(), encoder,
+            1, 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( result, operation, runMode );
+        encodeHeader( result, operation, reportEntry.getRunMode(), reportEntry.getTestRunId() );
         encodeCharset( result );
 
         encodeString( encoder, result, reportEntry.getSourceName() );
@@ -417,12 +389,22 @@ public class EventChannelEncoder extends EventEncoder implements MasterProcessCh
         return result;
     }
 
-    ByteBuffer encodeMessage( ForkedProcessEventType eventType, RunMode runMode, String message )
+    ByteBuffer encodeMessage( ForkedProcessEventType eventType, RunMode runMode, Long testRunId, String message )
+    {
+        CharsetEncoder encoder = newCharsetEncoder();
+        int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), runMode, encoder, 0,
+            testRunId == null ? 0 : 1, message );
+        ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
+        encode( encoder, result, eventType, runMode, testRunId, message );
+        return result;
+    }
+
+    ByteBuffer encodeMessage( ForkedProcessEventType eventType, String message )
     {
         CharsetEncoder encoder = newCharsetEncoder();
-        int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), runMode, encoder, 0, message );
+        int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), null, encoder, 0, 0, message );
         ByteBuffer result = ByteBuffer.allocate( bufferMaxLength );
-        encode( encoder, result, eventType, runMode, message );
+        encode( encoder, result, eventType, message );
         return result;
     }
 
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/CommandDecoder.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/CommandDecoder.java
index 76148b0..4a7e257 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/CommandDecoder.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/stream/CommandDecoder.java
@@ -23,7 +23,6 @@ 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;
@@ -46,10 +45,8 @@ 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;
 import static org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils.addShutDownHook;
 
@@ -65,13 +62,6 @@ public class CommandDecoder extends AbstractStreamDecoder<Command, MasterProcess
         END_OF_FRAME
     };
 
-    private static final SegmentType[] COMMAND_WITH_RUNNABLE_STRING = new SegmentType[] {
-        RUN_MODE,
-        STRING_ENCODING,
-        DATA_STRING,
-        END_OF_FRAME
-    };
-
     private static final SegmentType[] COMMAND_WITH_ONE_STRING = new SegmentType[] {
         STRING_ENCODING,
         DATA_STRING,
@@ -100,14 +90,11 @@ public class CommandDecoder extends AbstractStreamDecoder<Command, MasterProcess
                 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;
@@ -120,7 +107,7 @@ public class CommandDecoder extends AbstractStreamDecoder<Command, MasterProcess
                     case END_OF_FRAME:
                         memento.getLine().setPositionByteBuffer( memento.getByteBuffer().position() );
                         memento.getLine().clear();
-                        return toMessage( commandType, runMode, memento );
+                        return toMessage( commandType, memento );
                     default:
                         memento.getLine().setPositionByteBuffer( NO_POSITION );
                         arguments.dumpStreamText( "Unknown enum ("
@@ -179,7 +166,6 @@ public class CommandDecoder extends AbstractStreamDecoder<Command, MasterProcess
             case TEST_SET_FINISHED:
                 return COMMAND_WITHOUT_DATA;
             case RUN_CLASS:
-                return COMMAND_WITH_RUNNABLE_STRING;
             case SHUTDOWN:
                 return COMMAND_WITH_ONE_STRING;
             default:
@@ -189,7 +175,7 @@ public class CommandDecoder extends AbstractStreamDecoder<Command, MasterProcess
 
     @Nonnull
     @Override
-    protected Command toMessage( @Nonnull MasterProcessCommand commandType, RunMode runMode, @Nonnull Memento memento )
+    protected Command toMessage( @Nonnull MasterProcessCommand commandType, @Nonnull Memento memento )
         throws MalformedFrameException
     {
         switch ( commandType )
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 0f310c9..7fecd46 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
@@ -259,8 +259,6 @@ public class CommandReaderTest
             .append( ":maven-surefire-command:" )
             .append( (char) 13 )
             .append( ":run-testclass:" )
-            .append( (char) 10 )
-            .append( ":normal-run:" )
             .append( (char) 5 )
             .append( ":UTF-8:" )
             .append( (char) ( clsLength >> 24 ) )
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java
index 7307460..d6c6776 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java
@@ -89,8 +89,6 @@ public class CommandChannelDecoderTest
             .append( ":maven-surefire-command:" )
             .append( (char) 13 )
             .append( ":run-testclass:" )
-            .append( (char) 10 )
-            .append( ":normal-run:" )
             .append( (char) 5 )
             .append( ":UTF-8:" )
             .append( (char) 0 )
@@ -312,6 +310,7 @@ public class CommandChannelDecoderTest
     public void testBinaryCommandStream() throws Exception
     {
         InputStream commands = getClass().getResourceAsStream( "/binary-commands/75171711-encoder.bin" );
+        assertThat( commands ).isNotNull();
         ConsoleLoggerMock logger = new ConsoleLoggerMock( true, true, true, true );
         ForkNodeArguments args = new ForkNodeArgumentsMock( logger, new File( "" ) );
         CommandChannelDecoder decoder = new CommandChannelDecoder( newChannel( commands ), args );
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java
index 15965e4..5b28d3a 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java
@@ -25,6 +25,7 @@ import org.apache.maven.surefire.api.report.SafeThrowable;
 import org.apache.maven.surefire.api.report.StackTraceWriter;
 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
+import org.apache.maven.surefire.api.util.internal.ObjectUtils;
 import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
 import org.junit.Test;
 
@@ -78,6 +79,8 @@ public class EventChannelEncoderTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         TestSetReportEntry reportEntry = mock( TestSetReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "skipped test" );
@@ -88,13 +91,15 @@ public class EventChannelEncoderTest
 
         Stream out = Stream.newStream();
         EventChannelEncoder encoder = new EventChannelEncoder( newBufferedChannel( out ) );
-        ByteBuffer encoded = encoder.encode( BOOTERCODE_TEST_ERROR, NORMAL_RUN, reportEntry, false );
+        ByteBuffer encoded = encoder.encode( BOOTERCODE_TEST_ERROR, reportEntry, false );
         ByteArrayOutputStream expectedFrame = new ByteArrayOutputStream();
         expectedFrame.write( ":maven-surefire-event:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":test-error:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -143,13 +148,15 @@ public class EventChannelEncoderTest
 
         out = Stream.newStream();
         encoder = new EventChannelEncoder( newBufferedChannel( out ) );
-        encoded = encoder.encode( BOOTERCODE_TEST_ERROR, NORMAL_RUN, reportEntry, true );
+        encoded = encoder.encode( BOOTERCODE_TEST_ERROR, reportEntry, true );
         expectedFrame = new ByteArrayOutputStream();
         expectedFrame.write( ":maven-surefire-event:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":test-error:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -205,6 +212,8 @@ public class EventChannelEncoderTest
         expectedFrame.write( ":testset-starting:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -259,6 +268,8 @@ public class EventChannelEncoderTest
         expectedFrame.write( ":testset-starting:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -322,6 +333,8 @@ public class EventChannelEncoderTest
         Map<String, String> props = systemProps();
 
         TestSetReportEntry reportEntry = mock( TestSetReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "skipped test" );
@@ -344,6 +357,8 @@ public class EventChannelEncoderTest
             expectedFrame.write( ":sys-prop:".getBytes() );
             expectedFrame.write( 10 );
             expectedFrame.write( ":normal-run:".getBytes() );
+            expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+            expectedFrame.write( ":".getBytes() );
             expectedFrame.write( 5 );
             expectedFrame.write( ":UTF-8:".getBytes() );
             int[] k = toBytes( entry.getKey().length() );
@@ -369,6 +384,8 @@ public class EventChannelEncoderTest
         expectedFrame.write( ":testset-completed:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -431,6 +448,8 @@ public class EventChannelEncoderTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "skipped test" );
@@ -450,6 +469,8 @@ public class EventChannelEncoderTest
         expectedFrame.write( ":test-starting:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -511,6 +532,8 @@ public class EventChannelEncoderTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "skipped test" );
@@ -529,6 +552,8 @@ public class EventChannelEncoderTest
         expectedFrame.write( ":test-succeeded:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -590,6 +615,8 @@ public class EventChannelEncoderTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "skipped test" );
@@ -608,6 +635,8 @@ public class EventChannelEncoderTest
         expectedFrame.write( ":test-failed:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -668,6 +697,8 @@ public class EventChannelEncoderTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "skipped test" );
@@ -686,6 +717,8 @@ public class EventChannelEncoderTest
         expectedFrame.write( ":test-skipped:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -746,6 +779,8 @@ public class EventChannelEncoderTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( ELAPSED_TIME );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "skipped test" );
@@ -763,6 +798,8 @@ public class EventChannelEncoderTest
         expectedFrame.write( ":test-error:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -823,6 +860,8 @@ public class EventChannelEncoderTest
         when( stackTraceWriter.writeTraceToString() ).thenReturn( null );
 
         ReportEntry reportEntry = mock( ReportEntry.class );
+        when( reportEntry.getRunMode() ).thenReturn( NORMAL_RUN );
+        when( reportEntry.getTestRunId() ).thenReturn( 1L );
         when( reportEntry.getElapsed() ).thenReturn( null );
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "skipped test" );
@@ -841,6 +880,8 @@ public class EventChannelEncoderTest
         expectedFrame.write( ":test-assumption-failure:".getBytes( UTF_8 ) );
         expectedFrame.write( (byte) 10 );
         expectedFrame.write( ":normal-run:".getBytes( UTF_8 ) );
+        expectedFrame.write( "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001".getBytes() );
+        expectedFrame.write( ':' );
         expectedFrame.write( (byte) 5 );
         expectedFrame.write( ":UTF-8:".getBytes( UTF_8 ) );
         expectedFrame.write( new byte[] {0, 0, 0, 10} );
@@ -929,21 +970,26 @@ public class EventChannelEncoderTest
     public void testSendOpcode()
     {
         Channel channel = new Channel();
-        new EventChannelEncoder( channel ).testOutput( (TestOutputReportEntry) stdOut( "msg" ) );
+        new EventChannelEncoder( channel )
+            .testOutput( new TestOutputReportEntry( stdOut( "msg" ), NORMAL_RUN, 1L )  );
         assertThat( toString( channel.src ) )
                 .isEqualTo( ":maven-surefire-event:" + (char) 14 + ":std-out-stream:" + (char) 10 + ":normal-run:"
+                    + "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                     + (char) 5 + ":UTF-8:\u0000\u0000\u0000\u0003:msg:" );
 
         channel = new Channel();
-        new EventChannelEncoder( channel ).testOutput( stdErr( null ) );
+        new EventChannelEncoder( channel )
+            .testOutput( new TestOutputReportEntry( stdErr( null ), NORMAL_RUN, 1L ) );
         assertThat( toString( channel.src ) )
                 .isEqualTo( ":maven-surefire-event:" + (char) 14 + ":std-err-stream:" + (char) 10 + ":normal-run:"
+                    + "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                     + (char) 5 + ":UTF-8:\u0000\u0000\u0000\u0001:\u0000:" );
 
         ByteBuffer result = new EventChannelEncoder( new Channel() )
-            .encodeMessage( BOOTERCODE_TEST_ERROR, NORMAL_RUN, "msg" );
+            .encodeMessage( BOOTERCODE_TEST_ERROR, NORMAL_RUN, 1L, "msg" );
         assertThat( toString( result ) )
             .isEqualTo( ":maven-surefire-event:" + (char) 10 + ":test-error:" + (char) 10 + ":normal-run:"
+                + "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                 + (char) 5 + ":UTF-8:\u0000\u0000\u0000\u0003:msg:" );
     }
 
@@ -1115,11 +1161,12 @@ public class EventChannelEncoderTest
         WritableBufferedByteChannel channel = newBufferedChannel( out );
         EventChannelEncoder encoder = new EventChannelEncoder( channel );
 
-        encoder.testOutput( (TestOutputReportEntry) stdOut( "msg" ) );
+        encoder.testOutput( new TestOutputReportEntry( stdOut( "msg" ), NORMAL_RUN, 1L ) );
         channel.close();
 
         String expected = ":maven-surefire-event:\u000e:std-out-stream:"
-            + (char) 10 + ":normal-run:\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
+            + (char) 10 + ":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
+            + "\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
 
         assertThat( new String( out.toByteArray(), UTF_8 ) )
                 .isEqualTo( expected );
@@ -1132,11 +1179,12 @@ public class EventChannelEncoderTest
         WritableBufferedByteChannel channel = newBufferedChannel( out );
         EventChannelEncoder encoder = new EventChannelEncoder( channel );
 
-        encoder.testOutput( stdOutln( "msg" ) );
+        encoder.testOutput( new TestOutputReportEntry( stdOutln( "msg" ), NORMAL_RUN, 1L ) );
         channel.close();
 
         String expected = ":maven-surefire-event:\u0017:std-out-stream-new-line:"
-            + (char) 10 + ":normal-run:\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
+            + (char) 10 + ":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
+            + "\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
 
         assertThat( new String( out.toByteArray(), UTF_8 ) )
                 .isEqualTo( expected );
@@ -1149,11 +1197,12 @@ public class EventChannelEncoderTest
         WritableBufferedByteChannel channel = newBufferedChannel( out );
         EventChannelEncoder encoder = new EventChannelEncoder( channel );
 
-        encoder.testOutput( stdErr( "msg" ) );
+        encoder.testOutput( new TestOutputReportEntry( stdErr( "msg" ), NORMAL_RUN, 1L ) );
         channel.close();
 
         String expected = ":maven-surefire-event:\u000e:std-err-stream:"
-            + (char) 10 + ":normal-run:\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
+            + (char) 10 + ":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
+            + "\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
 
         assertThat( new String( out.toByteArray(), UTF_8 ) )
                 .isEqualTo( expected );
@@ -1166,17 +1215,53 @@ public class EventChannelEncoderTest
         WritableBufferedByteChannel channel = newBufferedChannel( out );
         EventChannelEncoder encoder = new EventChannelEncoder( channel );
 
-        encoder.testOutput( stdErrln( "msg" ) );
+        encoder.testOutput( new TestOutputReportEntry( stdErrln( "msg" ), NORMAL_RUN, 1L ) );
         channel.close();
 
         String expected = ":maven-surefire-event:\u0017:std-err-stream-new-line:"
-            + (char) 10 + ":normal-run:\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
+            + (char) 10 + ":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
+            + "\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
 
         assertThat( new String( out.toByteArray(), UTF_8 ) )
                 .isEqualTo( expected );
     }
 
     @Test
+    @SuppressWarnings( "checkstyle:innerassignment" )
+    public void shouldCountSameNumberOfSystemProperties() throws IOException
+    {
+        Stream stream = Stream.newStream();
+        WritableBufferedByteChannel channel = newBufferedChannel( stream );
+        EventChannelEncoder encoder = new EventChannelEncoder( channel );
+
+        Map<String, String> sysProps = ObjectUtils.systemProps();
+        encoder.encodeSystemProperties( sysProps, NORMAL_RUN, 1L );
+        channel.close();
+
+        for ( Entry<String, String> entry : sysProps.entrySet() )
+        {
+            int[] k = toBytes( entry.getKey().length() );
+            int[] v = toBytes( entry.getValue() == null ? 1 : entry.getValue().getBytes( UTF_8 ).length );
+            ByteArrayOutputStream expectedFrame = new ByteArrayOutputStream();
+            expectedFrame.write( ":maven-surefire-event:sys-prop:normal-run:UTF-8:".getBytes( UTF_8 ) );
+            expectedFrame.write( k[0] );
+            expectedFrame.write( k[1] );
+            expectedFrame.write( k[2] );
+            expectedFrame.write( k[3] );
+            expectedFrame.write( ':' );
+            expectedFrame.write( v[0] );
+            expectedFrame.write( v[1] );
+            expectedFrame.write( v[2] );
+            expectedFrame.write( v[3] );
+            expectedFrame.write( ':' );
+            expectedFrame.write( ( entry.getValue() == null ? "\u0000" : entry.getValue() ).getBytes( UTF_8 ) );
+            expectedFrame.write( ':' );
+            assertThat( stream.toByteArray() )
+                .contains( expectedFrame.toByteArray() );
+        }
+    }
+
+    @Test
     public void shouldHandleExit()
     {
         Stream out = Stream.newStream();
@@ -1220,7 +1305,7 @@ public class EventChannelEncoderTest
         Thread.currentThread().interrupt();
         try
         {
-            encoder.testOutput( (TestOutputReportEntry) stdOut( "msg" ) );
+            encoder.testOutput( new TestOutputReportEntry( stdOut( "msg" ), NORMAL_RUN, 2L )  );
             channel.close();
         }
         finally
@@ -1232,7 +1317,8 @@ public class EventChannelEncoderTest
 
         assertThat( new String( out.toByteArray(), UTF_8 ) )
                 .isEqualTo( ":maven-surefire-event:\u000e:std-out-stream:"
-                    + (char) 10 + ":normal-run:\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:" );
+                    + (char) 10 + ":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0002"
+                    + ":\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:" );
     }
 
     private static class Stream extends PrintStream
diff --git a/surefire-booter/src/test/resources/binary-commands/75171711-encoder.bin b/surefire-booter/src/test/resources/binary-commands/75171711-encoder.bin
index bbc337b..0a4ce71 100644
Binary files a/surefire-booter/src/test/resources/binary-commands/75171711-encoder.bin and b/surefire-booter/src/test/resources/binary-commands/75171711-encoder.bin differ