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:41 UTC

[maven-surefire] branch SUREFIRE-2015 updated (eca4871 -> 7422201)

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

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


    omit eca4871  removed unused field
    omit d87f08f  added Javadoc
    omit 20a24de  [SUREFIRE-2015] Implement testRunId and RunMode in the SimpleReportEntry
     new 5f33991  [SUREFIRE-2015] Implement testRunId and RunMode in the SimpleReportEntry
     new 7422201  [SUREFIRE-2014] Implement testRunId and RunMode in the EventEncoder and EventDecoder

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (eca4871)
            \
             N -- N -- N   refs/heads/SUREFIRE-2015 (7422201)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../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(-)

[maven-surefire] 01/02: [SUREFIRE-2015] Implement testRunId and RunMode in the SimpleReportEntry

Posted by ti...@apache.org.
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 5f33991657ef44ef0521bd2d1d9f8b6a89e772e4
Author: tibordigana <ti...@apache.org>
AuthorDate: Tue Mar 1 08:38:47 2022 +0100

    [SUREFIRE-2015] Implement testRunId and RunMode in the SimpleReportEntry
---
 .../surefire/booterclient/output/ForkClient.java   |  15 +--
 .../surefire/report/DefaultReporterFactory.java    |   3 +-
 .../plugin/surefire/report/TestSetRunListener.java |  20 +---
 .../report/TestcycleConsoleOutputReceiver.java     |   3 +-
 .../plugin/surefire/report/WrappedReportEntry.java |  15 +++
 .../apache/maven/surefire/stream/EventDecoder.java |   7 +-
 .../booterclient/ForkingRunListenerTest.java       |  34 +++---
 .../plugin/surefire/booterclient/MockReporter.java |  14 +--
 .../booterclient/output/ForkClientTest.java        |  16 ++-
 .../maven/plugin/surefire/extensions/E2ETest.java  |  37 ++++---
 .../extensions/ForkedProcessEventNotifierTest.java |   7 +-
 .../report/DefaultReporterFactoryTest.java         |   3 +-
 .../surefire/report/StatelessXmlReporterTest.java  |  59 +++++-----
 .../surefire/report/WrappedReportEntryTest.java    |  17 +--
 .../runorder/RunEntryStatisticsMapTest.java        |  33 ++++--
 .../report/ConsoleOutputFileReporterTest.java      |  21 ++--
 .../maven/surefire/report/FileReporterTest.java    |  10 +-
 .../surefire/api/booter/ForkingRunListener.java    |  16 +--
 .../api/report/CategorizedReportEntry.java         |  30 +++--
 .../surefire/api/report/ConsoleOutputCapture.java  |   7 +-
 .../surefire/api/report/OutputReportEntry.java     |  45 ++++++++
 .../maven/surefire/api/report/ReportEntry.java     |  17 +++
 .../maven/surefire/api/report/ReporterFactory.java |   2 +-
 .../maven/surefire/api/report/RunListener.java     |   9 --
 .../surefire/api/report/SimpleReportEntry.java     | 107 ++++++++++++------
 .../surefire/api/report/TestOutputReceiver.java    |   6 +-
 .../surefire/api/report/TestOutputReportEntry.java |  13 ++-
 .../surefire/api/report/TestReportListener.java    |   6 +-
 .../booter/spi/EventChannelEncoderTest.java        |   7 +-
 .../maven/surefire/report/ClassMethodIndexer.java  |  64 +++++++++++
 .../maven/surefire/report/RunModeSetter.java}      |  13 +--
 .../surefire/report/ClassMethodIndexerTest.java    |  75 +++++++++++++
 .../surefire/common/junit4/JUnit4RunListener.java  |  46 ++++++--
 .../maven/surefire/common/junit4/MockReporter.java |  17 +--
 .../junitplatform/JUnitPlatformProvider.java       |   4 +
 .../surefire/junitplatform/RunListenerAdapter.java |  32 ++++--
 .../junitplatform/JUnitPlatformProviderTest.java   |  32 ++++--
 .../junitplatform/RunListenerAdapterTest.java      |  54 ++++++---
 surefire-providers/surefire-junit3/pom.xml         |   5 +
 .../maven/surefire/junit/JUnit3Provider.java       |  36 +++---
 .../maven/surefire/junit/JUnit3Reporter.java       | 122 +++++++++++++++++++++
 .../maven/surefire/junit/JUnitTestSetExecutor.java |  11 +-
 .../maven/surefire/junit/PojoTestSetExecutor.java  |  48 ++++----
 .../surefire/junit/SurefireTestSetExecutor.java    |   3 +-
 .../junit/TestListenerInvocationHandler.java       |  17 ++-
 .../maven/surefire/junit/JUnitTestSetTest.java     |  77 +++++++++++--
 .../maven/surefire/junit4/JUnit4Provider.java      |  26 +++--
 .../junitcore/ClassesParallelRunListener.java      |   5 +-
 .../surefire/junitcore/ConcurrentRunListener.java  |  64 +++++------
 .../surefire/junitcore/JUnitCoreProvider.java      |  47 ++++----
 .../surefire/junitcore/JUnitCoreRunListener.java   |  11 +-
 .../maven/surefire/junitcore/LogicalStream.java    |   9 +-
 .../junitcore/MethodsParallelRunListener.java      |   1 +
 .../junitcore/NonConcurrentRunListener.java        |  26 ++---
 .../maven/surefire/junitcore/TestMethod.java       |  10 +-
 .../apache/maven/surefire/junitcore/TestSet.java   |  27 ++++-
 .../junitcore/ConcurrentRunListenerTest.java       |   6 +-
 .../maven/surefire/junitcore/JUnitCoreTester.java  |  14 ++-
 .../maven/surefire/junitcore/MockReporter.java     |  27 +++--
 .../maven/surefire/junitcore/Surefire746Test.java  |   3 +-
 .../maven/surefire/junitcore/TestMethodTest.java   |   7 +-
 .../testng/ConfigurationAwareTestNGReporter.java   |   7 +-
 .../surefire/testng/TestNGDirectoryTestSuite.java  |  35 +++---
 .../maven/surefire/testng/TestNGExecutor.java      |  51 ++-------
 .../maven/surefire/testng/TestNGProvider.java      | 103 +++++++++++------
 .../maven/surefire/testng/TestNGReporter.java      |  72 +++++++++---
 .../maven/surefire/testng/TestNGXmlTestSuite.java  |   9 +-
 .../apache/maven/surefire/testng/TestSuite.java    |  11 +-
 .../maven/surefire/testng/TestNGReporterTest.java  |  29 ++++-
 surefire-shadefire/pom.xml                         |   1 +
 70 files changed, 1233 insertions(+), 603 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 67b2ce7..fc622ef 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
@@ -80,7 +80,7 @@ public final class ForkClient
 
     private final int forkNumber;
 
-    private volatile TestReportListener testSetReporter;
+    private volatile TestReportListener<TestOutputReportEntry> testSetReporter;
 
     /**
      * Written by one Thread and read by another: Main Thread and ForkStarter's Thread.
@@ -139,10 +139,11 @@ public final class ForkClient
         public void handle( RunMode runMode, TestSetReportEntry reportEntry )
         {
             testsInProgress.clear();
-            TestSetReportEntry entry = reportEntry( reportEntry.getSourceName(), reportEntry.getSourceText(),
-                    reportEntry.getName(), reportEntry.getNameText(),
-                    reportEntry.getGroup(), reportEntry.getStackTraceWriter(), reportEntry.getElapsed(),
-                    reportEntry.getMessage(), getTestVmSystemProperties() );
+            TestSetReportEntry entry = reportEntry( reportEntry.getRunMode(), reportEntry.getTestRunId(),
+                reportEntry.getSourceName(), reportEntry.getSourceText(),
+                reportEntry.getName(), reportEntry.getNameText(),
+                reportEntry.getGroup(), reportEntry.getStackTraceWriter(), reportEntry.getElapsed(),
+                reportEntry.getMessage(), getTestVmSystemProperties() );
             getTestSetReporter().testSetCompleted( entry );
         }
     }
@@ -370,7 +371,7 @@ public final class ForkClient
     /**
      * Only {@link #getConsoleOutputReceiver()} may call this method in another Thread.
      */
-    private TestReportListener getTestSetReporter()
+    private TestReportListener<TestOutputReportEntry> getTestSetReporter()
     {
         if ( testSetReporter == null )
         {
@@ -414,7 +415,7 @@ public final class ForkClient
         return getTestSetReporter();
     }
 
-    public TestOutputReceiver getConsoleOutputReceiver()
+    public TestOutputReceiver<TestOutputReportEntry> getConsoleOutputReceiver()
     {
         return getTestSetReporter();
     }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
index cf93b6b..d45ae83 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
@@ -23,6 +23,7 @@ import org.apache.maven.plugin.surefire.StartupReportConfiguration;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.log.api.Level;
 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.shared.utils.logging.MessageBuilder;
 import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
 import org.apache.maven.surefire.extensions.StatelessReportEventListener;
@@ -97,7 +98,7 @@ public class DefaultReporterFactory
     }
 
     @Override
-    public TestReportListener createTestReportListener()
+    public TestReportListener<TestOutputReportEntry> createTestReportListener()
     {
         TestSetRunListener testSetRunListener =
             new TestSetRunListener( createConsoleReporter(),
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
index 6af665d..2884f68 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
@@ -26,22 +26,19 @@ import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
+import org.apache.maven.surefire.api.report.ReportEntry;
 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
+import org.apache.maven.surefire.api.report.TestReportListener;
+import org.apache.maven.surefire.api.report.TestSetReportEntry;
 import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
 import org.apache.maven.surefire.extensions.StatelessReportEventListener;
 import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
 import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
-import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
-import org.apache.maven.surefire.api.report.TestReportListener;
-import org.apache.maven.surefire.api.report.TestSetReportEntry;
 
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.FAILURE;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SKIPPED;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
-import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
-import static java.util.Objects.requireNonNull;
 
 /**
  * Reports data for a single test set.
@@ -50,7 +47,7 @@ import static java.util.Objects.requireNonNull;
  * @author Kristian Rosenvold
  */
 public class TestSetRunListener
-    implements TestReportListener
+    implements TestReportListener<TestOutputReportEntry>
 {
     private final Queue<TestMethodStats> testMethodStats = new ConcurrentLinkedQueue<>();
 
@@ -74,8 +71,6 @@ public class TestSetRunListener
 
     private Utf8RecodingDeferredFileOutputStream testStdErr = initDeferred( "stderr" );
 
-    private volatile RunMode runMode = NORMAL_RUN;
-
     @SuppressWarnings( "checkstyle:parameternumber" )
     public TestSetRunListener( StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats>
                                            consoleReporter,
@@ -282,13 +277,6 @@ public class TestSetRunListener
     {
     }
 
-    public RunMode markAs( RunMode currentRunMode )
-    {
-        RunMode runMode = this.runMode;
-        this.runMode = requireNonNull( currentRunMode );
-        return runMode;
-    }
-
     @Override
     public void testAssumptionFailure( ReportEntry report )
     {
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java
index 90cef51..dd4a10f 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java
@@ -20,6 +20,7 @@ package org.apache.maven.plugin.surefire.report;
  */
 
 import org.apache.maven.surefire.api.report.TestOutputReceiver;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 
@@ -27,7 +28,7 @@ import org.apache.maven.surefire.api.report.TestSetReportEntry;
  * @author Kristian Rosenvold
  */
 public interface TestcycleConsoleOutputReceiver
-    extends TestOutputReceiver, ConsoleOutputReportEventListener
+    extends TestOutputReceiver<TestOutputReportEntry>, ConsoleOutputReportEventListener
 {
     void testSetStarting( TestSetReportEntry reportEntry );
 
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
index 75b1f66..bc2fca0 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
@@ -20,9 +20,11 @@ package org.apache.maven.plugin.surefire.report;
  */
 
 import org.apache.maven.surefire.api.report.ReportEntry;
+import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.StackTraceWriter;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 
+import javax.annotation.Nonnull;
 import java.util.Collections;
 import java.util.Map;
 
@@ -224,6 +226,19 @@ public class WrappedReportEntry
         return original.getReportNameWithGroup();
     }
 
+    @Nonnull
+    @Override
+    public RunMode getRunMode()
+    {
+        return original.getRunMode();
+    }
+
+    @Override
+    public Long getTestRunId()
+    {
+        return original.getTestRunId();
+    }
+
     @Override
     public Map<String, String> getSystemProperties()
     {
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 60c9fc9..aab8689 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
@@ -58,14 +58,15 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.channels.ReadableByteChannel;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.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;
@@ -382,8 +383,8 @@ public class EventDecoder extends AbstractStreamDecoder<Event, ForkedProcessEven
         throws NumberFormatException
     {
         StackTraceWriter stackTraceWriter = toTrace( traceMessage, smartTrimmedStackTrace, stackTrace );
-        return reportEntry( source, sourceText, name, nameText, group, stackTraceWriter, timeElapsed, message,
-            Collections.<String, String>emptyMap() );
+        return reportEntry( NORMAL_RUN /*todo*/, 0L /*todo*/, 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 67ccde7..8eae70a 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
@@ -25,6 +25,7 @@ import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
 import org.apache.maven.plugin.surefire.extensions.EventConsumerThread;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.api.booter.ForkingRunListener;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.booter.spi.EventChannelEncoder;
 import org.apache.maven.surefire.api.event.Event;
 import org.apache.maven.surefire.extensions.EventHandler;
@@ -58,6 +59,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.apache.maven.surefire.api.report.TestOutputReportEntry.stdOut;
 import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
 import static org.apache.maven.surefire.api.util.internal.Channels.newChannel;
@@ -182,8 +184,8 @@ public class ForkingRunListenerTest
     public void testConsoleOutput() throws Exception
     {
         final StandardTestRun standardTestRun = new StandardTestRun();
-        TestOutputReceiver directConsoleReporter = standardTestRun.run();
-        directConsoleReporter.writeTestOutput( stdOut( "HeyYou" ) );
+        TestOutputReceiver<TestOutputReportEntry> directConsoleReporter = standardTestRun.run();
+        directConsoleReporter.writeTestOutput( (TestOutputReportEntry) stdOut( "HeyYou" ) );
         standardTestRun.assertExpected( MockReporter.STDOUT, "HeyYou" );
     }
 
@@ -276,9 +278,9 @@ public class ForkingRunListenerTest
         }
 
         MockReporter reporter = (MockReporter) forkStreamClient.getReporter();
-        assertEquals( MockReporter.TEST_STARTING, reporter.getFirstEvent() );
-        assertEquals( expected, reporter.getFirstData() );
-        assertEquals( 1, reporter.getEvents().size() );
+        assertThat( reporter.getFirstEvent() ).isEqualTo( MockReporter.TEST_STARTING );
+        //assertThat( reporter.getFirstData() ).isEqualTo( expected ); /*todo uncomment in SUREFIRE-2014*/
+        assertThat( reporter.getEvents() ).hasSize( 1 );
 
         forkStreamClient = new ForkClient( providerReporterFactory, notifiableTestStream, 2 );
         for ( Event e : streamToEvent( anotherContent.toByteArray() ) )
@@ -286,9 +288,9 @@ public class ForkingRunListenerTest
             forkStreamClient.handleEvent( e );
         }
         MockReporter reporter2 = (MockReporter) forkStreamClient.getReporter();
-        assertEquals( MockReporter.TEST_SKIPPED, reporter2.getFirstEvent() );
-        assertEquals( secondExpected, reporter2.getFirstData() );
-        assertEquals( 1, reporter2.getEvents().size() );
+        assertThat( reporter2.getFirstEvent() ).isEqualTo( MockReporter.TEST_SKIPPED );
+        //assertThat( reporter2.getFirstData() ).isEqualTo( secondExpected ); /*todo uncomment in SUREFIRE-2014*/
+        assertThat( reporter2.getEvents() ).hasSize( 1 );
     }
 
     private static List<Event> streamToEvent( byte[] stream ) throws Exception
@@ -422,7 +424,8 @@ public class ForkingRunListenerTest
 
     private SimpleReportEntry createDefaultReportEntry( Map<String, String> sysProps )
     {
-        return new SimpleReportEntry( "com.abc.TestClass", null, "testMethod", null, null, 22, sysProps );
+        return new SimpleReportEntry( NORMAL_RUN, 1L,
+            "com.abc.TestClass", null, "testMethod", null, null, 22, sysProps );
     }
 
     private SimpleReportEntry createDefaultReportEntry()
@@ -432,7 +435,8 @@ public class ForkingRunListenerTest
 
     private SimpleReportEntry createAnotherDefaultReportEntry()
     {
-        return new SimpleReportEntry( "com.abc.AnotherTestClass", null, "testAnotherMethod", null, 42 );
+        return new SimpleReportEntry( NORMAL_RUN, 0L,
+            "com.abc.AnotherTestClass", null, "testAnotherMethod", null, 42 );
     }
 
     private SimpleReportEntry createReportEntryWithStackTrace()
@@ -445,7 +449,8 @@ public class ForkingRunListenerTest
         {
             StackTraceWriter stackTraceWriter =
                 new LegacyPojoStackTraceWriter( "org.apache.tests.TestClass", "testMethod11", e );
-            return new CategorizedReportEntry( "com.abc.TestClass", "testMethod", "aGroup", stackTraceWriter, 77 );
+            return new CategorizedReportEntry( NORMAL_RUN, 0L,
+                "com.abc.TestClass", "testMethod", "aGroup", stackTraceWriter, 77 );
         }
     }
 
@@ -459,11 +464,12 @@ public class ForkingRunListenerTest
         {
             StackTraceWriter stackTraceWriter =
                 new LegacyPojoStackTraceWriter( "org.apache.tests.TestClass", "testMethod11", e );
-            return new CategorizedReportEntry( "com.abc.TestClass", "testMethod", "aGroup", stackTraceWriter, 77 );
+            return new CategorizedReportEntry( NORMAL_RUN, 0L,
+                "com.abc.TestClass", "testMethod", "aGroup", stackTraceWriter, 77 );
         }
     }
 
-    private TestReportListener createForkingRunListener()
+    private TestReportListener<TestOutputReportEntry> createForkingRunListener()
     {
         WritableBufferedByteChannel channel = (WritableBufferedByteChannel) newChannel( printStream );
         return new ForkingRunListener( new EventChannelEncoder( channel ), false );
@@ -473,7 +479,7 @@ public class ForkingRunListenerTest
     {
         private MockReporter reporter;
 
-        public TestReportListener run()
+        public TestReportListener<TestOutputReportEntry> run()
             throws ReporterException
         {
             reset();
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
index 3f5557f..c440430 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
@@ -23,7 +23,6 @@ import org.apache.maven.surefire.api.report.ReportEntry;
 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -33,7 +32,7 @@ import java.util.concurrent.atomic.AtomicInteger;
  * Internal tests use only.
  */
 public class MockReporter
-        implements TestReportListener
+        implements TestReportListener<TestOutputReportEntry>
 {
     private final List<String> events = new ArrayList<>();
 
@@ -131,17 +130,6 @@ public class MockReporter
     {
     }
 
-    @Override
-    public RunMode markAs( RunMode currentRunMode )
-    {
-        return null;
-    }
-
-    public void testSkippedByUser( ReportEntry report )
-    {
-        testSkipped( report );
-    }
-
     public List<String> getEvents()
     {
         return events;
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 c440114..5d8251c 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
@@ -1296,7 +1296,8 @@ public class ForkClientTest
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         ForkClient client = new ForkClient( factory, notifiableTestStream,  0 );
-        SimpleReportEntry testStarted = new SimpleReportEntry( reportEntry.getSourceName(), null, null, null );
+        SimpleReportEntry testStarted =
+            new SimpleReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(), null, null, null );
         client.handleEvent( new TestStartingEvent( NORMAL_RUN, testStarted ) );
 
         assertThat( client.testsInProgress() )
@@ -1400,7 +1401,8 @@ public class ForkClientTest
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        SimpleReportEntry testClass = new SimpleReportEntry( reportEntry.getSourceName(), null, null, null );
+        SimpleReportEntry testClass =
+            new SimpleReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(), null, null, null );
         client.handleEvent( new TestStartingEvent( NORMAL_RUN, testClass ) );
 
         assertThat( client.testsInProgress() )
@@ -1510,7 +1512,8 @@ public class ForkClientTest
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        SimpleReportEntry testStarted = new SimpleReportEntry( reportEntry.getSourceName(), null, null, null );
+        SimpleReportEntry testStarted =
+            new SimpleReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(), null, null, null );
         client.handleEvent( new TestStartingEvent( NORMAL_RUN, testStarted ) );
 
         assertThat( client.testsInProgress() )
@@ -1619,8 +1622,8 @@ public class ForkClientTest
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        SimpleReportEntry testStarted =
-            new SimpleReportEntry( reportEntry.getSourceName(), reportEntry.getSourceText(), null, null );
+        SimpleReportEntry testStarted = new SimpleReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(),
+            reportEntry.getSourceText(), null, null );
         client.handleEvent( new TestStartingEvent( NORMAL_RUN, testStarted ) );
 
         assertThat( client.testsInProgress() )
@@ -1725,7 +1728,8 @@ public class ForkClientTest
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         ForkClient client = new ForkClient( factory, notifiableTestStream, 0 );
-        SimpleReportEntry testStarted = new SimpleReportEntry( reportEntry.getSourceName(), null, null, null );
+        SimpleReportEntry testStarted =
+            new SimpleReportEntry( NORMAL_RUN, 1L, reportEntry.getSourceName(), null, null, null );
         client.handleEvent( new TestStartingEvent( NORMAL_RUN, testStarted ) );
 
         assertThat( client.testsInProgress() )
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 d3a3b72..304ed57 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
@@ -19,14 +19,30 @@ package org.apache.maven.plugin.surefire.extensions;
  * under the License.
  */
 
+import javax.annotation.Nonnull;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
 import org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
 import org.apache.maven.surefire.api.booter.Command;
 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;
@@ -36,21 +52,6 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
-import javax.annotation.Nonnull;
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
-import java.util.UUID;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
 import static java.util.concurrent.TimeUnit.HOURS;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.apache.maven.surefire.api.report.TestOutputReportEntry.stdOutln;
@@ -135,10 +136,10 @@ public class E2ETest
             @Override
             public void run()
             {
-                TestOutputReceiver target = new TestOutputReceiver()
+                TestOutputReceiver<OutputReportEntry> target = new TestOutputReceiver()
                 {
                     @Override
-                    public void writeTestOutput( TestOutputReportEntry reportEntry )
+                    public void writeTestOutput( OutputReportEntry reportEntry )
                     {
                         encoder.testOutput( stdOutln( reportEntry.getLog() ) );
                     }
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 7aad5af..a252077 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
@@ -36,6 +36,7 @@ import org.apache.maven.surefire.api.report.ReportEntry;
 import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.SafeThrowable;
 import org.apache.maven.surefire.api.report.StackTraceWriter;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
 import org.apache.maven.surefire.booter.spi.EventChannelEncoder;
@@ -530,7 +531,7 @@ public class ForkedProcessEventNotifierTest
             final Stream out = Stream.newStream();
             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
             EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
-            encoder.testOutput( stdOut( "msg" ) );
+            encoder.testOutput( (TestOutputReportEntry) stdOut( "msg" ) );
             wChannel.close();
 
             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -569,7 +570,7 @@ public class ForkedProcessEventNotifierTest
             final Stream out = Stream.newStream();
             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
             EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
-            encoder.testOutput( stdOut( "" ) );
+            encoder.testOutput( (TestOutputReportEntry) stdOut( "" ) );
             wChannel.close();
 
             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
@@ -608,7 +609,7 @@ public class ForkedProcessEventNotifierTest
             final Stream out = Stream.newStream();
             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
             EventChannelEncoder encoder = new EventChannelEncoder( wChannel );
-            encoder.testOutput( stdOut( null ) );
+            encoder.testOutput( (TestOutputReportEntry) stdOut( null ) );
             wChannel.close();
 
             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
index cb36c0d..ded28a3 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
@@ -33,6 +33,7 @@ import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.shared.utils.logging.MessageUtils;
 import org.apache.maven.surefire.report.RunStatistics;
@@ -287,7 +288,7 @@ public class DefaultReporterFactoryTest
 
         DefaultReporterFactory factory = new DefaultReporterFactory( reportConfig, reporter );
 
-        TestReportListener runListener = factory.createTestReportListener();
+        TestReportListener<TestOutputReportEntry> runListener = factory.createTestReportListener();
 
         assertTrue( runListener.isDebugEnabled() );
         assertTrue( runListener.isInfoEnabled() );
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
index 4358a39..8744c91 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
@@ -44,9 +44,10 @@ import java.util.concurrent.atomic.AtomicInteger;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.nio.file.Files.readAllLines;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
-import static org.apache.maven.plugin.surefire.report.ReportEntryType.FAILURE;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SKIPPED;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
+import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAILURE;
 import static org.apache.maven.surefire.api.util.internal.ObjectUtils.systemProps;
 import static org.apache.maven.surefire.shared.utils.StringUtils.isEmpty;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -111,8 +112,9 @@ public class StatelessXmlReporterTest
                         false, false, false, false );
         reporter.cleanTestHistoryMap();
 
-        ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), null, getClass().getName(), null, 12 );
-        WrappedReportEntry testSetReportEntry = new WrappedReportEntry( reportEntry, SUCCESS,
+        ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 0L,
+            getClass().getName(), null, getClass().getName(), null, 12 );
+        WrappedReportEntry testSetReportEntry = new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS,
                 12, null, null, systemProps() );
         stats.testSucceeded( testSetReportEntry );
         reporter.testSetCompleted( testSetReportEntry, stats );
@@ -126,7 +128,8 @@ public class StatelessXmlReporterTest
     public void testAllFieldsSerialized()
             throws IOException
     {
-        ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), null, TEST_ONE, null, 12 );
+        ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 0L,
+            getClass().getName(), null, TEST_ONE, null, 12 );
         WrappedReportEntry testSetReportEntry =
                 new WrappedReportEntry( reportEntry, SUCCESS, 12, null, null, systemProps() );
         expectedReportFile = new File( reportDir, "TEST-" + getClass().getName() + ".xml" );
@@ -152,9 +155,9 @@ public class StatelessXmlReporterTest
         Utf8RecodingDeferredFileOutputStream stdErr = new Utf8RecodingDeferredFileOutputStream( "fds" );
 
         stdErr.write( stdErrPrefix + "?&-&amp;&#163;\u0020\u0000\u001F", false );
-        WrappedReportEntry t2 =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_TWO, null,
-                        stackTraceWriter, 13 ), ERROR, 13, stdOut, stdErr );
+        WrappedReportEntry t2 = new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 0L,
+            getClass().getName(), null, TEST_TWO, null, stackTraceWriter, 13 ),
+            ReportEntryType.ERROR, 13, stdOut, stdErr );
 
         stats.testSucceeded( t2 );
         StatelessXmlReporter reporter = new StatelessXmlReporter( reportDir, null, false, 0,
@@ -195,8 +198,9 @@ public class StatelessXmlReporterTest
             throws IOException
     {
         WrappedReportEntry testSetReportEntry =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_ONE, null, 12 ),
-                        SUCCESS, 12, null, null, systemProps() );
+                new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 0L,
+                    getClass().getName(), null, TEST_ONE, null, 12 ),
+                        ReportEntryType.SUCCESS, 12, null, null, systemProps() );
         expectedReportFile = new File( reportDir, "TEST-" + getClass().getName() + ".xml" );
 
         stats.testSucceeded( testSetReportEntry );
@@ -210,25 +214,22 @@ public class StatelessXmlReporterTest
         String secondRunOut = "second run out";
         String secondRunErr = "second run err";
 
-        WrappedReportEntry testTwoFirstError =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_TWO, null,
-                        stackTraceWriterOne, 5 ), ERROR, 5, createStdOutput( firstRunOut ),
-                        createStdOutput( firstRunErr ) );
+        String cls = getClass().getName();
+        WrappedReportEntry testTwoFirstError = new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 0L,
+            cls, null, TEST_TWO, null, stackTraceWriterOne, 5 ),
+            ReportEntryType.ERROR, 5, createStdOutput( firstRunOut ), createStdOutput( firstRunErr ) );
 
-        WrappedReportEntry testTwoSecondError =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_TWO, null,
-                        stackTraceWriterTwo, 13 ), ERROR, 13, createStdOutput( secondRunOut ),
-                        createStdOutput( secondRunErr ) );
+        WrappedReportEntry testTwoSecondError = new WrappedReportEntry( new SimpleReportEntry(
+            RERUN_TEST_AFTER_FAILURE, 1L, cls, null, TEST_TWO, null, stackTraceWriterTwo, 13 ),
+            ReportEntryType.ERROR, 13, createStdOutput( secondRunOut ), createStdOutput( secondRunErr ) );
 
-        WrappedReportEntry testThreeFirstRun =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_THREE, null,
-                        stackTraceWriterOne, 13 ), FAILURE, 13, createStdOutput( firstRunOut ),
-                        createStdOutput( firstRunErr ) );
+        WrappedReportEntry testThreeFirstRun = new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 2L,
+            cls, null, TEST_THREE, null, stackTraceWriterOne, 13 ),
+            ReportEntryType.FAILURE, 13, createStdOutput( firstRunOut ), createStdOutput( firstRunErr ) );
 
-        WrappedReportEntry testThreeSecondRun =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_THREE, null,
-                        stackTraceWriterTwo, 2 ), SUCCESS, 2, createStdOutput( secondRunOut ),
-                        createStdOutput( secondRunErr ) );
+        WrappedReportEntry testThreeSecondRun = new WrappedReportEntry( new SimpleReportEntry(
+            RERUN_TEST_AFTER_FAILURE, 3L, cls, null, TEST_THREE, null, stackTraceWriterTwo, 2 ),
+            ReportEntryType.SUCCESS, 2, createStdOutput( secondRunOut ), createStdOutput( secondRunErr ) );
 
         stats.testSucceeded( testTwoFirstError );
         stats.testSucceeded( testThreeFirstRun );
@@ -312,14 +313,15 @@ public class StatelessXmlReporterTest
         String secondRunErr = "second run err";
 
         WrappedReportEntry testTwoFirstError =
-            new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_TWO, null,
+            new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 1L, getClass().getName(), null, TEST_TWO, null,
                 stackTraceWriterOne, 5 ), ERROR, 5, createStdOutput( firstRunOut ),
                 createStdOutput( firstRunErr ) );
 
         stats.testSucceeded( testTwoFirstError );
 
         WrappedReportEntry testTwoSecondError =
-            new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_TWO, null,
+            new WrappedReportEntry( new SimpleReportEntry( RERUN_TEST_AFTER_FAILURE, 1L, getClass().getName(), null,
+                TEST_TWO, null,
                 stackTraceWriterTwo, 13 ), SKIPPED, 13, createStdOutput( secondRunOut ),
                 createStdOutput( secondRunErr ) );
 
@@ -330,7 +332,8 @@ public class StatelessXmlReporterTest
                 new HashMap<>(), XSD, "3.0", false, false, false, false );
 
         WrappedReportEntry testSetReportEntry =
-            new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, null, null,
+            new WrappedReportEntry( new SimpleReportEntry( RERUN_TEST_AFTER_FAILURE, 1L, getClass().getName(), null,
+                null, null,
                 stackTraceWriterOne, 5 ), ERROR, 20, createStdOutput( firstRunOut ),
                 createStdOutput( firstRunErr ) );
 
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/WrappedReportEntryTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/WrappedReportEntryTest.java
index 208896e..7d40e88 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/WrappedReportEntryTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/WrappedReportEntryTest.java
@@ -28,6 +28,7 @@ import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.FAILURE;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SKIPPED;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 
 /**
  * @author Kristian Rosenvold
@@ -39,7 +40,8 @@ public class WrappedReportEntryTest
     {
         String className = "surefire.testcase.JunitParamsTest";
         WrappedReportEntry wr =
-            new WrappedReportEntry( new SimpleReportEntry( className, null, null, null ), SUCCESS, 12, null, null );
+            new WrappedReportEntry( new SimpleReportEntry( NORMAL_RUN, 1L, className, null, null, null ),
+                SUCCESS, 12, null, null );
         final String reportName = wr.getReportSourceName();
         assertEquals( "surefire.testcase.JunitParamsTest.null", wr.getClassMethodName() );
         assertEquals( "surefire.testcase.JunitParamsTest", reportName );
@@ -50,7 +52,8 @@ public class WrappedReportEntryTest
 
     public void testRegular()
     {
-        ReportEntry reportEntry = new SimpleReportEntry( "surefire.testcase.JunitParamsTest", null, "testSum", null );
+        ReportEntry reportEntry =
+            new SimpleReportEntry( NORMAL_RUN, 1L, "surefire.testcase.JunitParamsTest", null, "testSum", null );
         WrappedReportEntry wr = new WrappedReportEntry( reportEntry, null, 12, null, null );
         assertEquals( "surefire.testcase.JunitParamsTest.testSum", wr.getClassMethodName() );
         assertEquals( "surefire.testcase.JunitParamsTest", wr.getReportSourceName() );
@@ -69,8 +72,8 @@ public class WrappedReportEntryTest
 
     public void testDisplayNames()
     {
-        ReportEntry reportEntry =
-                new SimpleReportEntry( "surefire.testcase.JunitParamsTest", "dn1", "testSum", "dn2", "exception" );
+        ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 0L,
+            "surefire.testcase.JunitParamsTest", "dn1", "testSum", "dn2", "exception" );
         WrappedReportEntry wr = new WrappedReportEntry( reportEntry, ERROR, 12, null, null );
         assertEquals( "surefire.testcase.JunitParamsTest.testSum", wr.getClassMethodName() );
         assertEquals( "dn1", wr.getReportSourceName() );
@@ -90,7 +93,7 @@ public class WrappedReportEntryTest
 
     public void testEqualDisplayNames()
     {
-        ReportEntry reportEntry = new SimpleReportEntry( "surefire.testcase.JunitParamsTest",
+        ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 1L, "surefire.testcase.JunitParamsTest",
                 "surefire.testcase.JunitParamsTest", "testSum", "testSum" );
         WrappedReportEntry wr = new WrappedReportEntry( reportEntry, FAILURE, 12, null, null );
         assertEquals( "surefire.testcase.JunitParamsTest", wr.getReportSourceName() );
@@ -104,7 +107,7 @@ public class WrappedReportEntryTest
     public void testGetReportNameWithParams()
     {
         String className = "[0] 1\u002C 2\u002C 3 (testSum)";
-        ReportEntry reportEntry = new SimpleReportEntry( className, null, null, null );
+        ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 1L, className, null, null, null );
         WrappedReportEntry wr = new WrappedReportEntry( reportEntry, SKIPPED, 12, null, null );
         final String reportName = wr.getReportSourceName();
         assertEquals( "[0] 1, 2, 3 (testSum)", reportName );
@@ -116,7 +119,7 @@ public class WrappedReportEntryTest
     public void testElapsed()
     {
         String className = "[0] 1\u002C 2\u002C 3 (testSum)";
-        ReportEntry reportEntry = new SimpleReportEntry( className, null, null, null );
+        ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 1L, className, null, null, null );
         WrappedReportEntry wr = new WrappedReportEntry( reportEntry, null, 12, null, null );
         String elapsedTimeSummary = wr.getElapsedTimeSummary();
         assertEquals( "[0] 1, 2, 3 (testSum)  Time elapsed: 0.012 s",
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMapTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMapTest.java
index 69390d7..8f5f580 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMapTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMapTest.java
@@ -38,6 +38,7 @@ import junit.framework.TestCase;
 import org.apache.maven.surefire.api.util.internal.ClassMethod;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.apache.maven.surefire.shared.io.IOUtils.readLines;
 import static org.apache.maven.surefire.api.util.internal.StringUtils.NL;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -86,7 +87,8 @@ public class RunEntryStatisticsMapTest
     {
         File data = File.createTempFile( "surefire-unit", "test" );
         RunEntryStatisticsMap newResults = new RunEntryStatisticsMap();
-        ReportEntry reportEntry = new SimpleReportEntry( "abc", null, null, null, 42 );
+        ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 0L,
+            "abc", null, null, null, 42 );
         newResults.add( newResults.createNextGeneration( reportEntry ) );
         newResults.serialize( data );
         try ( InputStream io = new FileInputStream( data ) )
@@ -131,9 +133,12 @@ public class RunEntryStatisticsMapTest
         RunEntryStatisticsMap existingEntries = RunEntryStatisticsMap.fromFile( data );
         RunEntryStatisticsMap newResults = new RunEntryStatisticsMap();
 
-        ReportEntry reportEntry1 = new SimpleReportEntry( "abc", null, "method1", null, 42 );
-        ReportEntry reportEntry2 = new SimpleReportEntry( "abc", null, "willFail", null, 17 );
-        ReportEntry reportEntry3 = new SimpleReportEntry( "abc", null, "method3", null, 100 );
+        ReportEntry reportEntry1 = new SimpleReportEntry( NORMAL_RUN, 0L,
+            "abc", null, "method1", null, 42 );
+        ReportEntry reportEntry2 = new SimpleReportEntry( NORMAL_RUN, 0L,
+            "abc", null, "willFail", null, 17 );
+        ReportEntry reportEntry3 = new SimpleReportEntry( NORMAL_RUN, 0L,
+            "abc", null, "method3", null, 100 );
 
         newResults.add( existingEntries.createNextGeneration( reportEntry1 ) );
         newResults.add( existingEntries.createNextGeneration( reportEntry2 ) );
@@ -154,9 +159,12 @@ public class RunEntryStatisticsMapTest
         RunEntryStatisticsMap nextRun = RunEntryStatisticsMap.fromFile( data );
         newResults = new RunEntryStatisticsMap();
 
-        ReportEntry newRunReportEntry1 = new SimpleReportEntry( "abc", null, "method1", null, 52 );
-        ReportEntry newRunReportEntry2 = new SimpleReportEntry( "abc", null, "willFail", null, 27 );
-        ReportEntry newRunReportEntry3 = new SimpleReportEntry( "abc", null, "method3", null, 110 );
+        ReportEntry newRunReportEntry1 = new SimpleReportEntry( NORMAL_RUN, 0L,
+            "abc", null, "method1", null, 52 );
+        ReportEntry newRunReportEntry2 = new SimpleReportEntry( NORMAL_RUN, 0L,
+            "abc", null, "willFail", null, 27 );
+        ReportEntry newRunReportEntry3 = new SimpleReportEntry( NORMAL_RUN, 0L,
+            "abc", null, "method3", null, 110 );
 
         newResults.add( nextRun.createNextGeneration( newRunReportEntry1 ) );
         newResults.add( nextRun.createNextGenerationFailure( newRunReportEntry2 ) );
@@ -180,7 +188,8 @@ public class RunEntryStatisticsMapTest
     {
         File data = File.createTempFile( "surefire-unit", "test" );
         RunEntryStatisticsMap reportEntries = RunEntryStatisticsMap.fromFile( data );
-        ReportEntry reportEntry = new SimpleReportEntry( "abc", null, "line1\nline2" + NL + " line3", null, 42 );
+        ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 0L,
+            "abc", null, "line1\nline2" + NL + " line3", null, 42 );
         reportEntries.add( reportEntries.createNextGeneration( reportEntry ) );
 
         reportEntries.serialize( data );
@@ -215,10 +224,10 @@ public class RunEntryStatisticsMapTest
     {
         File data = File.createTempFile( "surefire-unit", "test" );
         RunEntryStatisticsMap reportEntries = RunEntryStatisticsMap.fromFile( data );
-        reportEntries.add(
-                reportEntries.createNextGeneration( new SimpleReportEntry( "abc", null, "line1\nline2", null, 42 ) ) );
-        reportEntries.add(
-                reportEntries.createNextGeneration( new SimpleReportEntry( "abc", null, "test", null, 10 ) ) );
+        reportEntries.add( reportEntries.createNextGeneration( new SimpleReportEntry( NORMAL_RUN, 0L,
+                    "abc", null, "line1\nline2", null, 42 ) ) );
+        reportEntries.add( reportEntries.createNextGeneration( new SimpleReportEntry( NORMAL_RUN, 0L,
+            "abc", null, "test", null, 10 ) ) );
 
         reportEntries.serialize( data );
         try ( InputStream io = new FileInputStream( data ) )
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/report/ConsoleOutputFileReporterTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/report/ConsoleOutputFileReporterTest.java
index 6e044f7..6b0ee44 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/report/ConsoleOutputFileReporterTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/report/ConsoleOutputFileReporterTest.java
@@ -26,14 +26,15 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-import org.apache.maven.plugin.surefire.report.ConsoleOutputFileReporter;
-
 import junit.framework.TestCase;
+import org.apache.maven.plugin.surefire.report.ConsoleOutputFileReporter;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 import org.apache.maven.surefire.shared.utils.io.FileUtils;
 
 import static java.nio.charset.StandardCharsets.US_ASCII;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.apache.maven.surefire.api.report.TestOutputReportEntry.stdOut;
 import static org.apache.maven.surefire.api.report.TestOutputReportEntry.stdOutln;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -53,10 +54,10 @@ public class ConsoleOutputFileReporterTest
         //noinspection ResultOfMethodCallIgnored
         reportDir.mkdirs();
         TestSetReportEntry reportEntry =
-                new SimpleReportEntry( getClass().getName(), null, getClass().getName(), null );
+                new SimpleReportEntry( NORMAL_RUN, 1L, getClass().getName(), null, getClass().getName(), null );
         ConsoleOutputFileReporter reporter = new ConsoleOutputFileReporter( reportDir, null, false, null, "UTF-8" );
         reporter.testSetStarting( reportEntry );
-        reporter.writeTestOutput( stdOut( "some " ) );
+        reporter.writeTestOutput( (TestOutputReportEntry) stdOut( "some " ) );
         reporter.testSetCompleted( reportEntry );
         reporter.close();
 
@@ -80,7 +81,7 @@ public class ConsoleOutputFileReporterTest
         File reportDir = new File( new File( System.getProperty( "user.dir" ), "target" ), "tmp2" );
         String suffixText = "sampleSuffixText";
         TestSetReportEntry reportEntry =
-                new SimpleReportEntry( getClass().getName(), null, getClass().getName(), null );
+                new SimpleReportEntry( NORMAL_RUN, 1L, getClass().getName(), null, getClass().getName(), null );
         ConsoleOutputFileReporter reporter =
                 new ConsoleOutputFileReporter( reportDir, suffixText, false, null, "UTF-8" );
         reporter.testSetStarting( reportEntry );
@@ -108,8 +109,9 @@ public class ConsoleOutputFileReporterTest
     {
         File reportDir = new File( new File( System.getProperty( "user.dir" ), "target" ), "tmp3" );
         ConsoleOutputFileReporter reporter = new ConsoleOutputFileReporter( reportDir, null, false, null, "UTF-8" );
-        reporter.writeTestOutput( stdOut( "some text" ) );
-        reporter.testSetCompleted( new SimpleReportEntry( getClass().getName(), null, getClass().getName(), null ) );
+        reporter.writeTestOutput( (TestOutputReportEntry) stdOut( "some text" ) );
+        reporter.testSetCompleted(
+            new SimpleReportEntry( NORMAL_RUN, 1L, getClass().getName(), null, getClass().getName(), null ) );
         reporter.close();
 
         File expectedReportFile = new File( reportDir, "null-output.txt" );
@@ -129,7 +131,8 @@ public class ConsoleOutputFileReporterTest
         File reportDir = new File( new File( System.getProperty( "user.dir" ), "target" ), "tmp4" );
         final ConsoleOutputFileReporter reporter =
                 new ConsoleOutputFileReporter( reportDir, null, false, null, "UTF-8" );
-        reporter.testSetStarting( new SimpleReportEntry( getClass().getName(), null, getClass().getName(), null ) );
+        reporter.testSetStarting(
+            new SimpleReportEntry( NORMAL_RUN, 1L, getClass().getName(), null, getClass().getName(), null ) );
         ExecutorService scheduler = Executors.newFixedThreadPool( 10 );
         final ArrayList<Callable<Void>> jobs = new ArrayList<>();
         for ( int i = 0; i < 10; i++ )
@@ -139,7 +142,7 @@ public class ConsoleOutputFileReporterTest
                 @Override
                 public Void call()
                 {
-                    reporter.writeTestOutput( stdOut( "some text\n" ) );
+                    reporter.writeTestOutput( (TestOutputReportEntry) stdOut( "some text\n" ) );
                     return null;
                 }
             } );
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/report/FileReporterTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/report/FileReporterTest.java
index d064421..beecd50 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/report/FileReporterTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/report/FileReporterTest.java
@@ -22,15 +22,17 @@ package org.apache.maven.surefire.report;
 import java.io.File;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
+
+import junit.framework.TestCase;
 import org.apache.maven.plugin.surefire.report.FileReporter;
 import org.apache.maven.plugin.surefire.report.ReportEntryType;
 import org.apache.maven.plugin.surefire.report.TestSetStats;
 import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
-
-import junit.framework.TestCase;
 import org.apache.maven.surefire.api.report.ReportEntry;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
 
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
+
 /**
  *
  */
@@ -47,7 +49,7 @@ public class FileReporterTest
     public void testFileNameWithoutSuffix()
     {
         File reportDir = new File( "target" );
-        reportEntry = new SimpleReportEntry( getClass().getName(), null, TEST_NAME, null );
+        reportEntry = new SimpleReportEntry( NORMAL_RUN, 1L, getClass().getName(), null, TEST_NAME, null );
         WrappedReportEntry wrappedReportEntry =
             new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null );
         reporter = new FileReporter( reportDir, null, Charset.defaultCharset(), false, false, false );
@@ -69,7 +71,7 @@ public class FileReporterTest
     {
         File reportDir = new File( "target" );
         String suffixText = "sampleSuffixText";
-        reportEntry = new SimpleReportEntry( getClass().getName(), null, TEST_NAME, null );
+        reportEntry = new SimpleReportEntry( NORMAL_RUN, 1L, getClass().getName(), null, TEST_NAME, null );
         WrappedReportEntry wrappedReportEntry =
             new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null );
         reporter = new FileReporter( reportDir, suffixText, Charset.defaultCharset(), false, false, false );
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkingRunListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkingRunListener.java
index 70391ad..b987228 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkingRunListener.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/booter/ForkingRunListener.java
@@ -22,12 +22,8 @@ package org.apache.maven.surefire.api.booter;
 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 
-import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
-import static java.util.Objects.requireNonNull;
-
 /**
  * Encodes the full output of the test run to the "target".
  * <br>
@@ -46,13 +42,11 @@ import static java.util.Objects.requireNonNull;
  * @author Kristian Rosenvold
  */
 public class ForkingRunListener
-    implements TestReportListener
+    implements TestReportListener<TestOutputReportEntry>
 {
     private final MasterProcessChannelEncoder target;
     private final boolean trim;
 
-    private volatile RunMode runMode = NORMAL_RUN;
-
     public ForkingRunListener( MasterProcessChannelEncoder target, boolean trim )
     {
         this.target = target;
@@ -114,14 +108,6 @@ public class ForkingRunListener
     }
 
     @Override
-    public RunMode markAs( RunMode currentRunMode )
-    {
-        RunMode runMode = this.runMode;
-        this.runMode = requireNonNull( currentRunMode );
-        return runMode;
-    }
-
-    @Override
     public void writeTestOutput( TestOutputReportEntry reportEntry )
     {
         target.testOutput( reportEntry );
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/CategorizedReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/CategorizedReportEntry.java
index 34be63c..47b130d 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/CategorizedReportEntry.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/CategorizedReportEntry.java
@@ -19,6 +19,8 @@ package org.apache.maven.surefire.api.report;
  * under the License.
  */
 
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Objects;
@@ -35,42 +37,48 @@ public class CategorizedReportEntry
 
     private final String group;
 
-    public CategorizedReportEntry( String source, String name, String group )
+    public CategorizedReportEntry( @Nonnull RunMode runMode, @Nonnegative Long testRunId,
+                                   String source, String name, String group )
     {
-        this( source, name, group, null, null );
+        this( runMode, testRunId, source, name, group, null, null );
     }
 
-    public CategorizedReportEntry( String source, String name, String group, StackTraceWriter stackTraceWriter,
+    public CategorizedReportEntry( @Nonnull RunMode runMode, @Nonnegative Long testRunId,
+                                   String source, String name, String group, StackTraceWriter stackTraceWriter,
                                    Integer elapsed )
     {
-        super( source, null, name, null, stackTraceWriter, elapsed );
+        super( runMode, testRunId, source, null, name, null, stackTraceWriter, elapsed );
         this.group = group;
     }
 
-    public CategorizedReportEntry( String source, String name, String group, StackTraceWriter stackTraceWriter,
+    public CategorizedReportEntry( @Nonnull RunMode runMode, @Nonnegative Long testRunId,
+                                   String source, String name, String group, StackTraceWriter stackTraceWriter,
                                    Integer elapsed, String message )
     {
-        this( source, null, name, null,
+        this( runMode, testRunId, source, null, name, null,
                 group, stackTraceWriter, elapsed, message, Collections.<String, String>emptyMap() );
     }
 
-    public CategorizedReportEntry( String source, String sourceText, String name, String nameText,
+    public CategorizedReportEntry( @Nonnull RunMode runMode, @Nonnegative Long testRunId,
+                                   String source, String sourceText, String name, String nameText,
                                    String group, StackTraceWriter stackTraceWriter,
                                    Integer elapsed, String message, Map<String, String> systemProperties )
     {
-        super( source, sourceText, name, nameText, stackTraceWriter, elapsed, message, systemProperties );
+        super( runMode, testRunId, source, sourceText, name, nameText, stackTraceWriter, elapsed, message,
+            systemProperties );
         this.group = group;
     }
 
-    public static TestSetReportEntry reportEntry( String source, String sourceText, String name, String nameText,
+    public static TestSetReportEntry reportEntry( @Nonnull RunMode runMode, @Nonnegative Long testRunId,
+                                                  String source, String sourceText, String name, String nameText,
                                                   String group,
                                                   StackTraceWriter stackTraceWriter, Integer elapsed, String message,
                                                   Map<String, String> systemProperties )
     {
         return group != null
-            ? new CategorizedReportEntry( source, sourceText, name, nameText,
+            ? new CategorizedReportEntry( runMode, testRunId, source, sourceText, name, nameText,
                 group, stackTraceWriter, elapsed, message, systemProperties )
-            : new SimpleReportEntry( source, sourceText, name, nameText,
+            : new SimpleReportEntry( runMode, testRunId, source, sourceText, name, nameText,
                 stackTraceWriter, elapsed, message, systemProperties );
     }
 
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ConsoleOutputCapture.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ConsoleOutputCapture.java
index c00619f..095dab0 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ConsoleOutputCapture.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ConsoleOutputCapture.java
@@ -38,7 +38,7 @@ import static org.apache.maven.surefire.api.report.TestOutputReportEntry.stdOutl
  */
 public final class ConsoleOutputCapture
 {
-    public static void startCapture( TestOutputReceiver target )
+    public static void startCapture( TestOutputReceiver<OutputReportEntry> target )
     {
         setOut( new ForwardingPrintStream( true, target ) );
         setErr( new ForwardingPrintStream( false, target ) );
@@ -48,9 +48,9 @@ public final class ConsoleOutputCapture
         extends PrintStream
     {
         private final boolean isStdout;
-        private final TestOutputReceiver target;
+        private final TestOutputReceiver<OutputReportEntry> target;
 
-        ForwardingPrintStream( boolean stdout, TestOutputReceiver target )
+        ForwardingPrintStream( boolean stdout, TestOutputReceiver<OutputReportEntry> target )
         {
             super( new NullOutputStream() );
             isStdout = stdout;
@@ -64,7 +64,6 @@ public final class ConsoleOutputCapture
             target.writeTestOutput( isStdout ? stdOut( log ) : stdErr( log ) );
         }
 
-        @Override
         public void write( @Nonnull byte[] b )
             throws IOException
         {
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/OutputReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/OutputReportEntry.java
new file mode 100644
index 0000000..5dabbc7
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/OutputReportEntry.java
@@ -0,0 +1,45 @@
+package org.apache.maven.surefire.api.report;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Minimum data requirement for report entry.
+ * <br>
+ * Additionally, we should distinguish between two events ({@link OutputReportEntry}, {@link TestOutputReportEntry}).
+ * The interface {@link TestReportListener} handles these two events via generics depending on the situation.
+ * <br>
+ * The first situation happens when provider's listeners handles the event {@link OutputReportEntry} from
+ * <i>System.out</i> and <i>System.err</i> via the {@link ConsoleOutputCapture}. The {@link ConsoleOutputCapture} does
+ * not have any notion about {@link RunMode} and <code>testRunId</code>, and therefore the only provider's listener
+ * would add {@link RunMode} and <code>testRunId</code> to a recreated entry which would be finally propagated to the
+ * <code>ForkingRunListener</code> and <code>TestSetRunListener</code>. The {@link RunMode} and <code>testRunId</code>
+ * are determined upon the events test-started, test-finished and Thread local.
+ * <br>
+ * The second situation happens when <code>ForkingRunListener</code> and <code>TestSetRunListener</code> handles
+ * {@link TestOutputReportEntry} which contains {@link RunMode} and <code>testRunId</code>.
+ */
+public interface OutputReportEntry
+{
+    String getLog();
+
+    boolean isStdOut();
+
+    boolean isNewLine();
+}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReportEntry.java
index b565ffa..ed3fdc5 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReportEntry.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReportEntry.java
@@ -19,6 +19,8 @@ package org.apache.maven.surefire.api.report;
  * under the License.
  */
 
+import javax.annotation.Nonnull;
+
 /**
  * Describes a single entry for a test report
  *
@@ -106,4 +108,19 @@ public interface ReportEntry
      * @return A string with the test case text and group/category, or just the source text.
      */
     String getReportNameWithGroup();
+
+    /**
+     * Run mode.
+     *
+     * @return a normal run, or re-run.
+     */
+    @Nonnull
+    RunMode getRunMode();
+
+    /**
+     * This represents a reference pointing to a literal representation of test description or literal unique id.
+     *
+     * @return id
+     */
+    Long getTestRunId();
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReporterFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReporterFactory.java
index 81b2318..52a434c 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReporterFactory.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReporterFactory.java
@@ -33,7 +33,7 @@ public interface ReporterFactory
      *
      * @return new reporter listener instance
      */
-    TestReportListener createTestReportListener();
+    TestReportListener<TestOutputReportEntry> createTestReportListener();
 
     /**
      * Closes the factory, freeing resources allocated in the factory.
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/RunListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/RunListener.java
index 659498e..acb8e43 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/RunListener.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/RunListener.java
@@ -95,13 +95,4 @@ public interface RunListener
      * (The event is fired after the Nth test failed to signal skipping the rest of test-set.)
      */
     void testExecutionSkippedByUser();
-
-    /**
-     * Marks the listener with run mode, e.g. normal run or re-run.
-     *
-     * @param currentRunMode    set current run
-     * @return previous run mode; never returns null
-     * @throws NullPointerException if <code>currentRunMode</code> is null
-     */
-    RunMode markAs( RunMode currentRunMode );
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/SimpleReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/SimpleReportEntry.java
index 5e324ad..8862eff 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/SimpleReportEntry.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/SimpleReportEntry.java
@@ -21,6 +21,7 @@ package org.apache.maven.surefire.api.report;
 
 import org.apache.maven.surefire.api.util.internal.ImmutableMap;
 
+import javax.annotation.Nonnull;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Objects;
@@ -33,6 +34,10 @@ import java.util.Objects;
 public class SimpleReportEntry
     implements TestSetReportEntry
 {
+    private final RunMode runMode;
+
+    private final Long testRunId;
+
     private final Map<String, String> systemProperties;
 
     private final String source;
@@ -49,37 +54,46 @@ public class SimpleReportEntry
 
     private final String message;
 
-    public SimpleReportEntry( String source, String sourceText, String name, String nameText )
+    public SimpleReportEntry( @Nonnull RunMode runMode, Long testRunId,
+                              String source, String sourceText, String name, String nameText )
     {
-        this( source, sourceText, name, nameText, null, null );
+        this( runMode, testRunId, source, sourceText, name, nameText, null, null );
     }
 
-    public SimpleReportEntry( String source, String sourceText, String name, String nameText,
+    public SimpleReportEntry( @Nonnull RunMode runMode, Long testRunId,
+                              String source, String sourceText, String name, String nameText,
                               Map<String, String> systemProperties )
     {
-        this( source, sourceText, name, nameText, null, null, systemProperties );
+        this( runMode, testRunId, source, sourceText, name, nameText, null, null, systemProperties );
     }
 
-    private SimpleReportEntry( String source, String sourceText, String name, String nameText,
+    private SimpleReportEntry( @Nonnull RunMode runMode, Long testRunId,
+                               String source, String sourceText, String name, String nameText,
                                StackTraceWriter stackTraceWriter )
     {
-        this( source, sourceText, name, nameText, stackTraceWriter, null );
+        this( runMode, testRunId, source, sourceText, name, nameText, stackTraceWriter, null );
     }
 
-    public SimpleReportEntry( String source, String sourceText, String name, String nameText, Integer elapsed )
+    public SimpleReportEntry( @Nonnull RunMode runMode, Long testRunId,
+                              String source, String sourceText, String name, String nameText, Integer elapsed )
     {
-        this( source, sourceText, name, nameText, null, elapsed );
+        this( runMode, testRunId, source, sourceText, name, nameText, null, elapsed );
     }
 
-    public SimpleReportEntry( String source, String sourceText, String name, String nameText, String message )
+    public SimpleReportEntry( @Nonnull RunMode runMode, Long testRunId,
+                              String source, String sourceText, String name, String nameText, String message )
     {
-        this( source, sourceText, name, nameText, null, null, message, Collections.<String, String>emptyMap() );
+        this( runMode, testRunId,
+            source, sourceText, name, nameText, null, null, message, Collections.<String, String>emptyMap() );
     }
 
-    public SimpleReportEntry( String source, String sourceText, String name, String nameText,
-                                 StackTraceWriter stackTraceWriter, Integer elapsed, String message,
-                                 Map<String, String> systemProperties )
+    public SimpleReportEntry( @Nonnull RunMode runMode, Long testRunId,
+                              String source, String sourceText, String name, String nameText,
+                              StackTraceWriter stackTraceWriter, Integer elapsed, String message,
+                              Map<String, String> systemProperties )
     {
+        this.runMode = runMode;
+        this.testRunId = testRunId;
         this.source = source;
         this.sourceText = sourceText;
         this.name = name;
@@ -90,35 +104,39 @@ public class SimpleReportEntry
         this.systemProperties = new ImmutableMap<>( systemProperties );
     }
 
-    public SimpleReportEntry( String source, String sourceText, String name, String nameText,
-                              StackTraceWriter stackTraceWriter, Integer elapsed )
+    public SimpleReportEntry( @Nonnull RunMode runMode, Long testRunId,
+                               String source, String sourceText, String name, String nameText,
+                               StackTraceWriter stackTraceWriter, Integer elapsed )
     {
-        this( source, sourceText, name, nameText, stackTraceWriter, elapsed, Collections.<String, String>emptyMap() );
+        this( runMode, testRunId,
+            source, sourceText, name, nameText, stackTraceWriter, elapsed, Collections.<String, String>emptyMap() );
     }
 
-    public SimpleReportEntry( String source, String sourceText, String name, String nameText,
+    public SimpleReportEntry( @Nonnull RunMode runMode, Long testRunId,
+                              String source, String sourceText, String name, String nameText,
                               StackTraceWriter stackTraceWriter, Integer elapsed, Map<String, String> systemProperties )
     {
-        this( source, sourceText, name, nameText,
-                stackTraceWriter, elapsed, safeGetMessage( stackTraceWriter ), systemProperties );
+        this( runMode, testRunId, source, sourceText, name, nameText,
+            stackTraceWriter, elapsed, safeGetMessage( stackTraceWriter ), systemProperties );
     }
 
-    public static SimpleReportEntry assumption( String source, String sourceText, String name, String nameText,
-                                                String message )
+    public static SimpleReportEntry assumption( RunMode runMode, Long testRunId, String source,
+                                                String sourceText, String name, String nameText, String message )
     {
-        return new SimpleReportEntry( source, sourceText, name, nameText, message );
+        return new SimpleReportEntry( runMode, testRunId, source, sourceText, name, nameText, message );
     }
 
-    public static SimpleReportEntry ignored( String source, String sourceText, String name, String nameText,
-                                             String message )
+    public static SimpleReportEntry ignored( RunMode runMode, Long testRunId, String source,
+                                             String sourceText, String name, String nameText, String message )
     {
-        return new SimpleReportEntry( source, sourceText, name, nameText, message );
+        return new SimpleReportEntry( runMode, testRunId, source, sourceText, name, nameText, message );
     }
 
-    public static SimpleReportEntry withException( String source, String sourceText, String name, String nameText,
+    public static SimpleReportEntry withException( RunMode runMode, Long testRunId, String source,
+                                                   String sourceText, String name, String nameText,
                                                    StackTraceWriter stackTraceWriter )
     {
-        return new SimpleReportEntry( source, sourceText, name, nameText, stackTraceWriter );
+        return new SimpleReportEntry( runMode, testRunId, source, sourceText, name, nameText, stackTraceWriter );
     }
 
     private static String safeGetMessage( StackTraceWriter stackTraceWriter )
@@ -185,9 +203,10 @@ public class SimpleReportEntry
     @Override
     public String toString()
     {
-        return "ReportEntry{" + "source='" + source + "', sourceText='" + sourceText
-                + "', name='" + name + "', nameText='" + nameText + "', stackTraceWriter='"
-                + stackTraceWriter + "', elapsed='" + elapsed + "', message='" + message + "'}";
+        return "ReportEntry{" + "runMode='" + runMode + "', testRunId='" + testRunId
+            + "', source='" + source + "', sourceText='" + sourceText
+            + "', name='" + name + "', nameText='" + nameText + "', stackTraceWriter='" + stackTraceWriter
+            + "', elapsed='" + elapsed + "', message='" + message + "'}";
     }
 
     @Override
@@ -209,7 +228,8 @@ public class SimpleReportEntry
         }
 
         SimpleReportEntry that = (SimpleReportEntry) o;
-        return isSourceEqual( that ) && isSourceTextEqual( that )
+        return isRunModeEqual( that ) && isTestRunIdEqual( that )
+                && isSourceEqual( that ) && isSourceTextEqual( that )
                 && isNameEqual( that ) && isNameTextEqual( that )
                 && isStackEqual( that )
                 && isElapsedTimeEqual( that )
@@ -221,6 +241,8 @@ public class SimpleReportEntry
     public int hashCode()
     {
         int result = Objects.hashCode( getSourceName() );
+        result = 31 * result + Objects.hashCode( getRunMode() );
+        result = 31 * result + Objects.hashCode( getTestRunId() );
         result = 31 * result + Objects.hashCode( getSourceText() );
         result = 31 * result + Objects.hashCode( getName() );
         result = 31 * result + Objects.hashCode( getNameText() );
@@ -244,11 +266,34 @@ public class SimpleReportEntry
     }
 
     @Override
+    @Nonnull
+    public final RunMode getRunMode()
+    {
+        return runMode;
+    }
+
+    @Override
+    public final Long getTestRunId()
+    {
+        return testRunId;
+    }
+
+    @Override
     public Map<String, String> getSystemProperties()
     {
         return systemProperties;
     }
 
+    private boolean isRunModeEqual( SimpleReportEntry en )
+    {
+        return Objects.equals( getRunMode(), en.getRunMode() );
+    }
+
+    private boolean isTestRunIdEqual( SimpleReportEntry en )
+    {
+        return Objects.equals( getTestRunId(), en.getTestRunId() );
+    }
+
     private boolean isElapsedTimeEqual( SimpleReportEntry en )
     {
         return Objects.equals( getElapsed(), en.getElapsed() );
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReceiver.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReceiver.java
index f77f9d4..4f3d0c5 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReceiver.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReceiver.java
@@ -22,8 +22,10 @@ package org.apache.maven.surefire.api.report;
 /**
  * A receiver of stdout/sterr output from running tests. This receiver knows how to associate
  * the output with a given testset.
+ *
+ * @param <T> usually {@link TestOutputReportEntry} or {@link OutputReportEntry}
  */
-public interface TestOutputReceiver
+public interface TestOutputReceiver<T extends OutputReportEntry>
 {
 
     /**
@@ -31,6 +33,6 @@ public interface TestOutputReceiver
      *
      * @param reportEntry wraps test output with descriptive information of the output
      */
-    void writeTestOutput( TestOutputReportEntry reportEntry );
+    void writeTestOutput( T reportEntry );
 
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReportEntry.java
index 9e8fb6e..3610f85 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReportEntry.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReportEntry.java
@@ -20,9 +20,11 @@ package org.apache.maven.surefire.api.report;
  */
 
 /**
- * This report entry should be used in {@link TestOutputReceiver#writeTestOutput(TestOutputReportEntry)}.
+ * This report entry should be used in {@link TestOutputReceiver#writeTestOutput(OutputReportEntry)}.
+ *
+ * {@inheritDoc}
  */
-public final class TestOutputReportEntry
+public final class TestOutputReportEntry implements OutputReportEntry
 {
     private final String log;
     private final boolean isStdOut;
@@ -60,7 +62,7 @@ public final class TestOutputReportEntry
         this( log, isStdOut, newLine, null, null );
     }
 
-    public TestOutputReportEntry( TestOutputReportEntry reportEntry, RunMode runMode, long testRunId )
+    public TestOutputReportEntry( OutputReportEntry reportEntry, RunMode runMode, Long testRunId )
     {
         log = reportEntry.getLog();
         isStdOut = reportEntry.isStdOut();
@@ -69,16 +71,19 @@ public final class TestOutputReportEntry
         this.testRunId = testRunId;
     }
 
+    @Override
     public String getLog()
     {
         return log;
     }
 
+    @Override
     public boolean isStdOut()
     {
         return isStdOut;
     }
 
+    @Override
     public boolean isNewLine()
     {
         return newLine;
@@ -94,7 +99,7 @@ public final class TestOutputReportEntry
         return testRunId;
     }
 
-    public static TestOutputReportEntry stdOut( String log )
+    public static OutputReportEntry stdOut( String log )
     {
         return new TestOutputReportEntry( log, true, false );
     }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestReportListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestReportListener.java
index 420de84..120361e 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestReportListener.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestReportListener.java
@@ -38,8 +38,10 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
  * <br>
  * Note: The adapters in the module <i>surefire-junit47</i> are temporal and will be removed after we have fixed
  * the SUREFIRE-1860 and XML reporter in SUREFIRE-1643. The adapters are a workaround of a real fix in both Jira issues.
+ *
+ * @param <T> usually {@link TestOutputReportEntry} or {@link OutputReportEntry}
  */
-public interface TestReportListener
-    extends RunListener, TestOutputReceiver, ConsoleLogger
+public interface TestReportListener<T extends OutputReportEntry>
+    extends RunListener, TestOutputReceiver<T>, ConsoleLogger
 {
 }
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 98c4b7d..15965e4 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
@@ -23,6 +23,7 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerUtils;
 import org.apache.maven.surefire.api.report.ReportEntry;
 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.WritableBufferedByteChannel;
 import org.junit.Test;
@@ -928,7 +929,7 @@ public class EventChannelEncoderTest
     public void testSendOpcode()
     {
         Channel channel = new Channel();
-        new EventChannelEncoder( channel ).testOutput( stdOut( "msg" ) );
+        new EventChannelEncoder( channel ).testOutput( (TestOutputReportEntry) stdOut( "msg" ) );
         assertThat( toString( channel.src ) )
                 .isEqualTo( ":maven-surefire-event:" + (char) 14 + ":std-out-stream:" + (char) 10 + ":normal-run:"
                     + (char) 5 + ":UTF-8:\u0000\u0000\u0000\u0003:msg:" );
@@ -1114,7 +1115,7 @@ public class EventChannelEncoderTest
         WritableBufferedByteChannel channel = newBufferedChannel( out );
         EventChannelEncoder encoder = new EventChannelEncoder( channel );
 
-        encoder.testOutput( stdOut( "msg" ) );
+        encoder.testOutput( (TestOutputReportEntry) stdOut( "msg" ) );
         channel.close();
 
         String expected = ":maven-surefire-event:\u000e:std-out-stream:"
@@ -1219,7 +1220,7 @@ public class EventChannelEncoderTest
         Thread.currentThread().interrupt();
         try
         {
-            encoder.testOutput( stdOut( "msg" ) );
+            encoder.testOutput( (TestOutputReportEntry) stdOut( "msg" ) );
             channel.close();
         }
         finally
diff --git a/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/ClassMethodIndexer.java b/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/ClassMethodIndexer.java
new file mode 100644
index 0000000..8e58d7a
--- /dev/null
+++ b/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/ClassMethodIndexer.java
@@ -0,0 +1,64 @@
+package org.apache.maven.surefire.report;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.api.util.internal.ClassMethod;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Creates an index for class/method.
+ * Returns ThreadLocal index if created before.
+ */
+public final class ClassMethodIndexer
+{
+    private final AtomicInteger classIndex = new AtomicInteger( 1 );
+    private final AtomicInteger methodIndex = new AtomicInteger( 1 );
+    private final Map<ClassMethod, Long> testIdMapping = new ConcurrentHashMap<>();
+    private final ThreadLocal<Long> testLocalMapping = new ThreadLocal<>();
+
+    public long indexClassMethod( String clazz, String method )
+    {
+        ClassMethod key = new ClassMethod( requireNonNull( clazz ), method );
+        return testIdMapping.computeIfAbsent( key, cm ->
+        {
+            Long classId = testIdMapping.get( new ClassMethod( requireNonNull( clazz ), null ) );
+            long c = classId == null ? ( ( (long) classIndex.getAndIncrement() ) << 32 ) : classId;
+            int m = method == null ? 0 : methodIndex.getAndIncrement();
+            long id = c | m;
+            testLocalMapping.set( id );
+            return id;
+        } );
+    }
+
+    public long indexClass( String clazz )
+    {
+        return indexClassMethod( clazz, null );
+    }
+
+    public Long getLocalIndex()
+    {
+        return testLocalMapping.get();
+    }
+}
diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/SurefireTestSetExecutor.java b/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/RunModeSetter.java
similarity index 69%
copy from surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/SurefireTestSetExecutor.java
copy to surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/RunModeSetter.java
index e192081..5836a3d 100644
--- a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/SurefireTestSetExecutor.java
+++ b/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/RunModeSetter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.junit;
+package org.apache.maven.surefire.report;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,15 +19,14 @@ package org.apache.maven.surefire.junit;
  * under the License.
  */
 
-import org.apache.maven.surefire.api.report.RunListener;
-import org.apache.maven.surefire.api.testset.TestSetFailedException;
+import org.apache.maven.surefire.api.report.RunMode;
 
 /**
- * Describes a single test set
+ * Sets the run mode in a listener, reflects the run mode of testset execution.
  *
+ * @since 3.0.0-M6
  */
-public interface SurefireTestSetExecutor
+public interface RunModeSetter
 {
-    void execute( Class<?> testClass, RunListener reportManager, ClassLoader loader )
-        throws TestSetFailedException;
+    void setRunMode( RunMode runMode );
 }
diff --git a/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/ClassMethodIndexerTest.java b/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/ClassMethodIndexerTest.java
new file mode 100644
index 0000000..bef648d
--- /dev/null
+++ b/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/ClassMethodIndexerTest.java
@@ -0,0 +1,75 @@
+package org.apache.maven.surefire.report;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import junit.framework.TestCase;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ *
+ */
+@SuppressWarnings( "checkstyle:magicnumber" )
+public class ClassMethodIndexerTest
+    extends TestCase
+{
+    public void testNPE()
+    {
+        ClassMethodIndexer indexer = new ClassMethodIndexer();
+        try
+        {
+            indexer.indexClass( null );
+            fail( "NPE expected" );
+        }
+        catch ( NullPointerException e )
+        {
+            // expected
+        }
+    }
+
+    public void testClass()
+    {
+        ClassMethodIndexer indexer = new ClassMethodIndexer();
+        long index = indexer.indexClass( getClass().getName() );
+        assertThat( index )
+            .isEqualTo( 0x0000000100000000L );
+    }
+
+    public void testClassMethod()
+    {
+        ClassMethodIndexer indexer = new ClassMethodIndexer();
+        long index = indexer.indexClassMethod( getClass().getName(), "methodName" );
+        assertThat( index )
+            .isEqualTo( 0x0000000100000001L );
+    }
+
+    public void testRun()
+    {
+        ClassMethodIndexer indexer = new ClassMethodIndexer();
+        long index = indexer.indexClass( getClass().getName() );
+        indexer.indexClass( "dummy" );
+        assertThat( index )
+            .isEqualTo( 0x0000000100000000L );
+        index = indexer.indexClassMethod( getClass().getName(), "methodName" );
+        assertThat( index )
+            .isEqualTo( 0x0000000100000001L );
+
+    }
+}
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4RunListener.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4RunListener.java
index f596810..b9890ac 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4RunListener.java
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4RunListener.java
@@ -20,7 +20,9 @@ package org.apache.maven.surefire.common.junit4;
  */
 
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.api.report.OutputReportEntry;
 import org.apache.maven.surefire.api.report.ReportEntry;
+import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
 import org.apache.maven.surefire.api.report.StackTraceWriter;
 import org.apache.maven.surefire.api.report.TestOutputReceiver;
@@ -28,6 +30,8 @@ import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
 import org.apache.maven.surefire.api.util.internal.ClassMethod;
+import org.apache.maven.surefire.report.ClassMethodIndexer;
+import org.apache.maven.surefire.report.RunModeSetter;
 import org.junit.runner.Description;
 import org.junit.runner.Result;
 import org.junit.runner.notification.Failure;
@@ -46,9 +50,11 @@ import static org.apache.maven.surefire.api.report.SimpleReportEntry.withExcepti
  */
 public class JUnit4RunListener
     extends RunListener
-    implements TestOutputReceiver
+    implements TestOutputReceiver<OutputReportEntry>, RunModeSetter
 {
-    protected final TestReportListener reporter;
+    protected final ClassMethodIndexer classMethodIndexer = new ClassMethodIndexer();
+    protected final TestReportListener<TestOutputReportEntry> reporter;
+    private volatile RunMode runMode;
 
     /**
      * This flag is set after a failure has occurred so that a
@@ -64,7 +70,7 @@ public class JUnit4RunListener
      *
      * @param reporter the reporter to log testing events to
      */
-    public JUnit4RunListener( TestReportListener reporter )
+    public JUnit4RunListener( TestReportListener<TestOutputReportEntry> reporter )
     {
         this.reporter = reporter;
     }
@@ -74,6 +80,17 @@ public class JUnit4RunListener
         return reporter;
     }
 
+    @Override
+    public void setRunMode( RunMode runMode )
+    {
+        this.runMode = runMode;
+    }
+
+    protected final RunMode getRunMode()
+    {
+        return runMode;
+    }
+
     // Testrun methods are not invoked when using the runner
 
     /**
@@ -87,7 +104,9 @@ public class JUnit4RunListener
     {
         String reason = getAnnotatedIgnoreValue( description );
         ClassMethod classMethod = toClassMethod( description );
-        reporter.testSkipped( ignored( classMethod.getClazz(), null, classMethod.getMethod(), null, reason ) );
+        long testRunId = classMethodIndexer.indexClassMethod( classMethod.getClazz(), classMethod.getMethod() );
+        reporter.testSkipped( ignored( runMode, testRunId, classMethod.getClazz(), null,
+            classMethod.getMethod(), null, reason ) );
     }
 
     /**
@@ -123,8 +142,9 @@ public class JUnit4RunListener
         {
             StackTraceWriter stackTrace = createStackTraceWriter( failure );
             ClassMethod classMethod = toClassMethod( failure.getDescription() );
-            ReportEntry report =
-                    withException( classMethod.getClazz(), null, classMethod.getMethod(), null, stackTrace );
+            long testRunId = classMethodIndexer.indexClassMethod( classMethod.getClazz(), classMethod.getMethod() );
+            ReportEntry report = withException( runMode, testRunId, classMethod.getClazz(), null,
+                classMethod.getMethod(), null, stackTrace );
 
             if ( failure.getException() instanceof AssertionError )
             {
@@ -147,8 +167,9 @@ public class JUnit4RunListener
         {
             Description desc = failure.getDescription();
             ClassMethod classMethod = toClassMethod( desc );
-            ReportEntry report = assumption( classMethod.getClazz(), null, classMethod.getMethod(), null,
-                    failure.getMessage() );
+            long testRunId = classMethodIndexer.indexClassMethod( classMethod.getClazz(), classMethod.getMethod() );
+            ReportEntry report = assumption( runMode, testRunId, classMethod.getClazz(), null,
+                classMethod.getMethod(), null, failure.getMessage() );
             reporter.testAssumptionFailure( report );
         }
         finally
@@ -189,7 +210,9 @@ public class JUnit4RunListener
     protected SimpleReportEntry createReportEntry( Description description )
     {
         ClassMethod classMethod = toClassMethod( description );
-        return new SimpleReportEntry( classMethod.getClazz(), null, classMethod.getMethod(), null );
+        long testRunId = classMethodIndexer.indexClassMethod( classMethod.getClazz(), classMethod.getMethod() );
+        return new SimpleReportEntry( runMode, testRunId, classMethod.getClazz(), null,
+            classMethod.getMethod(), null );
     }
 
     public static void rethrowAnyTestMechanismFailures( Result run )
@@ -206,8 +229,9 @@ public class JUnit4RunListener
     }
 
     @Override
-    public void writeTestOutput( TestOutputReportEntry reportEntry )
+    public void writeTestOutput( OutputReportEntry reportEntry )
     {
-        reporter.writeTestOutput( new TestOutputReportEntry( reportEntry, /*todo*/ null, 0L ) );
+        Long testRunId = classMethodIndexer.getLocalIndex();
+        reporter.writeTestOutput( new TestOutputReportEntry( reportEntry, runMode, testRunId ) );
     }
 }
diff --git a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/MockReporter.java b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/MockReporter.java
index 21ba876..bce301c 100644
--- a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/MockReporter.java
+++ b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/MockReporter.java
@@ -19,21 +19,20 @@ package org.apache.maven.surefire.common.junit4;
  * under the License.
  */
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.maven.surefire.api.report.ReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
 /**
  * Internal tests use only.
  */
 final class MockReporter
-        implements TestReportListener
+        implements TestReportListener<TestOutputReportEntry>
 {
     private final List<String> events = new ArrayList<>();
 
@@ -96,12 +95,6 @@ final class MockReporter
     {
     }
 
-    @Override
-    public RunMode markAs( RunMode currentRunMode )
-    {
-        return null;
-    }
-
     public int getTestSucceeded()
     {
         return testSucceeded.get();
diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
index 5749e91..2b124c6 100644
--- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
@@ -30,6 +30,8 @@ import static org.apache.maven.surefire.api.booter.ProviderParameterNames.INCLUD
 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP;
 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.TESTNG_GROUPS_PROP;
 import static org.apache.maven.surefire.api.report.ConsoleOutputCapture.startCapture;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
+import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAILURE;
 import static org.apache.maven.surefire.api.util.TestsToRun.fromClass;
 import static org.apache.maven.surefire.shared.utils.StringUtils.isBlank;
 import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
@@ -167,6 +169,7 @@ public class JUnitPlatformProvider
 
     private void invokeAllTests( TestsToRun testsToRun, RunListenerAdapter adapter )
     {
+        adapter.setRunMode( NORMAL_RUN );
         try
         {
             execute( testsToRun, adapter );
@@ -179,6 +182,7 @@ public class JUnitPlatformProvider
         int count = parameters.getTestRequest().getRerunFailingTestsCount();
         if ( count > 0 && adapter.hasFailingTests() )
         {
+            adapter.setRunMode( RERUN_TEST_AFTER_FAILURE );
             for ( int i = 0; i < count; i++ )
             {
                 try
diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java
index 2a8a0d7..aeb2457 100644
--- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java
@@ -24,7 +24,6 @@ import static java.util.stream.Collectors.joining;
 import static org.apache.maven.surefire.api.util.internal.ObjectUtils.systemProps;
 import static org.apache.maven.surefire.shared.lang3.StringUtils.isNotBlank;
 import static org.junit.platform.engine.TestExecutionResult.Status.FAILED;
-import static org.apache.maven.surefire.shared.lang3.StringUtils.isBlank;
 
 import java.util.Map;
 import java.util.Objects;
@@ -34,13 +33,17 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.regex.Pattern;
 import java.util.stream.Stream;
 
+import org.apache.maven.surefire.api.report.OutputReportEntry;
+import org.apache.maven.surefire.api.report.RunMode;
+import org.apache.maven.surefire.api.report.TestOutputReceiver;
 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
+import org.apache.maven.surefire.report.ClassMethodIndexer;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.report.PojoStackTraceWriter;
+import org.apache.maven.surefire.report.RunModeSetter;
 import org.apache.maven.surefire.api.report.SafeThrowable;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
 import org.apache.maven.surefire.api.report.StackTraceWriter;
-import org.apache.maven.surefire.api.report.TestOutputReceiver;
 import org.junit.platform.engine.TestExecutionResult;
 import org.junit.platform.engine.TestSource;
 import org.junit.platform.engine.support.descriptor.ClassSource;
@@ -53,22 +56,30 @@ import org.junit.platform.launcher.TestPlan;
  * @since 2.22.0
  */
 final class RunListenerAdapter
-    implements TestExecutionListener, TestOutputReceiver
+    implements TestExecutionListener, TestOutputReceiver<OutputReportEntry>, RunModeSetter
 {
     private static final Pattern COMMA_PATTERN = Pattern.compile( "," );
 
+    private final ClassMethodIndexer classMethodIndexer = new ClassMethodIndexer();
     private final ConcurrentMap<TestIdentifier, Long> testStartTime = new ConcurrentHashMap<>();
     private final ConcurrentMap<TestIdentifier, TestExecutionResult> failures = new ConcurrentHashMap<>();
     private final ConcurrentMap<String, TestIdentifier> runningTestIdentifiersByUniqueId = new ConcurrentHashMap<>();
-    private final TestReportListener runListener;
+    private final TestReportListener<TestOutputReportEntry> runListener;
     private volatile TestPlan testPlan;
+    private volatile RunMode runMode;
 
-    RunListenerAdapter( TestReportListener runListener )
+    RunListenerAdapter( TestReportListener<TestOutputReportEntry> runListener )
     {
         this.runListener = runListener;
     }
 
     @Override
+    public void setRunMode( RunMode runMode )
+    {
+        this.runMode = runMode;
+    }
+
+    @Override
     public void testPlanExecutionStarted( TestPlan testPlan )
     {
         this.testPlan = testPlan;
@@ -225,8 +236,8 @@ final class RunListenerAdapter
         }
         StackTraceWriter stw =
                 testExecutionResult == null ? null : toStackTraceWriter( className, methodName, testExecutionResult );
-        return new SimpleReportEntry( className, classText, methodName, methodText,
-                stw, elapsedTime, reason, systemProperties );
+        return new SimpleReportEntry( runMode, classMethodIndexer.indexClassMethod( className, methodName ), className,
+            classText, methodName, methodText, stw, elapsedTime, reason, systemProperties );
     }
 
     private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier )
@@ -300,7 +311,7 @@ final class RunListenerAdapter
                     .map( TestIdentifier::getDisplayName )
                     .collect( joining( " " ) );
 
-            boolean needsSpaceSeparator = !isBlank( parentDisplay ) && !display.startsWith( "[" );
+            boolean needsSpaceSeparator = isNotBlank( parentDisplay ) && !display.startsWith( "[" );
             String methodDisplay = parentDisplay + ( needsSpaceSeparator ? " " : "" ) + display;
 
             String simpleClassNames = COMMA_PATTERN.splitAsStream( methodSource.getMethodParameterTypes() )
@@ -362,8 +373,9 @@ final class RunListenerAdapter
     }
 
     @Override
-    public void writeTestOutput( TestOutputReportEntry reportEntry )
+    public void writeTestOutput( OutputReportEntry reportEntry )
     {
-        runListener.writeTestOutput( new TestOutputReportEntry( reportEntry, /*todo*/ null, 0L ) );
+        Long testRunId = classMethodIndexer.getLocalIndex();
+        runListener.writeTestOutput( new TestOutputReportEntry( reportEntry, runMode, testRunId ) );
     }
 }
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTest.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTest.java
index 8649ea1..c0b5e7b 100644
--- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTest.java
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTest.java
@@ -26,6 +26,7 @@ import static org.apache.maven.surefire.api.booter.ProviderParameterNames.TESTNG
 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP;
 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.INCLUDE_JUNIT5_ENGINES_PROP;
 import static org.apache.maven.surefire.api.booter.ProviderParameterNames.EXCLUDE_JUNIT5_ENGINES_PROP;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -96,12 +97,13 @@ public class JUnitPlatformProviderTest
     {
         Launcher launcher = LauncherFactory.create();
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParametersMock(), launcher );
-        TestReportListener listener = mock( TestReportListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
 
         ArgumentCaptor<ReportEntry> testCaptor = ArgumentCaptor.forClass( ReportEntry.class );
         ArgumentCaptor<TestSetReportEntry> testSetCaptor = ArgumentCaptor.forClass( TestSetReportEntry.class );
 
         RunListenerAdapter adapter = new RunListenerAdapter( listener );
+        adapter.setRunMode( NORMAL_RUN );
         launcher.registerTestExecutionListeners( adapter );
 
         TestsToRun testsToRun = newTestsToRun( FailingBeforeAllJupiterTest.class );
@@ -143,12 +145,13 @@ public class JUnitPlatformProviderTest
     {
         Launcher launcher = LauncherFactory.create();
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParametersMock(), launcher );
-        TestReportListener listener = mock( TestReportListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
 
         ArgumentCaptor<ReportEntry> testCaptor = ArgumentCaptor.forClass( ReportEntry.class );
         ArgumentCaptor<TestSetReportEntry> testSetCaptor = ArgumentCaptor.forClass( TestSetReportEntry.class );
 
         RunListenerAdapter adapter = new RunListenerAdapter( listener );
+        adapter.setRunMode( NORMAL_RUN );
         launcher.registerTestExecutionListeners( adapter );
 
         TestsToRun testsToRun = newTestsToRun( FailingWithErrorBeforeAllJupiterTest.class );
@@ -427,9 +430,10 @@ public class JUnitPlatformProviderTest
         ProviderParameters parameters = providerParametersMock();
         JUnitPlatformProvider provider = new JUnitPlatformProvider( parameters, launcher );
 
-        TestReportListener listener = mock( TestReportListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
         RunListenerAdapter adapter = new RunListenerAdapter( listener );
+        adapter.setRunMode( NORMAL_RUN );
 
         launcher.registerTestExecutionListeners( adapter );
 
@@ -457,9 +461,10 @@ public class JUnitPlatformProviderTest
 
         TestPlanSummaryListener executionListener = new TestPlanSummaryListener();
 
-        TestReportListener listener = mock( TestReportListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
         RunListenerAdapter adapter = new RunListenerAdapter( listener );
+        adapter.setRunMode( NORMAL_RUN );
 
         launcher.registerTestExecutionListeners( executionListener, adapter );
 
@@ -495,9 +500,10 @@ public class JUnitPlatformProviderTest
 
         TestPlanSummaryListener executionListener = new TestPlanSummaryListener();
 
-        TestReportListener listener = mock( TestReportListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
         RunListenerAdapter adapter = new RunListenerAdapter( listener );
+        adapter.setRunMode( NORMAL_RUN );
 
         launcher.registerTestExecutionListeners( executionListener, adapter );
 
@@ -538,9 +544,10 @@ public class JUnitPlatformProviderTest
 
         TestPlanSummaryListener executionListener = new TestPlanSummaryListener();
 
-        TestReportListener listener = mock( TestReportListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
         RunListenerAdapter adapter = new RunListenerAdapter( listener );
+        adapter.setRunMode( NORMAL_RUN );
 
         launcher.registerTestExecutionListeners( executionListener, adapter );
 
@@ -598,7 +605,7 @@ public class JUnitPlatformProviderTest
     public void allDiscoveredTestsAreInvokedForNullArgument()
                     throws Exception
     {
-        TestReportListener runListener = runListenerMock();
+        TestReportListener<TestOutputReportEntry> runListener = runListenerMock();
         ProviderParameters providerParameters =
                         providerParametersMock( runListener, TestClass1.class, TestClass2.class );
         Launcher launcher = LauncherFactory.create();
@@ -662,7 +669,7 @@ public class JUnitPlatformProviderTest
                     throws Exception
     {
         Launcher launcher = LauncherFactory.create();
-        TestReportListener runListener = runListenerMock();
+        TestReportListener<TestOutputReportEntry> runListener = runListenerMock();
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParametersMock( runListener ), launcher );
 
         invokeProvider( provider, VerboseTestClass.class );
@@ -985,7 +992,7 @@ public class JUnitPlatformProviderTest
     }
 
     private static ProviderParameters providerParametersMock(
-            TestReportListener runListener, Class<?>... testClasses )
+            TestReportListener<TestOutputReportEntry> runListener, Class<?>... testClasses )
     {
         TestListResolver testListResolver = new TestListResolver( "" );
         return providerParametersMock( runListener, testListResolver, testClasses );
@@ -997,8 +1004,9 @@ public class JUnitPlatformProviderTest
         return providerParametersMock( runListenerMock(), testListResolver, testClasses );
     }
 
-    private static ProviderParameters providerParametersMock(
-            TestReportListener runListener, TestListResolver testListResolver, Class<?>... testClasses )
+    private static ProviderParameters providerParametersMock( TestReportListener<TestOutputReportEntry> runListener,
+                                                              TestListResolver testListResolver,
+                                                              Class<?>... testClasses )
     {
         TestsToRun testsToRun = newTestsToRun( testClasses );
 
@@ -1023,7 +1031,7 @@ public class JUnitPlatformProviderTest
         return providerParameters;
     }
 
-    private static TestReportListener runListenerMock()
+    private static TestReportListener<TestOutputReportEntry> runListenerMock()
     {
         return mock( TestReportListener.class, withSettings().extraInterfaces( TestOutputReceiver.class ) );
     }
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTest.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTest.java
index 6ca778f..3c1e129 100644
--- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTest.java
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTest.java
@@ -22,6 +22,7 @@ package org.apache.maven.surefire.junitplatform;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singleton;
 import static java.util.Collections.singletonList;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -45,6 +46,7 @@ import java.lang.reflect.Method;
 import java.util.Map;
 import java.util.Optional;
 
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.report.PojoStackTraceWriter;
 import org.apache.maven.surefire.api.report.ReportEntry;
@@ -75,11 +77,12 @@ import org.opentest4j.TestSkippedException;
  *
  * @since 2.22.0
  */
+@SuppressWarnings( "checkstyle:magicnumber" )
 public class RunListenerAdapterTest
 {
     private static final ConfigurationParameters CONFIG_PARAMS = mock( ConfigurationParameters.class );
 
-    private TestReportListener listener;
+    private TestReportListener<TestOutputReportEntry> listener;
 
     private RunListenerAdapter adapter;
 
@@ -89,6 +92,7 @@ public class RunListenerAdapterTest
         listener = mock( TestReportListener.class );
         adapter = new RunListenerAdapter( listener );
         adapter.testPlanExecutionStarted( TestPlan.from( emptyList() ) );
+        adapter.setRunMode( NORMAL_RUN );
     }
 
     @Test
@@ -151,18 +155,22 @@ public class RunListenerAdapterTest
         adapter.testPlanExecutionStarted( plan );
         adapter.executionStarted( TestIdentifier.from( engine ) );
         adapter.executionStarted( TestIdentifier.from( parent ) );
-        verify( listener )
-                .testSetStarting( new SimpleReportEntry( className, null, null, null ) );
+        verify( listener ).testSetStarting( new SimpleReportEntry( NORMAL_RUN, 0x0000000100000000L, className,
+            null, null, null ) );
         verifyNoMoreInteractions( listener );
 
         adapter.executionStarted( TestIdentifier.from( child ) );
-        verify( listener )
-                .testStarting( new SimpleReportEntry( className, null, MY_TEST_METHOD_NAME, null ) );
+        verify( listener ).testStarting( new SimpleReportEntry( NORMAL_RUN, 0x0000000100000001L, className, null,
+            MY_TEST_METHOD_NAME, null ) );
         verifyNoMoreInteractions( listener );
 
         adapter.executionFinished( TestIdentifier.from( child ), successful() );
         ArgumentCaptor<SimpleReportEntry> report = ArgumentCaptor.forClass( SimpleReportEntry.class );
         verify( listener ).testSucceeded( report.capture() );
+        assertThat( report.getValue().getRunMode() )
+            .isEqualTo( NORMAL_RUN );
+        assertThat( report.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000001L );
         assertThat( report.getValue().getSourceName() )
                 .isEqualTo( className );
         assertThat( report.getValue().getSourceText() )
@@ -222,6 +230,8 @@ public class RunListenerAdapterTest
         adapter.executionStarted( TestIdentifier.from( parent ) );
         ArgumentCaptor<SimpleReportEntry> report = ArgumentCaptor.forClass( SimpleReportEntry.class );
         inOrder.verify( listener ).testSetStarting( report.capture() );
+        assertThat( report.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000000L );
         assertThat( report.getValue().getSourceName() )
                 .isEqualTo( MyTestClass.class.getName() );
         assertThat( report.getValue().getSourceText() )
@@ -233,14 +243,17 @@ public class RunListenerAdapterTest
         verifyZeroInteractions( listener );
 
         adapter.executionStarted( TestIdentifier.from( child1 ) );
-        inOrder.verify( listener )
-                .testStarting( new SimpleReportEntry( MyTestClass.class.getName(), "parent",
-                        MY_NAMED_TEST_METHOD_NAME, "dn1" ) );
+        inOrder.verify( listener ).testStarting( new SimpleReportEntry( NORMAL_RUN, 0x0000000100000001L,
+            MyTestClass.class.getName(), "parent", MY_NAMED_TEST_METHOD_NAME, "dn1" ) );
         inOrder.verifyNoMoreInteractions();
 
         adapter.executionFinished( TestIdentifier.from( child1 ), successful() );
         report = ArgumentCaptor.forClass( SimpleReportEntry.class );
         inOrder.verify( listener ).testSucceeded( report.capture() );
+        assertThat( report.getValue().getRunMode() )
+            .isEqualTo( NORMAL_RUN );
+        assertThat( report.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000001L );
         assertThat( report.getValue().getSourceName() )
                 .isEqualTo( MyTestClass.class.getName() );
         assertThat( report.getValue().getSourceText() )
@@ -257,14 +270,18 @@ public class RunListenerAdapterTest
 
         adapter.executionStarted( TestIdentifier.from( child2 ) );
         inOrder.verify( listener )
-                .testStarting( new SimpleReportEntry( MyTestClass.class.getName(), "parent",
-                        MY_TEST_METHOD_NAME + "(String)", null ) );
+                .testStarting( new SimpleReportEntry( NORMAL_RUN, 0x0000000100000002L, MyTestClass.class.getName(),
+                    "parent", MY_TEST_METHOD_NAME + "(String)", null ) );
         inOrder.verifyNoMoreInteractions();
 
         Exception assumptionFailure = new Exception();
         adapter.executionFinished( TestIdentifier.from( child2 ), aborted( assumptionFailure ) );
         report = ArgumentCaptor.forClass( SimpleReportEntry.class );
         inOrder.verify( listener ).testAssumptionFailure( report.capture() );
+        assertThat( report.getValue().getRunMode() )
+            .isEqualTo( NORMAL_RUN );
+        assertThat( report.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000002L );
         assertThat( report.getValue().getSourceName() )
                 .isEqualTo( MyTestClass.class.getName() );
         assertThat( report.getValue().getSourceText() )
@@ -326,13 +343,17 @@ public class RunListenerAdapterTest
 
 
         adapter.executionStarted( TestIdentifier.from( engine ) );
-        verify( listener )
-                .testStarting( new SimpleReportEntry( "engine", null, "engine", null ) );
+        verify( listener ).testStarting( new SimpleReportEntry( NORMAL_RUN, 0x0000000100000001L, "engine", null,
+            "engine", null ) );
         verifyNoMoreInteractions( listener );
 
         adapter.executionFinished( TestIdentifier.from( engine ), successful() );
         ArgumentCaptor<SimpleReportEntry> report = ArgumentCaptor.forClass( SimpleReportEntry.class );
         verify( listener ).testSucceeded( report.capture() );
+        assertThat( report.getValue().getRunMode() )
+            .isEqualTo( NORMAL_RUN );
+        assertThat( report.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000001L );
         assertThat( report.getValue().getSourceName() )
                 .isEqualTo( "engine" );
         assertThat( report.getValue().getSourceText() )
@@ -533,13 +554,16 @@ public class RunListenerAdapterTest
 
         String className = MyTestClass.class.getName();
 
-        verify( listener )
-                .testSetStarting( new SimpleReportEntry( className, null, null, null ) );
+        verify( listener ).testSetStarting( new SimpleReportEntry( NORMAL_RUN, 0x0000000100000000L, className, null,
+            null, null ) );
 
         ArgumentCaptor<SimpleReportEntry> report = ArgumentCaptor.forClass( SimpleReportEntry.class );
         verify( listener )
                 .testSetCompleted( report.capture() );
-
+        assertThat( report.getValue().getRunMode() )
+            .isEqualTo( NORMAL_RUN );
+        assertThat( report.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000000L );
         assertThat( report.getValue().getSourceName() )
                 .isEqualTo( className );
         assertThat( report.getValue().getSourceText() )
diff --git a/surefire-providers/surefire-junit3/pom.xml b/surefire-providers/surefire-junit3/pom.xml
index 3571e64..9b4876a 100644
--- a/surefire-providers/surefire-junit3/pom.xml
+++ b/surefire-providers/surefire-junit3/pom.xml
@@ -43,6 +43,11 @@
       <artifactId>common-junit3</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.maven.surefire</groupId>
+      <artifactId>common-java5</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java
index 6a049fc..39d5614 100644
--- a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java
+++ b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java
@@ -19,13 +19,11 @@ package org.apache.maven.surefire.junit;
  * under the License.
  */
 
-import org.apache.maven.surefire.api.report.TestReportListener;
-import org.apache.maven.surefire.common.junit3.JUnit3Reflector;
-import org.apache.maven.surefire.common.junit3.JUnit3TestChecker;
+import java.util.Map;
+
 import org.apache.maven.surefire.api.provider.AbstractProvider;
 import org.apache.maven.surefire.api.provider.ProviderParameters;
 import org.apache.maven.surefire.api.report.ReporterFactory;
-import org.apache.maven.surefire.api.report.RunListener;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 import org.apache.maven.surefire.api.suite.RunResult;
@@ -33,10 +31,11 @@ import org.apache.maven.surefire.api.testset.TestSetFailedException;
 import org.apache.maven.surefire.api.util.RunOrderCalculator;
 import org.apache.maven.surefire.api.util.ScanResult;
 import org.apache.maven.surefire.api.util.TestsToRun;
-
-import java.util.Map;
+import org.apache.maven.surefire.common.junit3.JUnit3Reflector;
+import org.apache.maven.surefire.common.junit3.JUnit3TestChecker;
 
 import static org.apache.maven.surefire.api.report.ConsoleOutputCapture.startCapture;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.apache.maven.surefire.api.util.ReflectionUtils.instantiate;
 import static org.apache.maven.surefire.api.util.internal.ObjectUtils.isSecurityManagerSupported;
 import static org.apache.maven.surefire.api.util.internal.ObjectUtils.systemProps;
@@ -76,6 +75,10 @@ public class JUnit3Provider
     public RunResult invoke( Object forkTestSet )
         throws TestSetFailedException
     {
+        ReporterFactory reporterFactory = providerParameters.getReporterFactory();
+        JUnit3Reporter reporter = new JUnit3Reporter( reporterFactory.createTestReportListener() );
+        reporter.setRunMode( NORMAL_RUN );
+        startCapture( reporter );
 
         final TestsToRun testsToRun;
         if ( forkTestSet instanceof TestsToRun )
@@ -91,18 +94,15 @@ public class JUnit3Provider
             testsToRun = scanClassPath();
         }
 
-        ReporterFactory reporterFactory = providerParameters.getReporterFactory();
         RunResult runResult;
         try
         {
-            TestReportListener reporter = reporterFactory.createTestReportListener();
-            startCapture( reporter );
             Map<String, String> systemProperties = systemProps();
             setSystemManager( System.getProperty( "surefire.security.manager" ) );
 
             for ( Class<?> clazz : testsToRun )
             {
-                SurefireTestSetExecutor surefireTestSetExecutor = createTestSet( clazz );
+                SurefireTestSetExecutor surefireTestSetExecutor = createTestSet( clazz, reporter );
                 executeTestSet( clazz, surefireTestSetExecutor, reporter, systemProperties );
             }
         }
@@ -128,28 +128,30 @@ public class JUnit3Provider
         }
     }
 
-    private SurefireTestSetExecutor createTestSet( Class<?> clazz )
+    private SurefireTestSetExecutor createTestSet( Class<?> clazz, JUnit3Reporter reporter )
     {
         return reflector.isJUnit3Available() && jUnit3TestChecker.accept( clazz )
-            ? new JUnitTestSetExecutor( reflector )
-            : new PojoTestSetExecutor();
+            ? new JUnitTestSetExecutor( reflector, reporter )
+            : new PojoTestSetExecutor( reporter );
     }
 
-    private void executeTestSet( Class<?> testSet, SurefireTestSetExecutor testSetExecutor, RunListener reporter,
+    private void executeTestSet( Class<?> testSet, SurefireTestSetExecutor testSetExecutor, JUnit3Reporter reporter,
                                  Map<String, String> systemProperties )
         throws TestSetFailedException
     {
         String clazz = testSet.getName();
+        long testId = reporter.getClassMethodIndexer().indexClass( clazz );
 
         try
         {
-            TestSetReportEntry started = new SimpleReportEntry( clazz, null, null, null );
+            TestSetReportEntry started = new SimpleReportEntry( NORMAL_RUN, testId, clazz, null, null, null );
             reporter.testSetStarting( started );
-            testSetExecutor.execute( testSet, reporter, testClassLoader );
+            testSetExecutor.execute( testSet, testClassLoader );
         }
         finally
         {
-            TestSetReportEntry completed = new SimpleReportEntry( clazz, null, null, null, systemProperties );
+            TestSetReportEntry completed =
+                new SimpleReportEntry( NORMAL_RUN, testId, clazz, null, null, null, systemProperties );
             reporter.testSetCompleted( completed );
         }
     }
diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Reporter.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Reporter.java
new file mode 100644
index 0000000..d9dfe80
--- /dev/null
+++ b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Reporter.java
@@ -0,0 +1,122 @@
+package org.apache.maven.surefire.junit;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.api.report.OutputReportEntry;
+import org.apache.maven.surefire.api.report.ReportEntry;
+import org.apache.maven.surefire.api.report.RunListener;
+import org.apache.maven.surefire.api.report.RunMode;
+import org.apache.maven.surefire.api.report.TestOutputReceiver;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
+import org.apache.maven.surefire.api.report.TestReportListener;
+import org.apache.maven.surefire.api.report.TestSetReportEntry;
+import org.apache.maven.surefire.report.ClassMethodIndexer;
+import org.apache.maven.surefire.report.RunModeSetter;
+
+/**
+ * This implementation of {@link RunListener} handles {@link OutputReportEntry} in the
+ * {@link TestOutputReceiver output receiver}, downcasting to {@link TestOutputReportEntry}, and
+ * delegates the report entry to the {@link TestReportListener}.
+ * This object necessarily requires setting the {@link RunMode} in order to behave properly.
+ */
+final class JUnit3Reporter
+    implements RunListener, TestOutputReceiver<OutputReportEntry>, RunModeSetter
+{
+    private final ClassMethodIndexer classMethodIndexer = new ClassMethodIndexer();
+    private final TestReportListener<TestOutputReportEntry> reporter;
+    private volatile RunMode runMode;
+
+    JUnit3Reporter( TestReportListener<TestOutputReportEntry> reporter )
+    {
+        this.reporter = reporter;
+    }
+
+    ClassMethodIndexer getClassMethodIndexer()
+    {
+        return classMethodIndexer;
+    }
+
+    @Override
+    public void setRunMode( RunMode runMode )
+    {
+        this.runMode = runMode;
+    }
+
+    @Override
+    public void testSetStarting( TestSetReportEntry report )
+    {
+        reporter.testSetStarting( report );
+    }
+
+    @Override
+    public void testSetCompleted( TestSetReportEntry report )
+    {
+        reporter.testSetCompleted( report );
+    }
+
+    @Override
+    public void testStarting( ReportEntry report )
+    {
+        reporter.testStarting( report );
+    }
+
+    @Override
+    public void testSucceeded( ReportEntry report )
+    {
+        reporter.testSucceeded( report );
+    }
+
+    @Override
+    public void testAssumptionFailure( ReportEntry report )
+    {
+        reporter.testAssumptionFailure( report );
+    }
+
+    @Override
+    public void testError( ReportEntry report )
+    {
+        reporter.testError( report );
+    }
+
+    @Override
+    public void testFailed( ReportEntry report )
+    {
+        reporter.testFailed( report );
+    }
+
+    @Override
+    public void testSkipped( ReportEntry report )
+    {
+        reporter.testSkipped( report );
+    }
+
+    @Override
+    public void testExecutionSkippedByUser()
+    {
+        reporter.testExecutionSkippedByUser();
+    }
+
+    @Override
+    public void writeTestOutput( OutputReportEntry reportEntry )
+    {
+        Long testRunId = classMethodIndexer.getLocalIndex();
+        reporter.writeTestOutput( new TestOutputReportEntry( reportEntry, runMode, testRunId ) );
+    }
+}
diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnitTestSetExecutor.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnitTestSetExecutor.java
index e8b5d19..3772e2a 100644
--- a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnitTestSetExecutor.java
+++ b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnitTestSetExecutor.java
@@ -22,9 +22,9 @@ package org.apache.maven.surefire.junit;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
-import org.apache.maven.surefire.common.junit3.JUnit3Reflector;
-import org.apache.maven.surefire.api.report.RunListener;
+
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
+import org.apache.maven.surefire.common.junit3.JUnit3Reflector;
 
 /**
  * JUnit3 test set
@@ -35,9 +35,12 @@ public final class JUnitTestSetExecutor
 {
     private final JUnit3Reflector reflector;
 
-    public JUnitTestSetExecutor( JUnit3Reflector reflector )
+    private final JUnit3Reporter reporter;
+
+    public JUnitTestSetExecutor( JUnit3Reflector reflector, JUnit3Reporter reporter )
     {
         this.reflector = reflector;
+        this.reporter = reporter;
 
         // ----------------------------------------------------------------------
         // Strategy for executing JUnit tests
@@ -55,7 +58,7 @@ public final class JUnitTestSetExecutor
     }
 
     @Override
-    public void execute( Class<?> testClass, RunListener reporter, ClassLoader loader )
+    public void execute( Class<?> testClass, ClassLoader loader )
         throws TestSetFailedException
     {
         try
diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/PojoTestSetExecutor.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/PojoTestSetExecutor.java
index dcefba2..45ca779 100644
--- a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/PojoTestSetExecutor.java
+++ b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/PojoTestSetExecutor.java
@@ -26,12 +26,13 @@ import java.util.ArrayList;
 import java.util.Collection;
 
 import org.apache.maven.surefire.api.report.LegacyPojoStackTraceWriter;
-import org.apache.maven.surefire.api.report.RunListener;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
 import org.apache.maven.surefire.api.report.StackTraceWriter;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
+import org.apache.maven.surefire.report.ClassMethodIndexer;
 
 import static java.util.Objects.requireNonNull;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.apache.maven.surefire.api.report.SimpleReportEntry.withException;
 
 /**
@@ -49,17 +50,24 @@ public class PojoTestSetExecutor
 
     private static final Object[] EMPTY_OBJECT_ARRAY = {};
 
+    private final JUnit3Reporter reporter;
+
+    public PojoTestSetExecutor( JUnit3Reporter reporter )
+    {
+        this.reporter = requireNonNull( reporter, "reportManager is null" );
+    }
+
     @Override
-    public void execute( Class<?> testClass, RunListener reportManager, ClassLoader loader )
+    public void execute( Class<?> testClass, ClassLoader loader )
             throws TestSetFailedException
     {
-        requireNonNull( reportManager, "reportManager is null" );
-
-        DiscoveredTestMethods discoveredTestMethods = discoverTestMethods( testClass );
+        DiscoveredTestMethods discoveredTestMethods = discoverTestMethods( requireNonNull( testClass ) );
 
         for ( Method testMethod : discoveredTestMethods.testMethods )
         {
-            boolean abort = executeTestMethod( testClass, testMethod, discoveredTestMethods, reportManager );
+            ClassMethodIndexer indexer = reporter.getClassMethodIndexer();
+            long testRunId = indexer.indexClassMethod( testClass.getName(), testMethod.getName() );
+            boolean abort = executeTestMethod( testClass, testMethod, testRunId, discoveredTestMethods );
             if ( abort )
             {
                 break;
@@ -67,20 +75,15 @@ public class PojoTestSetExecutor
         }
     }
 
-    private boolean executeTestMethod( Class<?> testClass, Method method, DiscoveredTestMethods methods,
-                                       RunListener reportManager )
+    private boolean executeTestMethod( Class<?> testClass, Method method, long testRunId,
+                                       DiscoveredTestMethods methods )
             throws TestSetFailedException
     {
-        if ( method == null || reportManager == null )
-        {
-            throw new NullPointerException();
-        }
-
         final Object testObject;
 
         try
         {
-            testObject = testClass.newInstance();
+            testObject = testClass.getDeclaredConstructor().newInstance();
         }
         catch ( ReflectiveOperationException e )
         {
@@ -92,7 +95,7 @@ public class PojoTestSetExecutor
         final String userFriendlyMethodName = methodName + "()";
         final String testName = getTestName( testClassName, userFriendlyMethodName );
 
-        reportManager.testStarting( new SimpleReportEntry( testClassName, null, testName, null ) );
+        reporter.testStarting( new SimpleReportEntry( NORMAL_RUN, testRunId, testClassName, null, testName, null ) );
 
         try
         {
@@ -101,7 +104,8 @@ public class PojoTestSetExecutor
         catch ( Throwable e )
         {
             StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( testClassName, methodName, e );
-            reportManager.testFailed( withException( testClassName, null, testName, null, stackTraceWriter ) );
+            reporter.testFailed(
+                withException( NORMAL_RUN, testRunId, testClassName, null, testName, null, stackTraceWriter ) );
 
             // A return value of true indicates to this class's executeTestMethods
             // method that it should abort and not attempt to execute
@@ -115,20 +119,23 @@ public class PojoTestSetExecutor
         try
         {
             method.invoke( testObject, EMPTY_OBJECT_ARRAY );
-            reportManager.testSucceeded( new SimpleReportEntry( testClassName, null, testName, null ) );
+            reporter.testSucceeded(
+                new SimpleReportEntry( NORMAL_RUN, testRunId, testClassName, null, testName, null ) );
         }
         catch ( InvocationTargetException e )
         {
             Throwable t = e.getTargetException();
             StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( testClassName, methodName, t );
-            reportManager.testFailed( withException( testClassName, null, testName, null, stackTraceWriter ) );
+            reporter.testFailed(
+                withException( NORMAL_RUN, testRunId, testClassName, null, testName, null, stackTraceWriter ) );
             // Don't return  here, because tearDownFixture should be called even
             // if the test method throws an exception.
         }
         catch ( Throwable t )
         {
             StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( testClassName, methodName, t );
-            reportManager.testFailed( withException( testClassName, null, testName, null, stackTraceWriter ) );
+            reporter.testFailed(
+                withException( NORMAL_RUN, testRunId, testClassName, null, testName, null, stackTraceWriter ) );
             // Don't return  here, because tearDownFixture should be called even
             // if the test method throws an exception.
         }
@@ -141,7 +148,8 @@ public class PojoTestSetExecutor
         {
             StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( testClassName, methodName, t );
             // Treat any exception from tearDownFixture as a failure of the test.
-            reportManager.testFailed( withException( testClassName, null, testName, null, stackTraceWriter ) );
+            reporter.testFailed(
+                withException( NORMAL_RUN, testRunId, testClassName, null, testName, null, stackTraceWriter ) );
 
             // A return value of true indicates to this class's executeTestMethods
             // method that it should abort and not attempt to execute
diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/SurefireTestSetExecutor.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/SurefireTestSetExecutor.java
index e192081..1b5bb71 100644
--- a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/SurefireTestSetExecutor.java
+++ b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/SurefireTestSetExecutor.java
@@ -19,7 +19,6 @@ package org.apache.maven.surefire.junit;
  * under the License.
  */
 
-import org.apache.maven.surefire.api.report.RunListener;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
 
 /**
@@ -28,6 +27,6 @@ import org.apache.maven.surefire.api.testset.TestSetFailedException;
  */
 public interface SurefireTestSetExecutor
 {
-    void execute( Class<?> testClass, RunListener reportManager, ClassLoader loader )
+    void execute( Class<?> testClass, ClassLoader loader )
         throws TestSetFailedException;
 }
diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/TestListenerInvocationHandler.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/TestListenerInvocationHandler.java
index cbbd655..b5cf55c 100644
--- a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/TestListenerInvocationHandler.java
+++ b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/TestListenerInvocationHandler.java
@@ -31,6 +31,7 @@ import org.apache.maven.surefire.api.report.SimpleReportEntry;
 import org.apache.maven.surefire.api.report.StackTraceWriter;
 
 import static java.util.Objects.requireNonNull;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.apache.maven.surefire.api.report.SimpleReportEntry.withException;
 import static org.apache.maven.surefire.api.util.internal.TestClassMethodNameUtils.extractClassName;
 import static org.apache.maven.surefire.api.util.internal.TestClassMethodNameUtils.extractMethodName;
@@ -53,7 +54,7 @@ public class TestListenerInvocationHandler
 
     private final Set<FailedTest> failedTestsSet = new HashSet<>();
 
-    private final RunListener reporter;
+    private final JUnit3Reporter reporter;
 
     private static final Class<?>[] EMPTY_CLASS_ARRAY = { };
 
@@ -106,7 +107,7 @@ public class TestListenerInvocationHandler
         }
     }
 
-    public TestListenerInvocationHandler( RunListener reporter )
+    public TestListenerInvocationHandler( JUnit3Reporter reporter )
     {
         this.reporter = requireNonNull( reporter, "reporter is null" );
     }
@@ -197,19 +198,23 @@ public class TestListenerInvocationHandler
         }
     }
 
-    private static ReportEntry toReportEntryWithException( Object[] args )
+    private ReportEntry toReportEntryWithException( Object[] args )
             throws ReflectiveOperationException
     {
         String description = args[0].toString();
         String className = extractClassName( description );
         String methodName = extractMethodName( description );
         StackTraceWriter stackTraceWriter = toStackTraceWriter( args );
-        return withException( className, null, methodName, null, stackTraceWriter );
+        long testRunId = reporter.getClassMethodIndexer().indexClassMethod( className, methodName );
+        return withException( NORMAL_RUN, testRunId, className, null, methodName, null, stackTraceWriter );
     }
 
-    private static SimpleReportEntry createStartEndReportEntry( Object[] args )
+    private SimpleReportEntry createStartEndReportEntry( Object[] args )
     {
         String description = args[0].toString();
-        return new SimpleReportEntry( extractClassName( description ), null, extractMethodName( description ), null );
+        String className = extractClassName( description );
+        String methodName = extractMethodName( description );
+        long testRunId = reporter.getClassMethodIndexer().indexClassMethod( className, methodName );
+        return new SimpleReportEntry( NORMAL_RUN, testRunId, className, null, methodName, null );
     }
 }
diff --git a/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java b/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java
index 0a0e1c1..417511a 100644
--- a/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java
+++ b/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java
@@ -22,11 +22,12 @@ package org.apache.maven.surefire.junit;
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
+import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
 import org.apache.maven.surefire.common.junit3.JUnit3Reflector;
 import org.apache.maven.surefire.api.report.ReportEntry;
 import org.apache.maven.surefire.api.report.RunListener;
-import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 
 import java.security.AccessControlException;
@@ -50,9 +51,10 @@ public class JUnitTestSetTest
     {
         ClassLoader testClassLoader = this.getClass().getClassLoader();
         JUnit3Reflector reflector = new JUnit3Reflector( testClassLoader );
-        JUnitTestSetExecutor testSet = new JUnitTestSetExecutor( reflector );
         SuccessListener listener = new SuccessListener();
-        testSet.execute( Suite.class, listener, testClassLoader );
+        JUnit3Reporter reporter = new JUnit3Reporter( listener );
+        JUnitTestSetExecutor testSet = new JUnitTestSetExecutor( reflector, reporter );
+        testSet.execute( Suite.class, testClassLoader );
         List<ReportEntry> succeededTests = listener.getSucceededTests();
         assertEquals( 1, succeededTests.size() );
         assertEquals( "org.apache.maven.surefire.junit.JUnitTestSetTest$AlwaysSucceeds",
@@ -128,7 +130,7 @@ public class JUnitTestSetTest
      *
      */
     public static class SuccessListener
-        implements RunListener
+        implements RunListener, TestReportListener<TestOutputReportEntry>
     {
 
         private List<ReportEntry> succeededTests = new ArrayList<>();
@@ -183,21 +185,76 @@ public class JUnitTestSetTest
         {
         }
 
-        public RunMode markAs( RunMode currentRunMode )
+        List<ReportEntry> getSucceededTests()
         {
-            return RunMode.NORMAL_RUN;
+            return succeededTests;
         }
 
-        public void testSkippedByUser( ReportEntry report )
+        @Override
+        public void writeTestOutput( TestOutputReportEntry reportEntry )
         {
-            testSkipped( report );
+
         }
 
-        List<ReportEntry> getSucceededTests()
+        @Override
+        public boolean isDebugEnabled()
         {
-            return succeededTests;
+            return false;
+        }
+
+        @Override
+        public void debug( String message )
+        {
+
+        }
+
+        @Override
+        public boolean isInfoEnabled()
+        {
+            return false;
+        }
+
+        @Override
+        public void info( String message )
+        {
+
+        }
+
+        @Override
+        public boolean isWarnEnabled()
+        {
+            return false;
+        }
+
+        @Override
+        public void warning( String message )
+        {
+
+        }
+
+        @Override
+        public boolean isErrorEnabled()
+        {
+            return false;
+        }
+
+        @Override
+        public void error( String message )
+        {
+
+        }
+
+        @Override
+        public void error( String message, Throwable t )
+        {
+
         }
 
+        @Override
+        public void error( Throwable t )
+        {
+
+        }
     }
 
     /**
diff --git a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
index 44cc0a1..d4a57a9 100644
--- a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
+++ b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
@@ -22,6 +22,7 @@ package org.apache.maven.surefire.junit4;
 import org.apache.maven.surefire.api.booter.Command;
 import org.apache.maven.surefire.api.provider.CommandChainReader;
 import org.apache.maven.surefire.api.provider.CommandListener;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
 import org.apache.maven.surefire.common.junit4.JUnit4TestChecker;
@@ -29,6 +30,7 @@ import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
 import org.apache.maven.surefire.common.junit4.Notifier;
 import org.apache.maven.surefire.api.provider.AbstractProvider;
 import org.apache.maven.surefire.api.provider.ProviderParameters;
+import org.apache.maven.surefire.report.ClassMethodIndexer;
 import org.apache.maven.surefire.report.PojoStackTraceWriter;
 import org.apache.maven.surefire.api.report.ReporterFactory;
 import org.apache.maven.surefire.api.report.RunListener;
@@ -40,6 +42,7 @@ import org.apache.maven.surefire.api.testset.TestSetFailedException;
 import org.apache.maven.surefire.api.util.RunOrderCalculator;
 import org.apache.maven.surefire.api.util.ScanResult;
 import org.apache.maven.surefire.api.util.TestsToRun;
+import org.apache.maven.surefire.report.RunModeSetter;
 import org.junit.runner.Description;
 import org.junit.runner.Request;
 import org.junit.runner.Result;
@@ -52,6 +55,8 @@ import java.util.Set;
 
 import static java.lang.reflect.Modifier.isAbstract;
 import static java.lang.reflect.Modifier.isInterface;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
+import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAILURE;
 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.createMatchAnyDescriptionFilter;
 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.generateFailingTestDescriptions;
 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.isFailureInsideJUnitItself;
@@ -75,6 +80,8 @@ public class JUnit4Provider
 {
     private static final String UNDETERMINED_TESTS_DESCRIPTION = "cannot determine test in forked JVM with surefire";
 
+    private final ClassMethodIndexer classMethodIndexer = new ClassMethodIndexer();
+
     private final ClassLoader testClassLoader;
 
     private final String customRunListeners;
@@ -121,7 +128,7 @@ public class JUnit4Provider
         RunResult runResult;
         try
         {
-            TestReportListener reporter = reporterFactory.createTestReportListener();
+            TestReportListener<TestOutputReportEntry> reporter = reporterFactory.createTestReportListener();
             JUnit4RunListener listener = new JUnit4RunListener( reporter );
 
             startCapture( listener );
@@ -156,7 +163,7 @@ public class JUnit4Provider
 
                 for ( Class<?> testToRun : testsToRun )
                 {
-                    executeTestSet( testToRun, reporter, notifier );
+                    executeTestSet( testToRun, reporter, notifier, listener );
                 }
             }
             finally
@@ -229,13 +236,15 @@ public class JUnit4Provider
         } );
     }
 
-    private void executeTestSet( Class<?> clazz, RunListener reporter, Notifier notifier )
+    private void executeTestSet( Class<?> clazz, RunListener reporter, Notifier notifier, RunModeSetter runMode )
     {
-        final SimpleReportEntry report = new SimpleReportEntry( clazz.getName(), null, null, null, systemProps() );
+        long testRunId = classMethodIndexer.indexClass( clazz.getName() );
+        SimpleReportEntry report =
+            new SimpleReportEntry( NORMAL_RUN, testRunId, clazz.getName(), null, null, null, systemProps() );
         reporter.testSetStarting( report );
         try
         {
-            executeWithRerun( clazz, notifier );
+            executeWithRerun( clazz, notifier, runMode );
         }
         catch ( Throwable e )
         {
@@ -250,7 +259,8 @@ public class JUnit4Provider
                 String reportName = report.getName();
                 String reportSourceName = report.getSourceName();
                 PojoStackTraceWriter stackWriter = new PojoStackTraceWriter( reportSourceName, reportName, e );
-                reporter.testError( withException( reportSourceName, null, reportName, null, stackWriter ) );
+                reporter.testError( withException( NORMAL_RUN, testRunId, reportSourceName, null, reportName, null,
+                    stackWriter ) );
             }
         }
         finally
@@ -259,7 +269,7 @@ public class JUnit4Provider
         }
     }
 
-    private void executeWithRerun( Class<?> clazz, Notifier notifier )
+    private void executeWithRerun( Class<?> clazz, Notifier notifier, RunModeSetter runMode )
     {
         JUnitTestFailureListener failureListener = new JUnitTestFailureListener();
         notifier.addListener( failureListener );
@@ -270,6 +280,7 @@ public class JUnit4Provider
             try
             {
                 notifier.asFailFast( isFailFast() );
+                runMode.setRunMode( NORMAL_RUN );
                 execute( clazz, notifier, hasMethodFilter ? createMethodFilter() : null );
             }
             finally
@@ -280,6 +291,7 @@ public class JUnit4Provider
             // Rerun failing tests if rerunFailingTestsCount is larger than 0
             if ( isRerunFailingTests() )
             {
+                runMode.setRunMode( RERUN_TEST_AFTER_FAILURE );
                 Notifier rerunNotifier = pureNotifier();
                 notifier.copyListenersTo( rerunNotifier );
                 for ( int i = 0; i < rerunFailingTestsCount && !failureListener.getAllFailures().isEmpty(); i++ )
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ClassesParallelRunListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ClassesParallelRunListener.java
index 09c0f20..c73eedb 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ClassesParallelRunListener.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ClassesParallelRunListener.java
@@ -19,13 +19,14 @@ package org.apache.maven.surefire.junitcore;
  * under the License.
  */
 
-import org.apache.maven.surefire.api.report.ReporterFactory;
-
 import java.util.Map;
 
+import org.apache.maven.surefire.api.report.ReporterFactory;
+
 /**
  * @author Kristian Rosenvold
  */
+@Deprecated // remove this class after StatelessXmlReporter is capable of parallel test sets processing
 final class ClassesParallelRunListener
     extends ConcurrentRunListener
 {
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConcurrentRunListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConcurrentRunListener.java
index 8d71559..f61bd9c 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConcurrentRunListener.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConcurrentRunListener.java
@@ -24,12 +24,12 @@ import java.util.Map;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.api.report.ReportEntry;
 import org.apache.maven.surefire.api.report.ReporterFactory;
-import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.StackTraceWriter;
 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
 
+import static java.lang.ThreadLocal.withInitial;
 import static org.apache.maven.surefire.junitcore.TestMethod.getThreadTestMethod;
 
 /**
@@ -43,12 +43,13 @@ import static org.apache.maven.surefire.junitcore.TestMethod.getThreadTestMethod
  * @see org.apache.maven.surefire.junitcore.JUnitCoreRunListener for details about regular junit run listening
  * @author Kristian Rosenvold
  */
+@Deprecated // remove this class after StatelessXmlReporter is capable of parallel test sets processing
 abstract class ConcurrentRunListener
-    implements TestReportListener
+    implements TestReportListener<TestOutputReportEntry>
 {
     private final Map<String, TestSet> classMethodCounts;
 
-    private final ThreadLocal<TestReportListener> reporterManagerThreadLocal;
+    private final ThreadLocal<TestReportListener<TestOutputReportEntry>> reporterManagerThreadLocal;
 
     private final boolean reportImmediately;
 
@@ -60,20 +61,20 @@ abstract class ConcurrentRunListener
         this.reportImmediately = reportImmediately;
         this.classMethodCounts = classMethodCounts;
         logger = reporterFactory.createTestReportListener();
-        reporterManagerThreadLocal = ThreadLocal.withInitial( reporterFactory::createTestReportListener );
+        reporterManagerThreadLocal = withInitial( reporterFactory::createTestReportListener );
     }
 
     @Override
-    public final void testSetStarting( TestSetReportEntry description )
+    public void testSetStarting( TestSetReportEntry description )
     {
     }
 
     @Override
-    public final void testSetCompleted( TestSetReportEntry result )
+    public void testSetCompleted( TestSetReportEntry result )
     {
         try
         {
-            final TestReportListener reporterManager = getRunListener();
+            TestReportListener<TestOutputReportEntry> reporterManager = getRunListener();
             for ( TestSet testSet : classMethodCounts.values() )
             {
                 testSet.replay( reporterManager );
@@ -86,7 +87,7 @@ abstract class ConcurrentRunListener
     }
 
     @Override
-    public final void testFailed( ReportEntry failure )
+    public void testFailed( ReportEntry failure )
     {
         final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
         if ( testMethod != null )
@@ -97,7 +98,7 @@ abstract class ConcurrentRunListener
     }
 
     @Override
-    public final void testError( ReportEntry failure )
+    public void testError( ReportEntry failure )
     {
         final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
         if ( testMethod != null )
@@ -108,7 +109,7 @@ abstract class ConcurrentRunListener
     }
 
     @Override
-    public final void testSkipped( ReportEntry description )
+    public void testSkipped( ReportEntry description )
     {
         TestSet testSet = getTestSet( description );
         TestMethod testMethod = testSet.createThreadAttachedTestMethod( description );
@@ -118,19 +119,14 @@ abstract class ConcurrentRunListener
     }
 
     @Override
-    public final void testExecutionSkippedByUser()
+    public void testExecutionSkippedByUser()
     {
         // cannot guarantee proper call to all listeners
         getRunListener().testExecutionSkippedByUser();
     }
 
-    public final RunMode markAs( RunMode currentRunMode )
-    {
-        return reporterManagerThreadLocal.get().markAs( currentRunMode );
-    }
-
     @Override
-    public final void testAssumptionFailure( ReportEntry failure )
+    public void testAssumptionFailure( ReportEntry failure )
     {
         final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
         if ( testMethod != null )
@@ -141,7 +137,7 @@ abstract class ConcurrentRunListener
     }
 
     @Override
-    public final void testStarting( ReportEntry description )
+    public void testStarting( ReportEntry description )
     {
         TestSet testSet = getTestSet( description );
         testSet.createThreadAttachedTestMethod( description );
@@ -151,7 +147,7 @@ abstract class ConcurrentRunListener
     }
 
     @Override
-    public final void testSucceeded( ReportEntry report )
+    public void testSucceeded( ReportEntry report )
     {
         TestMethod testMethod = getThreadTestMethod();
         if ( testMethod != null )
@@ -193,14 +189,14 @@ abstract class ConcurrentRunListener
         return classMethodCounts.get( description.getSourceName() );
     }
 
-    final TestReportListener getRunListener()
+    final TestReportListener<TestOutputReportEntry> getRunListener()
     {
         return reporterManagerThreadLocal.get();
     }
 
-    public static TestReportListener createInstance( Map<String, TestSet> classMethodCounts,
-                                                     ReporterFactory reporterFactory,
-                                                     boolean parallelClasses, boolean parallelBoth )
+    public static ConcurrentRunListener createInstance( Map<String, TestSet> classMethodCounts,
+                                                        ReporterFactory reporterFactory,
+                                                        boolean parallelClasses, boolean parallelBoth )
     {
         return parallelClasses
             ? new ClassesParallelRunListener( classMethodCounts, reporterFactory )
@@ -209,7 +205,7 @@ abstract class ConcurrentRunListener
 
 
     @Override
-    public final void writeTestOutput( TestOutputReportEntry reportEntry )
+    public void writeTestOutput( TestOutputReportEntry reportEntry )
     {
         TestMethod threadTestMethod = getThreadTestMethod();
         if ( threadTestMethod != null )
@@ -225,61 +221,61 @@ abstract class ConcurrentRunListener
     }
 
     @Override
-    public final boolean isDebugEnabled()
+    public boolean isDebugEnabled()
     {
         return logger.isDebugEnabled();
     }
 
     @Override
-    public final void debug( String message )
+    public void debug( String message )
     {
         logger.debug( message );
     }
 
     @Override
-    public final boolean isInfoEnabled()
+    public boolean isInfoEnabled()
     {
         return logger.isInfoEnabled();
     }
 
     @Override
-    public final void info( String message )
+    public void info( String message )
     {
         logger.info( message );
     }
 
     @Override
-    public final boolean isWarnEnabled()
+    public boolean isWarnEnabled()
     {
         return logger.isWarnEnabled();
     }
 
     @Override
-    public final void warning( String message )
+    public void warning( String message )
     {
         logger.warning( message );
     }
 
     @Override
-    public final boolean isErrorEnabled()
+    public boolean isErrorEnabled()
     {
         return logger.isErrorEnabled();
     }
 
     @Override
-    public final void error( String message )
+    public void error( String message )
     {
         logger.error( message );
     }
 
     @Override
-    public final void error( String message, Throwable t )
+    public void error( String message, Throwable t )
     {
         logger.error( message, t );
     }
 
     @Override
-    public final void error( Throwable t )
+    public void error( Throwable t )
     {
         logger.error( t );
     }
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
index d01620c..7ed4834 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
@@ -19,18 +19,15 @@ package org.apache.maven.surefire.junitcore;
  * under the License.
  */
 
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.api.booter.Command;
+import org.apache.maven.surefire.api.provider.AbstractProvider;
 import org.apache.maven.surefire.api.provider.CommandChainReader;
 import org.apache.maven.surefire.api.provider.CommandListener;
-import org.apache.maven.surefire.api.report.TestReportListener;
-import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
-import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
-import org.apache.maven.surefire.common.junit4.Notifier;
-import org.apache.maven.surefire.common.junit48.FilterFactory;
-import org.apache.maven.surefire.common.junit48.JUnit48Reflector;
-import org.apache.maven.surefire.common.junit48.JUnit48TestChecker;
-import org.apache.maven.surefire.api.provider.AbstractProvider;
 import org.apache.maven.surefire.api.provider.ProviderParameters;
 import org.apache.maven.surefire.api.report.ReporterFactory;
 import org.apache.maven.surefire.api.suite.RunResult;
@@ -40,20 +37,24 @@ import org.apache.maven.surefire.api.util.RunOrderCalculator;
 import org.apache.maven.surefire.api.util.ScanResult;
 import org.apache.maven.surefire.api.util.ScannerFilter;
 import org.apache.maven.surefire.api.util.TestsToRun;
+import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
+import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
+import org.apache.maven.surefire.common.junit4.Notifier;
+import org.apache.maven.surefire.common.junit48.FilterFactory;
+import org.apache.maven.surefire.common.junit48.JUnit48Reflector;
+import org.apache.maven.surefire.common.junit48.JUnit48TestChecker;
 import org.junit.runner.Description;
 import org.junit.runner.manipulation.Filter;
 
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
+import static org.apache.maven.surefire.api.report.ConsoleOutputCapture.startCapture;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
+import static org.apache.maven.surefire.api.report.RunMode.RERUN_TEST_AFTER_FAILURE;
+import static org.apache.maven.surefire.api.testset.TestListResolver.optionallyWildcardFilter;
+import static org.apache.maven.surefire.api.util.TestsToRun.fromClass;
 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.generateFailingTestDescriptions;
 import static org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory.createCustomListeners;
 import static org.apache.maven.surefire.common.junit4.Notifier.pureNotifier;
 import static org.apache.maven.surefire.junitcore.ConcurrentRunListener.createInstance;
-import static org.apache.maven.surefire.api.report.ConsoleOutputCapture.startCapture;
-import static org.apache.maven.surefire.api.testset.TestListResolver.optionallyWildcardFilter;
-import static org.apache.maven.surefire.api.util.TestsToRun.fromClass;
 
 /**
  * @author Kristian Rosenvold
@@ -120,6 +121,7 @@ public class JUnitCoreProvider
     {
         ReporterFactory reporterFactory = providerParameters.getReporterFactory();
         JUnit4RunListener listener = createRunListener( reporterFactory );
+        listener.setRunMode( NORMAL_RUN );
         ConsoleLogger logger = listener.getConsoleLogger();
         Notifier notifier = new Notifier( listener, getSkipAfterFailureCount() );
         // startCapture() called in createRunListener() in prior to setTestsToRun()
@@ -159,6 +161,7 @@ public class JUnitCoreProvider
             // Rerun failing tests if rerunFailingTestsCount is larger than 0
             if ( isRerunFailingTests() )
             {
+                listener.setRunMode( RERUN_TEST_AFTER_FAILURE );
                 Notifier rerunNotifier = pureNotifier();
                 notifier.copyListenersTo( rerunNotifier );
                 JUnitCoreWrapper rerunCore = new JUnitCoreWrapper( rerunNotifier, jUnitCoreParameters, logger );
@@ -239,21 +242,23 @@ public class JUnitCoreProvider
 
     private JUnit4RunListener createRunListener( ReporterFactory reporterFactory )
     {
+        final JUnit4RunListener listener;
         if ( isSingleThreaded() )
         {
-            NonConcurrentRunListener rm = new NonConcurrentRunListener( reporterFactory.createTestReportListener() );
-            startCapture( rm );
-            return rm;
+            listener = new NonConcurrentRunListener( reporterFactory.createTestReportListener() );
         }
         else
         {
             Map<String, TestSet> testSetMap = new ConcurrentHashMap<>();
             boolean parallelClasses = isParallelTypes();
             boolean parallelBoth = isParallelMethodsAndTypes();
-            TestReportListener listener = createInstance( testSetMap, reporterFactory, parallelClasses, parallelBoth );
-            startCapture( listener );
-            return new JUnitCoreRunListener( listener, testSetMap );
+            ConcurrentRunListener concurrentListener =
+                createInstance( testSetMap, reporterFactory, parallelClasses, parallelBoth );
+            listener = new JUnitCoreRunListener( concurrentListener, testSetMap );
         }
+
+        startCapture( listener );
+        return listener;
     }
 
     private boolean isParallelMethodsAndTypes()
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListener.java
index 93f4bc0..f4fd0a9 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListener.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListener.java
@@ -19,16 +19,15 @@ package org.apache.maven.surefire.junitcore;
  * under the License.
  */
 
-import org.apache.maven.surefire.api.report.TestReportListener;
+import java.util.Map;
+
+import org.apache.maven.surefire.api.report.StackTraceWriter;
 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
 import org.apache.maven.surefire.common.junit4.JUnit4StackTraceWriter;
-import org.apache.maven.surefire.api.report.StackTraceWriter;
 import org.junit.runner.Description;
 import org.junit.runner.Result;
 import org.junit.runner.notification.Failure;
 
-import java.util.Map;
-
 import static org.apache.maven.surefire.api.util.internal.TestClassMethodNameUtils.extractClassName;
 
 /**
@@ -49,7 +48,7 @@ final class JUnitCoreRunListener
      * @param reporter          the report manager to log testing events to
      * @param classMethodCounts A map of methods
      */
-    JUnitCoreRunListener( TestReportListener reporter, Map<String, TestSet> classMethodCounts )
+    JUnitCoreRunListener( ConcurrentRunListener reporter, Map<String, TestSet> classMethodCounts )
     {
         super( reporter );
         this.classMethodCounts = classMethodCounts;
@@ -107,7 +106,7 @@ final class JUnitCoreRunListener
                 }
                 else
                 {
-                    testSet = new TestSet( testClassName );
+                    testSet = new TestSet( testClassName, getRunMode(), classMethodIndexer );
                     classMethodCounts.put( testClassName, testSet );
                 }
                 testSet.incrementTestMethodCount();
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/LogicalStream.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/LogicalStream.java
index 137812e..a7bcc6d 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/LogicalStream.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/LogicalStream.java
@@ -19,15 +19,16 @@ package org.apache.maven.surefire.junitcore;
  * under the License.
  */
 
-import org.apache.maven.surefire.api.report.TestOutputReceiver;
-import org.apache.maven.surefire.api.report.TestOutputReportEntry;
-
 import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
+import org.apache.maven.surefire.api.report.TestOutputReceiver;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
+
 /**
  * A stream-like object that preserves ordering between stdout/stderr
  */
+@Deprecated // remove this class after StatelessXmlReporter is capable of parallel test sets processing
 final class LogicalStream
 {
     private final Queue<TestOutputReportEntry> output = new ConcurrentLinkedQueue<>();
@@ -37,7 +38,7 @@ final class LogicalStream
         output.add( reportEntry );
     }
 
-    void writeDetails( TestOutputReceiver outputReceiver )
+    void writeDetails( TestOutputReceiver<TestOutputReportEntry> outputReceiver )
     {
         for ( TestOutputReportEntry entry = output.poll(); entry != null; entry = output.poll() )
         {
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/MethodsParallelRunListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/MethodsParallelRunListener.java
index d60e0ef..822fb80 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/MethodsParallelRunListener.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/MethodsParallelRunListener.java
@@ -26,6 +26,7 @@ import java.util.Map;
 /**
  * @author Kristian Rosenvold
  */
+@Deprecated // remove this class after StatelessXmlReporter is capable of parallel test sets processing
 final class MethodsParallelRunListener
     extends ConcurrentRunListener
 {
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java
index 1248a33..f3b121b 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java
@@ -40,6 +40,7 @@ import static org.apache.maven.surefire.api.util.internal.ObjectUtils.systemProp
  * limitation a la Junit4 provider. Specifically, we can redirect properly the output even if we don't have class
  * demarcation in JUnit. It works when if there is a JVM instance per test run, i.e. with forkMode=always or perthread.
  */
+@Deprecated // remove this class after StatelessXmlReporter is capable of parallel test sets processing
 class NonConcurrentRunListener
     extends JUnit4RunListener
 {
@@ -47,28 +48,18 @@ class NonConcurrentRunListener
 
     private Description lastFinishedDescription;
 
-    NonConcurrentRunListener( TestReportListener reporter )
+    NonConcurrentRunListener( TestReportListener<TestOutputReportEntry> reporter )
     {
         super( reporter );
     }
 
-    public synchronized void writeTestOutput( TestOutputReportEntry reportEntry )
-    {
-        // We can write immediately: no parallelism and a single class.
-        reporter.writeTestOutput( new TestOutputReportEntry( reportEntry, /*todo*/ null, 0L ) );
-    }
-
-    @Override
-    protected SimpleReportEntry createReportEntry( Description description )
-    {
-        ClassMethod classMethod = toClassMethod( description );
-        return new SimpleReportEntry( classMethod.getClazz(), null, classMethod.getMethod(), null );
-    }
-
     private TestSetReportEntry createReportEntryForTestSet( Description description, Map<String, String> systemProps )
     {
         ClassMethod classMethod = toClassMethod( description );
-        return new SimpleReportEntry( classMethod.getClazz(), null, null, null, systemProps );
+        String className = classMethod.getClazz();
+        String methodName = classMethod.getMethod();
+        long testRunId = classMethodIndexer.indexClassMethod( className, methodName );
+        return new SimpleReportEntry( getRunMode(), testRunId, className, null, null, null, systemProps );
     }
 
     private TestSetReportEntry createTestSetReportEntryStarted( Description description )
@@ -96,8 +87,7 @@ class NonConcurrentRunListener
             currentTestSetDescription = description;
             if ( lastFinishedDescription != null )
             {
-                TestSetReportEntry reportEntry = createTestSetReportEntryFinished( lastFinishedDescription );
-                reporter.testSetCompleted( reportEntry );
+                reporter.testSetCompleted( createTestSetReportEntryFinished( lastFinishedDescription ) );
                 lastFinishedDescription = null;
             }
             reporter.testSetStarting( createTestSetReportEntryStarted( description ) );
@@ -154,6 +144,8 @@ class NonConcurrentRunListener
     @Override
     public void testAssumptionFailure( Failure failure )
     {
+        finishLastTestSetIfNecessary( failure.getDescription() );
+
         super.testAssumptionFailure( failure );
         lastFinishedDescription = failure.getDescription();
     }
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestMethod.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestMethod.java
index 42faa39..142bd32 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestMethod.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestMethod.java
@@ -34,8 +34,9 @@ import java.util.concurrent.atomic.AtomicReference;
  * Notes about thread safety: This instance is serially confined to 1-3 threads (construction, test-run, reporting),
  * without any actual parallel access
  */
+@Deprecated // remove this class after StatelessXmlReporter is capable of parallel test sets processing
 class TestMethod
-    implements TestOutputReceiver
+    implements TestOutputReceiver<TestOutputReportEntry>
 {
     private static final InheritableThreadLocal<TestMethod> TEST_METHOD = new InheritableThreadLocal<>();
 
@@ -113,7 +114,7 @@ class TestMethod
         return endTime;
     }
 
-    void replay( TestReportListener reporter )
+    void replay( TestReportListener<TestOutputReportEntry> reporter )
     {
         if ( testIgnored != null )
         {
@@ -150,8 +151,9 @@ class TestMethod
 
     private ReportEntry createReportEntry( ReportEntry reportEntry )
     {
-        return new CategorizedReportEntry( reportEntry.getSourceName(), reportEntry.getName(), reportEntry.getGroup(),
-                                           reportEntry.getStackTraceWriter(), getElapsed(), reportEntry.getMessage() );
+        return new CategorizedReportEntry( reportEntry.getRunMode(), reportEntry.getTestRunId(),
+            reportEntry.getSourceName(), reportEntry.getName(), reportEntry.getGroup(),
+            reportEntry.getStackTraceWriter(), getElapsed(), reportEntry.getMessage() );
     }
 
     void attachToThread()
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestSet.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestSet.java
index d295bb7..f01c22d 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestSet.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestSet.java
@@ -20,9 +20,12 @@ package org.apache.maven.surefire.junitcore;
  */
 
 import org.apache.maven.surefire.api.report.ReportEntry;
+import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
+import org.apache.maven.surefire.report.ClassMethodIndexer;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -36,6 +39,7 @@ import static org.apache.maven.surefire.api.util.internal.ObjectUtils.systemProp
 /**
  * * Represents the test-state of a testset that is run.
  */
+@Deprecated // remove this class after StatelessXmlReporter is capable of parallel test sets processing
 public class TestSet
 {
     private static final InheritableThreadLocal<TestSet> TEST_SET = new InheritableThreadLocal<>();
@@ -53,14 +57,30 @@ public class TestSet
 
     private final AtomicInteger numberOfTests = new AtomicInteger();
 
+    private final RunMode runMode;
+
+    private final ClassMethodIndexer classMethodIndexer;
+
     private volatile boolean allScheduled;
 
-    public TestSet( String testClassName )
+    public TestSet( String testClassName, RunMode runMode, ClassMethodIndexer classMethodIndexer )
     {
         this.testClassName = testClassName;
+        this.runMode = runMode;
+        this.classMethodIndexer = classMethodIndexer;
+    }
+
+    final RunMode getRunMode()
+    {
+        return runMode;
+    }
+
+    final ClassMethodIndexer getClassMethodIndexer()
+    {
+        return classMethodIndexer;
     }
 
-    public void replay( TestReportListener target )
+    public void replay( TestReportListener<TestOutputReportEntry> target )
     {
         if ( played.compareAndSet( false, true ) )
         {
@@ -120,7 +140,8 @@ public class TestSet
 
     private TestSetReportEntry createReportEntry( Integer elapsed, Map<String, String> systemProps )
     {
-        return new SimpleReportEntry( testClassName, null, testClassName, null, null, elapsed, systemProps );
+        return new SimpleReportEntry( runMode, classMethodIndexer.indexClass( testClassName ), testClassName, null,
+            testClassName, null, null, elapsed, systemProps );
     }
 
     public void incrementTestMethodCount()
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ConcurrentRunListenerTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ConcurrentRunListenerTest.java
index a3a9944..7605b55 100644
--- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ConcurrentRunListenerTest.java
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ConcurrentRunListenerTest.java
@@ -29,7 +29,6 @@ import junit.framework.TestCase;
 import junit.framework.TestSuite;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.surefire.api.report.ReporterFactory;
-import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
 import org.apache.maven.surefire.report.RunStatistics;
 import org.junit.Ignore;
@@ -146,7 +145,8 @@ public class ConcurrentRunListenerTest
     {
         DefaultReporterFactory reporterFactory = createReporterFactory();
         HashMap<String, TestSet> classMethodCounts = new HashMap<>();
-        TestReportListener reporter = new ClassesParallelRunListener( classMethodCounts, reporterFactory );
+        ConcurrentRunListener reporter =
+            new ClassesParallelRunListener( classMethodCounts, reporterFactory );
         JUnitCoreRunListener runListener = new JUnitCoreRunListener( reporter, classMethodCounts );
         RunStatistics result = runClasses( reporterFactory, runListener, classes );
         assertReporter( result, success, ignored, failure, "classes" );
@@ -195,7 +195,7 @@ public class ConcurrentRunListenerTest
     private org.junit.runner.notification.RunListener createRunListener( ReporterFactory reporterFactory,
                                                                          Map<String, TestSet> testSetMap )
     {
-        TestReportListener handler = new ClassesParallelRunListener( testSetMap, reporterFactory );
+        ConcurrentRunListener handler = new ClassesParallelRunListener( testSetMap, reporterFactory );
         return new JUnitCoreRunListener( handler, testSetMap );
     }
 
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreTester.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreTester.java
index f3cea12..8153689 100644
--- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreTester.java
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreTester.java
@@ -26,13 +26,13 @@ import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoR
 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.surefire.api.report.ReporterFactory;
-import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
 import org.junit.runner.Computer;
 import org.junit.runner.JUnitCore;
 import org.junit.runner.Result;
 
 import java.io.File;
+import java.io.PrintStream;
 import java.util.HashMap;
 import java.util.concurrent.ExecutionException;
 
@@ -60,15 +60,15 @@ public class JUnitCoreTester
         throws TestSetFailedException, ExecutionException
     {
         ReporterFactory reporterManagerFactory = defaultNoXml();
-
+        PrintStream out = System.out;
+        PrintStream err = System.err;
         try
         {
-            final HashMap<String, TestSet> classMethodCounts = new HashMap<>();
-            TestReportListener reporter =
+            HashMap<String, TestSet> classMethodCounts = new HashMap<>();
+            ConcurrentRunListener reporter =
                 createInstance( classMethodCounts, reporterManagerFactory, parallelClasses, false );
-            startCapture( reporter );
-
             JUnitCoreRunListener runListener = new JUnitCoreRunListener( reporter, classMethodCounts );
+            startCapture( runListener );
             JUnitCore junitCore = new JUnitCore();
 
             junitCore.addListener( runListener );
@@ -78,6 +78,8 @@ public class JUnitCoreTester
         }
         finally
         {
+            System.setOut( out );
+            System.setErr( err );
             reporterManagerFactory.close();
             if ( computer instanceof ConfigurableParallelComputer )
             {
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/MockReporter.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/MockReporter.java
index 3552a46..1fa59e0 100644
--- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/MockReporter.java
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/MockReporter.java
@@ -22,17 +22,20 @@ package org.apache.maven.surefire.junitcore;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.maven.surefire.api.report.ReportEntry;
+import org.apache.maven.surefire.api.report.ReporterFactory;
 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
-import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.report.TestSetReportEntry;
-import org.apache.maven.surefire.api.report.RunMode;
+
+import static java.util.Collections.emptyMap;
+import static org.mockito.Mockito.mock;
 
 /**
  * Internal tests use only.
  */
 final class MockReporter
-        implements TestReportListener
+        extends ConcurrentRunListener
 {
     private final List<String> events = new ArrayList<>();
 
@@ -56,6 +59,7 @@ final class MockReporter
 
     MockReporter()
     {
+        super( mock( ReporterFactory.class ), false, emptyMap() );
     }
 
     @Override
@@ -84,26 +88,21 @@ final class MockReporter
     }
 
     @Override
-    public void testSkipped( ReportEntry report )
+    protected void checkIfTestSetCanBeReported( TestSet testSetForTest )
     {
-        events.add( TEST_SKIPPED );
-        testIgnored.incrementAndGet();
-    }
 
-    @Override
-    public void testExecutionSkippedByUser()
-    {
     }
 
     @Override
-    public RunMode markAs( RunMode currentRunMode )
+    public void testSkipped( ReportEntry report )
     {
-        return null;
+        events.add( TEST_SKIPPED );
+        testIgnored.incrementAndGet();
     }
 
-    public void testSkippedByUser( ReportEntry report )
+    @Override
+    public void testExecutionSkippedByUser()
     {
-        testSkipped( report );
     }
 
     public int getTestSucceeded()
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
index f9d5ce1..fab0367 100644
--- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
@@ -49,7 +49,6 @@ import org.apache.maven.surefire.api.booter.BaseProviderFactory;
 import org.apache.maven.surefire.api.booter.ProviderParameterNames;
 import org.apache.maven.surefire.api.report.ReporterConfiguration;
 import org.apache.maven.surefire.api.report.ReporterFactory;
-import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.suite.RunResult;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
 import org.apache.maven.surefire.api.util.TestsToRun;
@@ -121,7 +120,7 @@ public class Surefire746Test
 
         final Map<String, TestSet> testSetMap = new ConcurrentHashMap<>();
 
-        TestReportListener listener = createInstance( testSetMap, reporterFactory, false, false );
+        ConcurrentRunListener listener = createInstance( testSetMap, reporterFactory, false, false );
 
         TestsToRun testsToRun = new TestsToRun( Collections.<Class<?>>singleton( TestClassTest.class ) );
 
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/TestMethodTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/TestMethodTest.java
index 317f587..cd42fe9 100644
--- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/TestMethodTest.java
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/TestMethodTest.java
@@ -24,6 +24,8 @@ import org.apache.maven.surefire.api.report.SimpleReportEntry;
 
 import junit.framework.TestCase;
 
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
+
 /**
  * @author Kristian Rosenvold
  */
@@ -35,8 +37,9 @@ public class TestMethodTest
 
     public void testTestFailure()
     {
-        ReportEntry reportEntry = new SimpleReportEntry( "a", null, "b", null );
-        TestMethod testMethod = new TestMethod( reportEntry, new TestSet( TestMethodTest.class.getName() ) );
+        ReportEntry reportEntry = new SimpleReportEntry( NORMAL_RUN, 0L, "a", null, "b", null );
+        TestSet testSet = new TestSet( TestMethodTest.class.getName(), NORMAL_RUN, null );
+        TestMethod testMethod = new TestMethod( reportEntry, testSet );
         testMethod.testFailure( reportEntry );
         final int elapsed = testMethod.getElapsed();
         assertTrue( elapsed >= 0 );
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/ConfigurationAwareTestNGReporter.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/ConfigurationAwareTestNGReporter.java
index bc92979..319eed4 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/ConfigurationAwareTestNGReporter.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/ConfigurationAwareTestNGReporter.java
@@ -19,9 +19,8 @@ package org.apache.maven.surefire.testng;
  * under the License.
  */
 
-
-import org.apache.maven.surefire.api.report.RunListener;
-
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
+import org.apache.maven.surefire.api.report.TestReportListener;
 import org.testng.internal.IResultListener;
 
 /**
@@ -34,7 +33,7 @@ public class ConfigurationAwareTestNGReporter
     implements IResultListener
 {
 
-    public ConfigurationAwareTestNGReporter( RunListener reportManager )
+    public ConfigurationAwareTestNGReporter( TestReportListener<TestOutputReportEntry> reportManager )
     {
         super( reportManager );
     }
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
index f2bdbc7..e9ede9d 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
@@ -23,17 +23,16 @@ import java.io.File;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.maven.surefire.api.cli.CommandLineOption;
-import org.apache.maven.surefire.api.report.RunListener;
 import org.apache.maven.surefire.api.testset.TestListResolver;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
 import org.apache.maven.surefire.api.util.TestsToRun;
 
+import static java.util.Collections.singleton;
 import static org.apache.maven.surefire.testng.TestNGExecutor.run;
 import static org.apache.maven.surefire.shared.utils.StringUtils.isBlank;
 
@@ -82,45 +81,45 @@ final class TestNGDirectoryTestSuite
         this.skipAfterFailureCount = skipAfterFailureCount;
     }
 
-    void execute( TestsToRun testsToRun, RunListener reporterManager )
+    void execute( TestsToRun testsToRun, TestNGReporter testNGReporter )
         throws TestSetFailedException
     {
         if ( !testsToRun.allowEagerReading() )
         {
-            executeLazy( testsToRun, reporterManager );
+            executeLazy( testsToRun, testNGReporter );
         }
         else if ( testsToRun.containsAtLeast( 2 ) )
         {
-            executeMulti( testsToRun, reporterManager );
+            executeMulti( testsToRun, testNGReporter );
         }
         else if ( testsToRun.containsAtLeast( 1 ) )
         {
             Class<?> testClass = testsToRun.iterator().next();
-            executeSingleClass( reporterManager, testClass );
+            executeSingleClass( testNGReporter, testClass );
         }
     }
 
-    private void executeSingleClass( RunListener reporter, Class<?> testClass )
+    private void executeSingleClass( TestNGReporter testNGReporter, Class<?> testClass )
         throws TestSetFailedException
     {
         options.put( "suitename", testClass.getName() );
 
-        startTestSuite( reporter );
+        startTestSuite( testNGReporter.getRunListener() );
 
         Map<String, String> optionsToUse = isJUnitTest( testClass ) ? junitOptions : options;
 
-        run( Collections.<Class<?>>singleton( testClass ), testSourceDirectory, optionsToUse, reporter,
-                reportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
+        run( singleton( testClass ), testSourceDirectory, optionsToUse, testNGReporter,
+            reportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
 
-        finishTestSuite( reporter );
+        finishTestSuite( testNGReporter.getRunListener() );
     }
 
-    private void executeLazy( TestsToRun testsToRun, RunListener reporterManager )
+    private void executeLazy( TestsToRun testsToRun, TestNGReporter testNGReporter )
         throws TestSetFailedException
     {
         for ( Class<?> testToRun : testsToRun )
         {
-            executeSingleClass( reporterManager, testToRun );
+            executeSingleClass( testNGReporter, testToRun );
         }
     }
 
@@ -164,7 +163,7 @@ final class TestNGDirectoryTestSuite
         }
     }
 
-    private void executeMulti( TestsToRun testsToRun, RunListener reporterManager )
+    private void executeMulti( TestsToRun testsToRun, TestNGReporter testNGReporter )
         throws TestSetFailedException
     {
         List<Class<?>> testNgTestClasses = new ArrayList<>();
@@ -188,18 +187,18 @@ final class TestNGDirectoryTestSuite
             testNgReportsDirectory = new File( reportsDirectory, "testng-native-results" );
             junitReportsDirectory = new File( reportsDirectory, "testng-junit-results" );
         }
-        startTestSuite( reporterManager );
+        startTestSuite( testNGReporter.getRunListener() );
 
-        run( testNgTestClasses, testSourceDirectory, options, reporterManager,
+        run( testNgTestClasses, testSourceDirectory, options, testNGReporter,
                 testNgReportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
 
         if ( !junitTestClasses.isEmpty() )
         {
-            run( junitTestClasses, testSourceDirectory, junitOptions, reporterManager,
+            run( junitTestClasses, testSourceDirectory, junitOptions, testNGReporter,
                     junitReportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
         }
 
-        finishTestSuite( reporterManager );
+        finishTestSuite( testNGReporter.getRunListener() );
     }
 
     private boolean isJUnitTest( Class<?> c )
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
index 54fea3d..7ed1719 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
@@ -41,7 +41,6 @@ import org.testng.xml.XmlTest;
 import java.io.File;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -94,10 +93,10 @@ final class TestNGExecutor
 
     @SuppressWarnings( "checkstyle:parameternumbercheck" )
     static void run( Iterable<Class<?>> testClasses, String testSourceDirectory,
-                            Map<String, String> options, // string,string because TestNGMapConfigurator#configure()
-                            RunListener reportManager, File reportsDirectory,
-                            TestListResolver methodFilter, List<CommandLineOption> mainCliOptions,
-                            int skipAfterFailureCount )
+                     Map<String, String> options, // string,string because TestNGMapConfigurator#configure()
+                     TestNGReporter testNGReporter, File reportsDirectory,
+                     TestListResolver methodFilter, List<CommandLineOption> mainCliOptions,
+                     int skipAfterFailureCount )
         throws TestSetFailedException
     {
         TestNG testng = new TestNG( true );
@@ -147,7 +146,7 @@ final class TestNGExecutor
 
         testng.setXmlSuites( xmlSuites );
         configurator.configure( testng, options );
-        postConfigure( testng, testSourceDirectory, reportManager, reportsDirectory, skipAfterFailureCount,
+        postConfigure( testng, testSourceDirectory, testNGReporter, reportsDirectory, skipAfterFailureCount,
                        extractVerboseLevel( options ) );
         testng.run();
     }
@@ -312,14 +311,14 @@ final class TestNGExecutor
     }
 
     static void run( List<String> suiteFiles, String testSourceDirectory,
-                            Map<String, String> options, // string,string because TestNGMapConfigurator#configure()
-                            RunListener reportManager, File reportsDirectory, int skipAfterFailureCount )
+                     Map<String, String> options, // string,string because TestNGMapConfigurator#configure()
+                     TestNGReporter testNGReporter, File reportsDirectory, int skipAfterFailureCount )
         throws TestSetFailedException
     {
         TestNG testng = new TestNG( true );
         Configurator configurator = getConfigurator( options.get( "testng.configurator" ) );
         configurator.configure( testng, options );
-        postConfigure( testng, testSourceDirectory, reportManager, reportsDirectory, skipAfterFailureCount,
+        postConfigure( testng, testSourceDirectory, testNGReporter, reportsDirectory, skipAfterFailureCount,
                        extractVerboseLevel( options ) );
         testng.setTestSuites( suiteFiles );
         testng.run();
@@ -337,20 +336,19 @@ final class TestNGExecutor
         }
     }
 
-    private static void postConfigure( TestNG testNG, String sourcePath, final RunListener reportManager,
+    private static void postConfigure( TestNG testNG, String sourcePath, TestNGReporter testNGReporter,
                                        File reportsDirectory, int skipAfterFailureCount, int verboseLevel )
     {
         // 0 (default): turn off all TestNG output
         testNG.setVerbose( verboseLevel );
-
-        TestNGReporter reporter = createTestNGReporter( reportManager );
-        testNG.addListener( (ITestNGListener) reporter );
+        testNG.addListener( (ITestNGListener) testNGReporter );
 
         if ( skipAfterFailureCount > 0 )
         {
             ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
             testNG.addListener( instantiate( classLoader, FailFastNotifier.class.getName(), Object.class ) );
-            testNG.addListener( new FailFastListener( createStoppable( reportManager, skipAfterFailureCount ) ) );
+            testNG.addListener(
+                new FailFastListener( createStoppable( testNGReporter.getRunListener(), skipAfterFailureCount ) ) );
         }
 
         // FIXME: use classifier to decide if we need to pass along the source dir (only for JDK14)
@@ -373,31 +371,6 @@ final class TestNGExecutor
         };
     }
 
-    // If we have access to IResultListener, return a ConfigurationAwareTestNGReporter
-    // But don't cause NoClassDefFoundErrors if it isn't available; just return a regular TestNGReporter instead
-    private static TestNGReporter createTestNGReporter( RunListener reportManager )
-    {
-        try
-        {
-            Class.forName( "org.testng.internal.IResultListener" );
-            Class<?> c = Class.forName( "org.apache.maven.surefire.testng.ConfigurationAwareTestNGReporter" );
-            Constructor<?> ctor = c.getConstructor( RunListener.class );
-            return (TestNGReporter) ctor.newInstance( reportManager );
-        }
-        catch ( InvocationTargetException e )
-        {
-            throw new RuntimeException( "Bug in ConfigurationAwareTestNGReporter", e.getCause() );
-        }
-        catch ( ClassNotFoundException e )
-        {
-            return new TestNGReporter( reportManager );
-        }
-        catch ( Exception e )
-        {
-            throw new RuntimeException( "Bug in ConfigurationAwareTestNGReporter", e );
-        }
-    }
-
     private static int extractVerboseLevel( Map<String, String> options )
         throws TestSetFailedException
     {
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
index fabe13a..0ca1403 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
@@ -19,30 +19,34 @@ package org.apache.maven.surefire.testng;
  * under the License.
  */
 
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.maven.surefire.api.booter.Command;
-import org.apache.maven.surefire.api.provider.CommandChainReader;
-import org.apache.maven.surefire.api.provider.CommandListener;
 import org.apache.maven.surefire.api.cli.CommandLineOption;
 import org.apache.maven.surefire.api.provider.AbstractProvider;
+import org.apache.maven.surefire.api.provider.CommandChainReader;
+import org.apache.maven.surefire.api.provider.CommandListener;
 import org.apache.maven.surefire.api.provider.ProviderParameters;
 import org.apache.maven.surefire.api.report.ReporterConfiguration;
 import org.apache.maven.surefire.api.report.ReporterFactory;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
 import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.suite.RunResult;
-import org.apache.maven.surefire.testng.utils.FailFastEventsSingleton;
 import org.apache.maven.surefire.api.testset.TestListResolver;
 import org.apache.maven.surefire.api.testset.TestRequest;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
 import org.apache.maven.surefire.api.util.RunOrderCalculator;
 import org.apache.maven.surefire.api.util.ScanResult;
 import org.apache.maven.surefire.api.util.TestsToRun;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
+import org.apache.maven.surefire.testng.utils.FailFastEventsSingleton;
 
 import static org.apache.maven.surefire.api.report.ConsoleOutputCapture.startCapture;
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.apache.maven.surefire.api.testset.TestListResolver.getEmptyTestListResolver;
 import static org.apache.maven.surefire.api.testset.TestListResolver.optionallyWildcardFilter;
 import static org.apache.maven.surefire.api.util.TestsToRun.fromClass;
@@ -71,8 +75,6 @@ public class TestNGProvider
 
     private final CommandChainReader commandsReader;
 
-    private TestsToRun testsToRun;
-
     public TestNGProvider( ProviderParameters bootParams )
     {
         // don't start a thread in CommandReader while we are in in-plugin process
@@ -96,43 +98,52 @@ public class TestNGProvider
             registerPleaseStopListener();
         }
 
-        final ReporterFactory reporterFactory = providerParameters.getReporterFactory();
-        final TestReportListener reporter = reporterFactory.createTestReportListener();
-        /*
-         * {@link org.apache.maven.surefire.api.report.ConsoleOutputCapture#startCapture(ConsoleOutputReceiver)}
-         * called in prior to initializing variable {@link #testsToRun}
-         */
-        startCapture( reporter );
+        ReporterFactory reporterFactory = providerParameters.getReporterFactory();
+        TestReportListener<TestOutputReportEntry> reporter = reporterFactory.createTestReportListener();
 
         RunResult runResult;
         try
         {
             if ( isTestNGXmlTestSuite( testRequest ) )
             {
+                TestNGReporter testNGReporter = createTestNGReporter( reporter );
+                testNGReporter.setRunMode( NORMAL_RUN );
+                /*
+                 * {@link org.apache.maven.surefire.api.report.ConsoleOutputCapture#startCapture(ConsoleOutputReceiver)}
+                 * called in prior to initializing variable {@link #testsToRun}
+                 */
+                startCapture( testNGReporter );
+
                 if ( commandsReader != null )
                 {
                     commandsReader.awaitStarted();
                 }
                 TestNGXmlTestSuite testNGXmlTestSuite = newXmlSuite();
                 testNGXmlTestSuite.locateTestSets();
-                testNGXmlTestSuite.execute( reporter );
+                testNGXmlTestSuite.execute( testNGReporter );
             }
             else
             {
-                if ( testsToRun == null )
+                TestNGReporter testNGReporter = createTestNGReporter( reporter );
+                testNGReporter.setRunMode( NORMAL_RUN );
+                /*
+                 * {@link org.apache.maven.surefire.api.report.ConsoleOutputCapture#startCapture(ConsoleOutputReceiver)}
+                 * called in prior to initializing variable {@link #testsToRun}
+                 */
+                startCapture( testNGReporter );
+
+                final TestsToRun testsToRun;
+                if ( forkTestSet instanceof TestsToRun )
+                {
+                    testsToRun = (TestsToRun) forkTestSet;
+                }
+                else if ( forkTestSet instanceof Class )
+                {
+                    testsToRun = fromClass( (Class<?>) forkTestSet );
+                }
+                else
                 {
-                    if ( forkTestSet instanceof TestsToRun )
-                    {
-                        testsToRun = (TestsToRun) forkTestSet;
-                    }
-                    else if ( forkTestSet instanceof Class )
-                    {
-                        testsToRun = fromClass( (Class<?>) forkTestSet );
-                    }
-                    else
-                    {
-                        testsToRun = scanClassPath();
-                    }
+                    testsToRun = scanClassPath();
                 }
 
                 if ( commandsReader != null )
@@ -141,7 +152,7 @@ public class TestNGProvider
                     commandsReader.awaitStarted();
                 }
                 TestNGDirectoryTestSuite suite = newDirectorySuite();
-                suite.execute( testsToRun, reporter );
+                suite.execute( testsToRun, testNGReporter );
             }
         }
         finally
@@ -223,8 +234,7 @@ public class TestNGProvider
         }
         else
         {
-            testsToRun = scanClassPath();
-            return testsToRun;
+            return scanClassPath();
         }
     }
 
@@ -245,4 +255,29 @@ public class TestNGProvider
         TestListResolver filter = optionallyWildcardFilter( testRequest.getTestListResolver() );
         return filter.isWildcard() ? getEmptyTestListResolver() : filter;
     }
+
+    // If we have access to IResultListener, return a ConfigurationAwareTestNGReporter.
+    // But don't cause NoClassDefFoundErrors if it isn't available; just return a regular TestNGReporter instead.
+    private static TestNGReporter createTestNGReporter( TestReportListener<TestOutputReportEntry> reportManager )
+    {
+        try
+        {
+            Class.forName( "org.testng.internal.IResultListener" );
+            Class<?> c = Class.forName( "org.apache.maven.surefire.testng.ConfigurationAwareTestNGReporter" );
+            Constructor<?> ctor = c.getConstructor( TestReportListener.class );
+            return (TestNGReporter) ctor.newInstance( reportManager );
+        }
+        catch ( InvocationTargetException e )
+        {
+            throw new RuntimeException( "Bug in ConfigurationAwareTestNGReporter", e.getCause() );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            return new TestNGReporter( reportManager );
+        }
+        catch ( Exception e )
+        {
+            throw new RuntimeException( "Bug in ConfigurationAwareTestNGReporter", e );
+        }
+    }
 }
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGReporter.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGReporter.java
index fcbea5d..4e34f73 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGReporter.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGReporter.java
@@ -20,11 +20,19 @@ package org.apache.maven.surefire.testng;
  */
 
 import org.apache.maven.surefire.api.report.CategorizedReportEntry;
+import org.apache.maven.surefire.api.report.OutputReportEntry;
+import org.apache.maven.surefire.api.report.RunMode;
+import org.apache.maven.surefire.api.report.StackTraceWriter;
+import org.apache.maven.surefire.api.report.TestOutputReceiver;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
+import org.apache.maven.surefire.api.report.TestReportListener;
+import org.apache.maven.surefire.report.ClassMethodIndexer;
 import org.apache.maven.surefire.report.PojoStackTraceWriter;
 import org.apache.maven.surefire.api.report.ReportEntry;
 import org.apache.maven.surefire.api.report.RunListener;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
 
+import org.apache.maven.surefire.report.RunModeSetter;
 import org.testng.IClass;
 import org.testng.ISuite;
 import org.testng.ISuiteListener;
@@ -45,9 +53,11 @@ import static org.apache.maven.surefire.api.report.SimpleReportEntry.withExcepti
  * @author jkuhnert
  */
 public class TestNGReporter
-    implements ITestListener, ISuiteListener
+    implements TestOutputReceiver<OutputReportEntry>, ITestListener, ISuiteListener, RunModeSetter
 {
-    private final RunListener reporter;
+    private final ClassMethodIndexer classMethodIndexer = new ClassMethodIndexer();
+    private final TestReportListener<TestOutputReportEntry> reporter;
+    private volatile RunMode runMode;
 
     /**
      * Constructs a new instance that will listen to
@@ -59,23 +69,33 @@ public class TestNGReporter
      *
      * @param reportManager Instance to report suite status to
      */
-    public TestNGReporter( RunListener reportManager )
+    public TestNGReporter( TestReportListener<TestOutputReportEntry> reportManager )
     {
         this.reporter = reportManager;
     }
 
+    protected final RunListener getRunListener()
+    {
+        return reporter;
+    }
+
     @Override
     public void onTestStart( ITestResult result )
     {
-        String clazz = result.getTestClass().getName();
-        String group = groupString( result.getMethod().getGroups(), clazz );
-        reporter.testStarting( new CategorizedReportEntry( clazz, testName( result ), group ) );
+        String className = result.getTestClass().getName();
+        String methodName = testName( result );
+        long testRunId = classMethodIndexer.indexClassMethod( className, methodName );
+        String group = groupString( result.getMethod().getGroups(), className );
+        reporter.testStarting( new CategorizedReportEntry( runMode, testRunId, className, methodName, group ) );
     }
 
     @Override
     public void onTestSuccess( ITestResult result )
     {
-        ReportEntry report = new SimpleReportEntry( result.getTestClass().getName(), null, testName( result ), null );
+        String className = result.getTestClass().getName();
+        String methodName = testName( result );
+        long testRunId = classMethodIndexer.indexClassMethod( className, methodName );
+        ReportEntry report = new SimpleReportEntry( runMode, testRunId, className, null, methodName, null );
         reporter.testSucceeded( report );
     }
 
@@ -83,9 +103,13 @@ public class TestNGReporter
     public void onTestFailure( ITestResult result )
     {
         IClass clazz = result.getTestClass();
-        ReportEntry report = withException( clazz.getName(), null, testName( result ), null,
-            new PojoStackTraceWriter( clazz.getRealClass().getName(), result.getMethod().getMethodName(),
-                result.getThrowable() ) );
+        String className = clazz.getName();
+        String methodName = testName( result );
+        long testRunId = classMethodIndexer.indexClassMethod( className, methodName );
+        StackTraceWriter stackTraceWriter = new PojoStackTraceWriter( clazz.getRealClass().getName(),
+            result.getMethod().getMethodName(), result.getThrowable() );
+        ReportEntry report = withException( runMode, testRunId, clazz.getName(), null, methodName,
+            null, stackTraceWriter );
 
         reporter.testFailed( report );
     }
@@ -93,10 +117,13 @@ public class TestNGReporter
     @Override
     public void onTestSkipped( ITestResult result )
     {
+        String className = result.getTestClass().getName();
+        String methodName = testName( result );
+        long testRunId = classMethodIndexer.indexClassMethod( className, methodName );
         //noinspection ThrowableResultOfMethodCallIgnored
         Throwable t = result.getThrowable();
         String reason = t == null ? null : t.getMessage();
-        ReportEntry report = ignored( result.getTestClass().getName(), null, testName( result ), null, reason );
+        ReportEntry report = ignored( runMode, testRunId, className, null, methodName, null, reason );
         reporter.testSkipped( report );
     }
 
@@ -104,10 +131,12 @@ public class TestNGReporter
     public void onTestFailedButWithinSuccessPercentage( ITestResult result )
     {
         IClass clazz = result.getTestClass();
-        ReportEntry report = withException( clazz.getName(), null, testName( result ), null,
-            new PojoStackTraceWriter( clazz.getRealClass().getName(), result.getMethod().getMethodName(),
-                result.getThrowable() ) );
-
+        String className = clazz.getName();
+        String methodName = testName( result );
+        long testRunId = classMethodIndexer.indexClassMethod( className, methodName );
+        StackTraceWriter stackTraceWriter = new PojoStackTraceWriter( clazz.getRealClass().getName(),
+            result.getMethod().getMethodName(), result.getThrowable() );
+        ReportEntry report = withException( runMode, testRunId, className, null, methodName, null, stackTraceWriter );
         reporter.testSucceeded( report );
     }
 
@@ -197,4 +226,17 @@ public class TestNGReporter
         return parameters == null || parameters.length == 0
             ? name : name + Arrays.toString( parameters ) + "(" + result.getMethod().getCurrentInvocationCount() + ")";
     }
+
+    @Override
+    public void setRunMode( RunMode runMode )
+    {
+        this.runMode = runMode;
+    }
+
+    @Override
+    public void writeTestOutput( OutputReportEntry reportEntry )
+    {
+        Long testRunId = classMethodIndexer.getLocalIndex();
+        reporter.writeTestOutput( new TestOutputReportEntry( reportEntry, runMode, testRunId ) );
+    }
 }
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
index bf7f991..53f6801 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
@@ -24,7 +24,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.maven.surefire.api.report.RunListener;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
 
 import static org.apache.maven.surefire.testng.TestNGExecutor.run;
@@ -64,16 +63,16 @@ final class TestNGXmlTestSuite
         this.skipAfterFailureCount = skipAfterFailureCount;
     }
 
-    void execute( RunListener reporter )
+    void execute( TestNGReporter testNGReporter )
         throws TestSetFailedException
     {
         if ( suiteFilePaths == null )
         {
             throw new IllegalStateException( "You must call locateTestSets before calling execute" );
         }
-        startTestSuite( reporter );
-        run( suiteFilePaths, testSourceDirectory, options, reporter, reportsDirectory, skipAfterFailureCount );
-        finishTestSuite( reporter );
+        startTestSuite( testNGReporter.getRunListener() );
+        run( suiteFilePaths, testSourceDirectory, options, testNGReporter, reportsDirectory, skipAfterFailureCount );
+        finishTestSuite( testNGReporter.getRunListener() );
     }
 
     Iterable locateTestSets()
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestSuite.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestSuite.java
index b78f797..73ddf84 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestSuite.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestSuite.java
@@ -22,10 +22,10 @@ package org.apache.maven.surefire.testng;
 import org.apache.maven.surefire.api.report.ReporterException;
 import org.apache.maven.surefire.api.report.RunListener;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
-import org.apache.maven.surefire.api.report.TestSetReportEntry;
 
 import java.util.Map;
 
+import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
 import static org.apache.maven.surefire.api.util.internal.ObjectUtils.systemProps;
 
 /**
@@ -43,11 +43,10 @@ abstract class TestSuite
 
     final void startTestSuite( RunListener reporterManager )
     {
-        TestSetReportEntry report = new SimpleReportEntry( getSuiteName(), null, null, null );
-
         try
         {
-            reporterManager.testSetStarting( report );
+            reporterManager.testSetStarting(
+                new SimpleReportEntry( NORMAL_RUN, 0L, getSuiteName(), null, null, null ) );
         }
         catch ( ReporterException e )
         {
@@ -57,7 +56,7 @@ abstract class TestSuite
 
     final void finishTestSuite( RunListener reporterManager )
     {
-        SimpleReportEntry report = new SimpleReportEntry( getSuiteName(), null, null, null, systemProps() );
-        reporterManager.testSetCompleted( report );
+        reporterManager.testSetCompleted(
+            new SimpleReportEntry( NORMAL_RUN, 0L, getSuiteName(), null, null, null, systemProps() ) );
     }
 }
diff --git a/surefire-providers/surefire-testng/src/test/java/org/apache/maven/surefire/testng/TestNGReporterTest.java b/surefire-providers/surefire-testng/src/test/java/org/apache/maven/surefire/testng/TestNGReporterTest.java
index 8f42b97..91068e7 100644
--- a/surefire-providers/surefire-testng/src/test/java/org/apache/maven/surefire/testng/TestNGReporterTest.java
+++ b/surefire-providers/surefire-testng/src/test/java/org/apache/maven/surefire/testng/TestNGReporterTest.java
@@ -21,8 +21,9 @@ package org.apache.maven.surefire.testng;
 
 import junit.framework.TestCase;
 import org.apache.maven.surefire.api.report.CategorizedReportEntry;
-import org.apache.maven.surefire.api.report.RunListener;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
+import org.apache.maven.surefire.api.report.TestOutputReportEntry;
+import org.apache.maven.surefire.api.report.TestReportListener;
 import org.mockito.ArgumentCaptor;
 import org.testng.ITestClass;
 import org.testng.ITestNGMethod;
@@ -38,6 +39,7 @@ import static org.powermock.reflect.Whitebox.invokeMethod;
 /**
  * Tests for {@link TestNGReporter}.
  */
+@SuppressWarnings( "checkstyle:magicnumber" )
 public class TestNGReporterTest extends TestCase
 {
     public void testParameterizedTestName() throws Exception
@@ -79,7 +81,7 @@ public class TestNGReporterTest extends TestCase
         when( testResult.getName() ).thenReturn( "myTest" );
         when( testResult.getParameters() ).thenReturn( new String[] { "val1", "val2" } );
 
-        RunListener listener = mock( RunListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
         TestNGReporter reporter = new TestNGReporter( listener );
         reporter.onTestStart( testResult );
 
@@ -87,6 +89,9 @@ public class TestNGReporterTest extends TestCase
         verify( listener ).testStarting( reportEntry.capture() );
         verifyNoMoreInteractions( listener );
 
+        assertThat( reportEntry.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000001L );
+
         assertThat( reportEntry.getValue().getSourceName() )
             .isEqualTo( "pkg.MyClass" );
 
@@ -108,7 +113,7 @@ public class TestNGReporterTest extends TestCase
         when( testResult.getName() ).thenReturn( "myTest" );
         when( testResult.getParameters() ).thenReturn( new String[] { "val1", "val2" } );
 
-        RunListener listener = mock( RunListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
         TestNGReporter reporter = new TestNGReporter( listener );
         reporter.onTestSuccess( testResult );
 
@@ -116,6 +121,9 @@ public class TestNGReporterTest extends TestCase
         verify( listener ).testSucceeded( reportEntry.capture() );
         verifyNoMoreInteractions( listener );
 
+        assertThat( reportEntry.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000001L );
+
         assertThat( reportEntry.getValue().getSourceName() )
             .isEqualTo( "pkg.MyClass" );
 
@@ -142,7 +150,7 @@ public class TestNGReporterTest extends TestCase
         when( testResult.getName() ).thenReturn( "myTest" );
         when( testResult.getParameters() ).thenReturn( new String[] { "val1", "val2" } );
 
-        RunListener listener = mock( RunListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
         TestNGReporter reporter = new TestNGReporter( listener );
         reporter.onTestFailure( testResult );
 
@@ -150,6 +158,9 @@ public class TestNGReporterTest extends TestCase
         verify( listener ).testFailed( reportEntry.capture() );
         verifyNoMoreInteractions( listener );
 
+        assertThat( reportEntry.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000001L );
+
         assertThat( reportEntry.getValue().getSourceName() )
             .isEqualTo( getClass().getName() );
 
@@ -180,7 +191,7 @@ public class TestNGReporterTest extends TestCase
         when( testResult.getName() ).thenReturn( "myTest" );
         when( testResult.getParameters() ).thenReturn( new String[] { "val1", "val2" } );
 
-        RunListener listener = mock( RunListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
         TestNGReporter reporter = new TestNGReporter( listener );
         reporter.onTestSkipped( testResult );
 
@@ -188,6 +199,9 @@ public class TestNGReporterTest extends TestCase
         verify( listener ).testSkipped( reportEntry.capture() );
         verifyNoMoreInteractions( listener );
 
+        assertThat( reportEntry.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000001L );
+
         assertThat( reportEntry.getValue().getSourceName() )
             .isEqualTo( getClass().getName() );
 
@@ -217,7 +231,7 @@ public class TestNGReporterTest extends TestCase
         when( testResult.getName() ).thenReturn( "myTest" );
         when( testResult.getParameters() ).thenReturn( new String[] { "val1", "val2" } );
 
-        RunListener listener = mock( RunListener.class );
+        TestReportListener<TestOutputReportEntry> listener = mock( TestReportListener.class );
         TestNGReporter reporter = new TestNGReporter( listener );
         reporter.onTestFailedButWithinSuccessPercentage( testResult );
 
@@ -225,6 +239,9 @@ public class TestNGReporterTest extends TestCase
         verify( listener ).testSucceeded( reportEntry.capture() );
         verifyNoMoreInteractions( listener );
 
+        assertThat( reportEntry.getValue().getTestRunId() )
+            .isEqualTo( 0x0000000100000001L );
+
         assertThat( reportEntry.getValue().getSourceName() )
             .isEqualTo( getClass().getName() );
 
diff --git a/surefire-shadefire/pom.xml b/surefire-shadefire/pom.xml
index 1c8c412..ea261e2 100644
--- a/surefire-shadefire/pom.xml
+++ b/surefire-shadefire/pom.xml
@@ -78,6 +78,7 @@
                   <include>org.apache.maven.surefire:surefire-api</include>
                   <include>org.apache.maven.surefire:surefire-extensions-spi</include>
                   <include>org.apache.maven.surefire:surefire-booter</include>
+                  <include>org.apache.maven.surefire:common-java5</include>
                   <include>org.apache.maven.surefire:common-junit3</include>
                   <include>org.apache.maven.surefire:surefire-junit3</include>
                 </includes>

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

Posted by ti...@apache.org.
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