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 2019/05/22 22:32:17 UTC

[maven-surefire] branch 1546-1222 updated (37733c7 -> 69020a3)

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

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


 discard 37733c7  change by request https://github.com/apache/maven-surefire/pull/226#discussion_r286049889
 discard 1b2e3b4  change by request https://github.com/apache/maven-surefire/pull/226#discussion_r286048530
 discard f603349  [SUREFIRE-1546] JUnit 5 runner does not honor JUnit 5 display names
     add 75f87ee  [SUREFIRE-1647] When using junit5, delay loading testClass and use myown classLoader
     new c832448  [SUREFIRE-1546] JUnit 5 runner does not honor JUnit 5 display names
     new 69020a3  removed temporary execution 'jacoco-report' extension SurefireConsoleOutputReporter should be used instead of ConsoleOutputReporter

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   (37733c7)
            \
             N -- N -- N   refs/heads/1546-1222 (69020a3)

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:
 maven-failsafe-plugin/pom.xml                                 |  8 --------
 maven-surefire-common/pom.xml                                 |  8 --------
 .../maven/plugin/surefire/StartupReportConfiguration.java     | 11 ++++-------
 maven-surefire-plugin/pom.xml                                 |  8 --------
 maven-surefire-report-plugin/pom.xml                          |  8 --------
 surefire-api/pom.xml                                          |  8 --------
 surefire-booter/pom.xml                                       |  8 --------
 surefire-extensions-api/pom.xml                               |  8 --------
 surefire-grouper/pom.xml                                      |  8 --------
 surefire-logger-api/pom.xml                                   |  8 --------
 surefire-providers/common-java5/pom.xml                       |  8 --------
 surefire-providers/common-junit3/pom.xml                      |  8 --------
 surefire-providers/common-junit4/pom.xml                      |  8 --------
 surefire-providers/common-junit48/pom.xml                     |  8 --------
 surefire-providers/surefire-junit-platform/pom.xml            |  8 --------
 .../maven/surefire/junitplatform/JUnitPlatformProvider.java   |  2 +-
 .../maven/surefire/junitplatform/TestPlanScannerFilter.java   |  2 +-
 surefire-providers/surefire-junit3/pom.xml                    |  8 --------
 surefire-providers/surefire-junit4/pom.xml                    |  8 --------
 surefire-providers/surefire-junit47/pom.xml                   |  8 --------
 surefire-providers/surefire-testng-utils/pom.xml              |  8 --------
 surefire-providers/surefire-testng/pom.xml                    |  8 --------
 surefire-report-parser/pom.xml                                |  8 --------
 23 files changed, 6 insertions(+), 169 deletions(-)


[maven-surefire] 01/02: [SUREFIRE-1546] JUnit 5 runner does not honor JUnit 5 display names

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c83244885402d9f65e394be9a99267e46bc554e7
Author: tibordigana <ti...@apache.org>
AuthorDate: Tue Apr 2 10:54:39 2019 +0200

    [SUREFIRE-1546] JUnit 5 runner does not honor JUnit 5 display names
---
 Jenkinsfile                                        |   3 -
 maven-failsafe-plugin/pom.xml                      |  30 +++
 .../plugin/failsafe/IntegrationTestMojoTest.java   |   2 +-
 maven-surefire-common/pom.xml                      |  67 +++++-
 .../plugin/surefire/AbstractSurefireMojo.java      |  50 ++++-
 .../maven/plugin/surefire/CommonReflector.java     |  32 ++-
 .../surefire/StartupReportConfiguration.java       | 118 ++++++----
 .../surefire/SurefireDependencyResolver.java       |   9 +-
 .../surefire/booterclient/output/ForkClient.java   |   3 +-
 .../booterclient/output/ForkedChannelDecoder.java  |  17 +-
 .../output/ThreadedStreamConsumer.java             |   2 +-
 .../DefaultStatelessReportMojoConfiguration.java   |  58 +++++
 .../extensions/SurefireConsoleOutputReporter.java  |  86 ++++++++
 .../extensions/SurefireStatelessReporter.java      |  98 +++++++++
 .../SurefireStatelessTestsetInfoReporter.java      |  87 ++++++++
 .../junit5/JUnit5ConsoleOutputReporter.java        |  92 ++++++++
 .../junit5/JUnit5StatelessTestsetInfoReporter.java | 148 +++++++++++++
 .../junit5/JUnit5Xml30StatelessReporter.java       | 161 ++++++++++++++
 .../surefire/report/ConsoleOutputFileReporter.java |  23 +-
 .../plugin/surefire/report/ConsoleReporter.java    |  43 ++--
 .../surefire/report/DefaultReporterFactory.java    |  36 +--
 .../surefire/report/DirectConsoleOutput.java       |  18 +-
 .../maven/plugin/surefire/report/FileReporter.java |  33 ++-
 ...eporter.java => NullConsoleOutputReceiver.java} |  35 +--
 .../surefire/report/NullConsoleReporter.java       |   6 +-
 .../plugin/surefire/report/NullFileReporter.java   |   2 +-
 .../surefire/report/NullStatelessXmlReporter.java  |   2 +-
 .../surefire/report/StatelessXmlReporter.java      |  39 +++-
 .../plugin/surefire/report/TestSetRunListener.java |  23 +-
 .../maven/plugin/surefire/report/TestSetStats.java |  39 ++--
 .../report/TestcycleConsoleOutputReceiver.java     |   9 +-
 .../plugin/surefire/report/WrappedReportEntry.java |  42 +++-
 .../maven/surefire/report/RunStatistics.java       |  21 +-
 .../AbstractSurefireMojoJava7PlusTest.java         |  12 +-
 .../plugin/surefire/AbstractSurefireMojoTest.java  |  25 ++-
 .../maven/plugin/surefire/CommonReflectorTest.java |  96 ++++++++
 .../maven/plugin/surefire/MojoMocklessTest.java    | 117 ++++++++++
 .../surefire/SurefireDependencyResolverTest.java   |   2 +-
 .../maven/plugin/surefire/SurefireHelperTest.java  |  47 ++++
 .../plugin/surefire/SurefireReflectorTest.java     |   2 +-
 .../booterclient/DefaultForkConfigurationTest.java |   2 +
 .../booterclient/ForkingRunListenerTest.java       |   4 +-
 .../JarManifestForkConfigurationTest.java          |   2 +
 .../booterclient/TestSetMockReporterFactory.java   |   8 +-
 .../booterclient/output/ForkClientTest.java        | 100 ++++++++-
 .../output/ForkedChannelDecoderTest.java           |  55 +++--
 .../report/DefaultReporterFactoryTest.java         | 152 ++++++++++++-
 .../surefire/report/StatelessXmlReporterTest.java  |  33 +--
 .../plugin/surefire/report/TestSetStatsTest.java   |  71 ++++++
 .../surefire/report/WrappedReportEntryTest.java    |  72 +++++-
 .../runorder/RunEntryStatisticsMapTest.java        |  20 +-
 .../org/apache/maven/surefire/JUnit4SuiteTest.java |  13 +-
 .../extensions/ConsoleOutputReporterTest.java      | 173 +++++++++++++++
 .../surefire/extensions/StatelessReporterTest.java | 244 +++++++++++++++++++++
 .../StatelessTestsetInfoReporterTest.java          | 229 +++++++++++++++++++
 .../report/ConsoleOutputFileReporterTest.java      |  32 +--
 .../maven/surefire/report/FileReporterTest.java    |   8 +-
 maven-surefire-plugin/pom.xml                      |  26 +++
 .../maven/plugin/surefire/SurefirePlugin.java      |   2 +-
 .../src/site/apt/examples/junit-platform.apt.vm    |  53 +++++
 .../src/site/apt/featurematrix.apt.vm              |  13 +-
 .../maven/plugin/surefire/SurefirePluginTest.java  | 117 ++++------
 maven-surefire-report-plugin/pom.xml               |  27 +++
 .../plugins/surefire/report/JUnit4SuiteTest.java   |  22 +-
 pom.xml                                            | 146 ++++++------
 surefire-api/pom.xml                               |  24 ++
 .../surefire/booter/ForkedChannelEncoder.java      |   4 +
 .../maven/surefire/booter/SurefireReflector.java   |  49 +----
 .../surefire/report/CategorizedReportEntry.java    |  31 ++-
 .../apache/maven/surefire/report/ReportEntry.java  |  21 ++
 .../maven/surefire/report/SimpleReportEntry.java   | 135 ++++++++----
 .../maven/surefire/util/ReflectionUtils.java       |   6 +
 .../java/org/apache/maven/JUnit4SuiteTest.java     |   2 +
 .../maven/surefire/booter/CommandReaderTest.java   |   4 +-
 .../java/org/apache/maven/surefire/booter/Foo.java |   0
 .../surefire/booter/ForkedChannelEncoderTest.java  | 108 ++++++---
 .../surefire/booter/NewClassLoaderRunner.java      |   7 +-
 .../surefire/booter/SurefireReflectorTest.java     | 151 ++++++++++++-
 .../maven/surefire/util/ReflectionUtilsTest.java   |   8 +
 surefire-booter/pom.xml                            |  36 ++-
 .../maven/surefire/booter/JUnit4SuiteTest.java     |  22 +-
 .../surefire/booter/SurefireReflectorTest.java     | 154 -------------
 .../maven/surefire/booter/SystemUtilsTest.java     |   5 +-
 surefire-extensions-api/pom.xml                    | 105 +++++++++
 .../ConsoleOutputReportEventListener.java          |  21 +-
 .../surefire/extensions/ConsoleOutputReporter.java |  72 ++++++
 .../extensions/StatelessReportEventListener.java   |  43 ++++
 .../StatelessReportMojoConfiguration.java          |  75 +++++++
 .../surefire/extensions/StatelessReporter.java     |  87 ++++++++
 ...elessTestsetInfoConsoleReportEventListener.java |  53 +++++
 ...tatelessTestsetInfoFileReportEventListener.java |  67 ++++++
 .../extensions/StatelessTestsetInfoReporter.java   |  58 +++++
 surefire-grouper/pom.xml                           |  26 +++
 surefire-its/pom.xml                               |   7 -
 .../maven/surefire/its/JUnitPlatformEnginesIT.java |   5 +-
 .../apache/maven/surefire/its/JUnitPlatformIT.java |  41 +++-
 .../junit-platform-engine-jupiter/pom.xml          |  31 +++
 ...{DisplayNameTest.javax => DisplayNameTest.java} |  16 +-
 surefire-logger-api/pom.xml                        |  52 +++++
 .../surefire/log/api/ConsoleLoggerDecorator.java   |   2 +-
 .../surefire/log/api/ConsoleLoggerUtilsTest.java   |  64 ++++++
 .../plugin/surefire/log/api}/JUnit4SuiteTest.java  |  14 +-
 .../maven/plugin/surefire/log/api/LevelTest.java   |  73 ++++++
 .../maven/plugin/surefire/log/api/LoggersTest.java | 139 ++++++++++++
 surefire-providers/common-java5/pom.xml            |  34 ++-
 surefire-providers/common-junit3/pom.xml           |  41 ++++
 surefire-providers/common-junit4/pom.xml           |  41 ++++
 .../surefire/common/junit4/JUnit4RunListener.java  |  10 +-
 surefire-providers/common-junit48/pom.xml          |  31 +++
 .../surefire/common/junit48/JUnit4SuiteTest.java   |  18 +-
 surefire-providers/surefire-junit-platform/pom.xml |  29 +++
 .../surefire/junitplatform/RunListenerAdapter.java |  80 +++----
 .../surefire/junitplatform/JUnit47SuiteTest.java   |  21 +-
 .../junitplatform/RunListenerAdapterTest.java      | 182 ++++++++++-----
 surefire-providers/surefire-junit3/pom.xml         |  41 ++++
 .../maven/surefire/junit/JUnit3Provider.java       |   4 +-
 .../apache/maven/surefire/junit/PojoTestSet.java   |  12 +-
 .../junit/TestListenerInvocationHandler.java       |   4 +-
 surefire-providers/surefire-junit4/pom.xml         |  44 ++++
 .../maven/surefire/junit4/JUnit4Provider.java      |   4 +-
 .../maven/surefire/junit4/JUnit4ProviderTest.java  |   6 +-
 .../maven/surefire/junit4}/JUnit4SuiteTest.java    |  18 +-
 surefire-providers/surefire-junit47/pom.xml        |  31 +++
 .../junitcore/NonConcurrentRunListener.java        |   4 +-
 .../apache/maven/surefire/junitcore/TestSet.java   |   2 +-
 .../maven/surefire/junitcore/JUnit47SuiteTest.java |  40 ++--
 .../maven/surefire/junitcore/JUnitCoreTester.java  |   8 +-
 .../maven/surefire/junitcore/TestMethodTest.java   |   2 +-
 surefire-providers/surefire-testng-utils/pom.xml   |  26 +++
 surefire-providers/surefire-testng/pom.xml         |  26 +++
 .../maven/surefire/testng/TestNGReporter.java      |   9 +-
 .../apache/maven/surefire/testng/TestSuite.java    |   4 +-
 surefire-report-parser/pom.xml                     |  24 ++
 .../plugins/surefire/report/JUnit4SuiteTest.java   |  20 +-
 134 files changed, 4962 insertions(+), 1031 deletions(-)

diff --git a/Jenkinsfile b/Jenkinsfile
index 3f96cad..b2d8023 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -68,9 +68,6 @@ oses.eachWithIndex { osMapping, indexOfOs ->
                         boolean first = indexOfOs == 0 && indexOfMaven == 0 && indexOfJdk == 0
                         def failsafeItPort = 8000 + 100 * indexOfMaven + 10 * indexOfJdk
                         def allOptions = options + ["-Dfailsafe-integration-test-port=${failsafeItPort}", "-Dfailsafe-integration-test-stop-port=${1 + failsafeItPort}"]
-                        if (jdk > 7) {
-                            allOptions += ['-DpowermockVersion=2.0.0', '-Denforcer.skip=true']
-                        }
                         ws(dir: "${os == 'windows' ? "${TEMP}\\${BUILD_TAG}" : pwd()}") {
                             buildProcess(stageKey, jdkName, jdkTestName, mvnName, first ? goalsDepl : goals, allOptions, mavenOpts, first)
                         }
diff --git a/maven-failsafe-plugin/pom.xml b/maven-failsafe-plugin/pom.xml
index 31dcfbf..9928f11 100644
--- a/maven-failsafe-plugin/pom.xml
+++ b/maven-failsafe-plugin/pom.xml
@@ -86,6 +86,12 @@
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.jacoco</groupId>
+            <artifactId>org.jacoco.agent</artifactId>
+            <classifier>runtime</classifier>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -113,6 +119,29 @@
                 </executions>
             </plugin>
             <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>jacoco-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <!--todo remove-->
+                        <id>jacoco-report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <propertyName>jacoco.agent</propertyName>
+                </configuration>
+            </plugin>
+            <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
                 <dependencies>
                     <dependency>
@@ -122,6 +151,7 @@
                     </dependency>
                 </dependencies>
                 <configuration>
+                    <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
                     <includes>
                         <include>**/JUnit4SuiteTest.java</include>
                     </includes>
diff --git a/maven-failsafe-plugin/src/test/java/org/apache/maven/plugin/failsafe/IntegrationTestMojoTest.java b/maven-failsafe-plugin/src/test/java/org/apache/maven/plugin/failsafe/IntegrationTestMojoTest.java
index 8414d6b..5e008aa 100644
--- a/maven-failsafe-plugin/src/test/java/org/apache/maven/plugin/failsafe/IntegrationTestMojoTest.java
+++ b/maven-failsafe-plugin/src/test/java/org/apache/maven/plugin/failsafe/IntegrationTestMojoTest.java
@@ -1 +1 @@
-package org.apache.maven.plugin.failsafe;

/*
 * 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.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.artifact.versioning.Invali
 dVersionSpecificationException;
import org.apache.maven.project.MavenProject;
import org.junit.Before;
import org.junit.Test;

import java.io.File;
import java.io.IOException;

import static org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.fest.assertions.Assertions.assertThat;

/**
 * @since 2.20
 */
public class IntegrationTestMojoTest
{
    private IntegrationTestMojo mojo;

    @Before
    public void init() throws InvalidVersionSpecificationException, IOException
    {
        Artifact artifact = new DefaultArtifact( "g", "a", createFromVersionSpec( "1.0" ), "compile", "jar", "", null );
        artifact.setFile( new File( "./target/tmp/a-1.0.jar" ) );
        new File( "./target/tmp" ).mkdir();
        artifact.getFile().createNewFile();
        mojo = spy( IntegrationTestMojo.class );
        MavenProject project = mo
 ck( MavenProject.class );
        when( project.getArtifact() ).thenReturn( artifact );
        when( mojo.getProject() ).thenReturn( project );
    }

    @Test
    public void shouldBeJar()
    {
        mojo.setDefaultClassesDirectory( new File( "./target/classes" ) );
        File binaries = mojo.getClassesDirectory();
        assertThat( binaries.getName() ).isEqualTo( "a-1.0.jar" );
    }

    @Test
    public void shouldBeAnotherJar()
    {
        mojo.setClassesDirectory( new File( "./target/another-1.0.jar" ) );
        mojo.setDefaultClassesDirectory( new File( "./target/classes" ) );
        File binaries = mojo.getClassesDirectory();
        assertThat( binaries.getName() ).isEqualTo( "another-1.0.jar" );
    }

    @Test
    public void shouldBeClasses()
    {
        mojo.setClassesDirectory( new File( "./target/classes" ) );
        mojo.setDefaultClassesDirectory( new File( "./target/classes" ) );
        File binaries = mojo.getClassesDirectory();
        assertTha
 t( binaries.getName() ).isEqualTo( "classes" );
    }
}
\ No newline at end of file
+package org.apache.maven.plugin.failsafe;

/*
 * 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.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.artifact.versioning.Invali
 dVersionSpecificationException;
import org.apache.maven.project.MavenProject;
import org.junit.Before;
import org.junit.Test;

import java.io.File;
import java.io.IOException;

import static org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.fest.assertions.Assertions.assertThat;

/**
 * @since 2.20
 */
public class IntegrationTestMojoTest
{
    private IntegrationTestMojo mojo;

    @Before
    public void init() throws InvalidVersionSpecificationException, IOException
    {
        Artifact artifact = new DefaultArtifact( "g", "a", createFromVersionSpec( "1.0" ), "compile", "jar", "", null );
        artifact.setFile( new File( "./target/tmp/a-1.0.jar" ) );
        new File( "./target/tmp" ).mkdir();
        artifact.getFile().createNewFile();
        mojo = new IntegrationTestMojo();
        MavenProject project = mock( MavenProject.class );
        when( projec
 t.getArtifact() ).thenReturn( artifact );
        mojo.setProject( project );
    }

    @Test
    public void shouldBeJar()
    {
        mojo.setDefaultClassesDirectory( new File( "./target/classes" ) );
        File binaries = mojo.getClassesDirectory();
        assertThat( binaries.getName() ).isEqualTo( "a-1.0.jar" );
    }

    @Test
    public void shouldBeAnotherJar()
    {
        mojo.setClassesDirectory( new File( "./target/another-1.0.jar" ) );
        mojo.setDefaultClassesDirectory( new File( "./target/classes" ) );
        File binaries = mojo.getClassesDirectory();
        assertThat( binaries.getName() ).isEqualTo( "another-1.0.jar" );
    }

    @Test
    public void shouldBeClasses()
    {
        mojo.setClassesDirectory( new File( "./target/classes" ) );
        mojo.setDefaultClassesDirectory( new File( "./target/classes" ) );
        File binaries = mojo.getClassesDirectory();
        assertThat( binaries.getName() ).isEqualTo( "classes" );
    }
}
\ No newline at end of file
diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml
index 770642e..ff7eb97 100644
--- a/maven-surefire-common/pom.xml
+++ b/maven-surefire-common/pom.xml
@@ -44,6 +44,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.maven.surefire</groupId>
+            <artifactId>surefire-extensions-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.surefire</groupId>
             <artifactId>surefire-booter</artifactId>
             <version>${project.version}</version>
         </dependency>
@@ -115,17 +120,60 @@
             <artifactId>powermock-api-mockito2</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.jacoco</groupId>
+            <artifactId>org.jacoco.agent</artifactId>
+            <classifier>runtime</classifier>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>build-test-classpath</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>build-classpath</goal>
+                        </goals>
+                        <configuration>
+                            <includeScope>test</includeScope>
+                            <outputFile>target/test-classpath/cp.txt</outputFile>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>jacoco-instrumentation</id>
+                        <goals>
+                            <goal>instrument</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>restore-classes</id>
+                        <goals>
+                            <goal>restore-instrumented-classes</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <!--todo remove-->
+                        <id>jacoco-report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <includes>
-                        <include>**/JUnit4SuiteTest.java</include>
-                    </includes>
-                </configuration>
                 <dependencies>
                     <dependency>
                         <groupId>org.apache.maven.surefire</groupId>
@@ -133,6 +181,15 @@
                         <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
                     </dependency>
                 </dependencies>
+                <configuration>
+                    <argLine>${jvm.args.tests}</argLine>
+                    <includes>
+                        <include>**/JUnit4SuiteTest.java</include>
+                    </includes>
+                    <systemPropertyVariables>
+                        <jacoco-agent.destfile>${project.build.directory}/jacoco.exec</jacoco-agent.destfile>
+                    </systemPropertyVariables>
+                </configuration>
             </plugin>
             <plugin>
                 <artifactId>maven-shade-plugin</artifactId>
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index 856a527..2f179e8 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
@@ -25,6 +25,9 @@ import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.artifact.handler.ArtifactHandler;
 import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.model.Dependency;
+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.plugins.annotations.Component;
 import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.repository.RepositorySystem;
@@ -157,6 +160,18 @@ public abstract class AbstractSurefireMojo
     private final ProviderDetector providerDetector = new ProviderDetector();
 
     /**
+     * Note: use the legacy system property <em>disableXmlReport</em> set to {@code true} to disable the report.
+     */
+    @Parameter
+    private SurefireStatelessReporter statelessTestsetReporter;
+
+    @Parameter
+    private SurefireConsoleOutputReporter consoleOutputReporter;
+
+    @Parameter
+    private SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter;
+
+    /**
      * Information about this plugin, mainly used to lookup this plugin's configuration from the currently executing
      * project.
      *
@@ -670,9 +685,11 @@ public abstract class AbstractSurefireMojo
 
     /**
      * Flag to disable the generation of report files in xml format.
-     *
+     * Deprecated since 3.0.0-M4.
+     * Instead use <em>disable</em> within {@code statelessTestsetReporter} since of 3.0.0-M6.
      * @since 2.2
      */
+    @Deprecated // todo make readonly to handle system property
     @Parameter( property = "disableXmlReport", defaultValue = "false" )
     private boolean disableXmlReport;
 
@@ -1781,7 +1798,8 @@ public abstract class AbstractSurefireMojo
         getConsoleLogger().debug( testClasspath.getCompactLogMessage( "test(compact) classpath:" ) );
         getConsoleLogger().debug( providerClasspath.getCompactLogMessage( "provider(compact) classpath:" ) );
 
-        Artifact[] additionalInProcArtifacts = { getCommonArtifact(), getApiArtifact(), getLoggerApiArtifact() };
+        Artifact[] additionalInProcArtifacts =
+                { getCommonArtifact(), getExtensionsArtifact(), getApiArtifact(), getLoggerApiArtifact() };
         Set<Artifact> inProcArtifacts = retainInProcArtifactsUnique( providerArtifacts, additionalInProcArtifacts );
         Classpath inProcClasspath = createInProcClasspath( providerClasspath, inProcArtifacts );
         getConsoleLogger().debug( inProcClasspath.getLogMessage( "in-process classpath:" ) );
@@ -1881,7 +1899,8 @@ public abstract class AbstractSurefireMojo
         ModularClasspath modularClasspath = new ModularClasspath( moduleDescriptor, testModulepath.getClassPath(),
                 packages, getTestClassesDirectory() );
 
-        Artifact[] additionalInProcArtifacts = { getCommonArtifact(), getApiArtifact(), getLoggerApiArtifact() };
+        Artifact[] additionalInProcArtifacts =
+                { getCommonArtifact(), getExtensionsArtifact(), getApiArtifact(), getLoggerApiArtifact() };
         Set<Artifact> inProcArtifacts = retainInProcArtifactsUnique( providerArtifacts, additionalInProcArtifacts );
         Classpath inProcClasspath = createInProcClasspath( providerClasspath, inProcArtifacts );
 
@@ -1906,6 +1925,11 @@ public abstract class AbstractSurefireMojo
         return getPluginArtifactMap().get( "org.apache.maven.surefire:maven-surefire-common" );
     }
 
+    private Artifact getExtensionsArtifact()
+    {
+        return getPluginArtifactMap().get( "org.apache.maven.surefire:surefire-extensions-api" );
+    }
+
     private Artifact getApiArtifact()
     {
         return getPluginArtifactMap().get( "org.apache.maven.surefire:surefire-api" );
@@ -1928,12 +1952,26 @@ public abstract class AbstractSurefireMojo
 
     private StartupReportConfiguration getStartupReportConfiguration( String configChecksum, boolean isForkMode )
     {
+        SurefireStatelessReporter xmlReporter =
+                statelessTestsetReporter == null
+                        ? new SurefireStatelessReporter( /*todo call def. constr.*/ isDisableXmlReport(), "3.0" )
+                        : statelessTestsetReporter;
+
+        xmlReporter.setDisable( isDisableXmlReport() ); // todo change to Boolean in the version 3.0.0-M6
+
+        SurefireConsoleOutputReporter outReporter =
+                consoleOutputReporter == null ? new SurefireConsoleOutputReporter() : consoleOutputReporter;
+
+        SurefireStatelessTestsetInfoReporter testsetReporter =
+                statelessTestsetInfoReporter == null
+                        ? new SurefireStatelessTestsetInfoReporter() : statelessTestsetInfoReporter;
+
         return new StartupReportConfiguration( isUseFile(), isPrintSummary(), getReportFormat(),
-                                               isRedirectTestOutputToFile(), isDisableXmlReport(),
+                                               isRedirectTestOutputToFile(),
                                                getReportsDirectory(), isTrimStackTrace(), getReportNameSuffix(),
                                                getStatisticsFile( configChecksum ), requiresRunHistory(),
                                                getRerunFailingTestsCount(), getReportSchemaLocation(), getEncoding(),
-                                               isForkMode );
+                                               isForkMode, xmlReporter, outReporter, testsetReporter );
     }
 
     private boolean isSpecificTestSpecified()
@@ -3159,7 +3197,7 @@ public abstract class AbstractSurefireMojo
         public Set<Artifact> getProviderClasspath()
         {
             return surefireDependencyResolver.addProviderToClasspath( getPluginArtifactMap(), getMojoArtifact(),
-                    getCommonArtifact(), getApiArtifact(), getLoggerApiArtifact() );
+                    getApiArtifact(), getLoggerApiArtifact() );
         }
     }
 
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java
index 81830ca..835fb89 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java
@@ -19,15 +19,17 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import java.io.File;
-import java.lang.reflect.Constructor;
-
+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.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.surefire.booter.SurefireReflector;
 import org.apache.maven.surefire.util.SurefireReflectionException;
 
 import javax.annotation.Nonnull;
+import java.io.File;
+import java.lang.reflect.Constructor;
 
 import static org.apache.maven.surefire.util.ReflectionUtils.getConstructor;
 import static org.apache.maven.surefire.util.ReflectionUtils.instantiateObject;
@@ -40,6 +42,9 @@ public class CommonReflector
 {
     private final Class<?> startupReportConfiguration;
     private final Class<?> consoleLogger;
+    private final Class<?> statelessTestsetReporter;
+    private final Class<?> consoleOutputReporter;
+    private final Class<?> statelessTestsetInfoReporter;
     private final ClassLoader surefireClassLoader;
 
     public CommonReflector( @Nonnull ClassLoader surefireClassLoader )
@@ -50,6 +55,10 @@ public class CommonReflector
         {
             startupReportConfiguration = surefireClassLoader.loadClass( StartupReportConfiguration.class.getName() );
             consoleLogger = surefireClassLoader.loadClass( ConsoleLogger.class.getName() );
+            statelessTestsetReporter = surefireClassLoader.loadClass( SurefireStatelessReporter.class.getName() );
+            consoleOutputReporter = surefireClassLoader.loadClass( SurefireConsoleOutputReporter.class.getName() );
+            statelessTestsetInfoReporter =
+                    surefireClassLoader.loadClass( SurefireStatelessTestsetInfoReporter.class.getName() );
         }
         catch ( ClassNotFoundException e )
         {
@@ -70,18 +79,23 @@ public class CommonReflector
     private Object createStartupReportConfiguration( @Nonnull StartupReportConfiguration reporterConfiguration )
     {
         Constructor<?> constructor = getConstructor( startupReportConfiguration, boolean.class, boolean.class,
-                                                           String.class, boolean.class, boolean.class, File.class,
-                                                           boolean.class, String.class, File.class, boolean.class,
-                                                           int.class, String.class, String.class, boolean.class );
+                                                     String.class, boolean.class, File.class,
+                                                     boolean.class, String.class, File.class, boolean.class,
+                                                     int.class, String.class, String.class, boolean.class,
+                                                     statelessTestsetReporter, consoleOutputReporter,
+                                                     statelessTestsetInfoReporter );
         //noinspection BooleanConstructorCall
         Object[] params = { reporterConfiguration.isUseFile(), reporterConfiguration.isPrintSummary(),
             reporterConfiguration.getReportFormat(), reporterConfiguration.isRedirectTestOutputToFile(),
-            reporterConfiguration.isDisableXmlReport(), reporterConfiguration.getReportsDirectory(),
+            reporterConfiguration.getReportsDirectory(),
             reporterConfiguration.isTrimStackTrace(), reporterConfiguration.getReportNameSuffix(),
             reporterConfiguration.getStatisticsFile(), reporterConfiguration.isRequiresRunHistory(),
             reporterConfiguration.getRerunFailingTestsCount(), reporterConfiguration.getXsdSchemaLocation(),
-            reporterConfiguration.getEncoding().name(), reporterConfiguration.isForkMode() };
+            reporterConfiguration.getEncoding().name(), reporterConfiguration.isForkMode(),
+            reporterConfiguration.getXmlReporter().clone( surefireClassLoader ),
+            reporterConfiguration.getConsoleOutputReporter().clone( surefireClassLoader ),
+            reporterConfiguration.getTestsetReporter().clone( surefireClassLoader )
+        };
         return newInstance( constructor, params );
     }
-
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
index 932eecd..2b82afe 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
@@ -19,13 +19,21 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import org.apache.maven.plugin.surefire.report.ConsoleOutputFileReporter;
-import org.apache.maven.plugin.surefire.report.DirectConsoleOutput;
-import org.apache.maven.plugin.surefire.report.FileReporter;
-import org.apache.maven.plugin.surefire.report.StatelessXmlReporter;
-import org.apache.maven.plugin.surefire.report.TestcycleConsoleOutputReceiver;
+import org.apache.maven.plugin.surefire.extensions.DefaultStatelessReportMojoConfiguration;
+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.plugin.surefire.report.TestSetStats;
 import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
+import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
+import org.apache.maven.surefire.extensions.ConsoleOutputReporter;
+import org.apache.maven.surefire.extensions.StatelessReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessReporter;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoReporter;
 
 import javax.annotation.Nonnull;
 import java.io.File;
@@ -35,10 +43,11 @@ import java.util.Deque;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.commons.lang3.StringUtils.trimToNull;
 import static org.apache.maven.plugin.surefire.SurefireHelper.replaceForkThreadsInPath;
 import static org.apache.maven.plugin.surefire.report.ConsoleReporter.BRIEF;
 import static org.apache.maven.plugin.surefire.report.ConsoleReporter.PLAIN;
-import static org.apache.commons.lang3.StringUtils.trimToNull;
 
 /**
  * All the parameters used to construct reporters
@@ -66,8 +75,6 @@ public final class StartupReportConfiguration
 
     private final boolean redirectTestOutputToFile;
 
-    private final boolean disableXmlReport;
-
     private final File reportsDirectory;
 
     private final boolean trimStackTrace;
@@ -76,27 +83,33 @@ public final class StartupReportConfiguration
 
     private final String xsdSchemaLocation;
 
-    private final Map<String, Deque<WrappedReportEntry>> testClassMethodRunHistory
-        = new ConcurrentHashMap<>();
+    private final Map<String, Deque<WrappedReportEntry>> testClassMethodRunHistory = new ConcurrentHashMap<>();
 
     private final Charset encoding;
 
-    private boolean isForkMode;
+    private final boolean isForkMode;
+
+    private final SurefireStatelessReporter xmlReporter;
+
+    private final ConsoleOutputReporter consoleOutputReporter;
+
+    private final SurefireStatelessTestsetInfoReporter testsetReporter;
 
     private StatisticsReporter statisticsReporter;
 
     @SuppressWarnings( "checkstyle:parameternumber" )
     public StartupReportConfiguration( boolean useFile, boolean printSummary, String reportFormat,
-                                       boolean redirectTestOutputToFile, boolean disableXmlReport,
-                                       @Nonnull File reportsDirectory, boolean trimStackTrace, String reportNameSuffix,
-                                       File statisticsFile, boolean requiresRunHistory, int rerunFailingTestsCount,
-                                       String xsdSchemaLocation, String encoding, boolean isForkMode )
+               boolean redirectTestOutputToFile,
+               @Nonnull File reportsDirectory, boolean trimStackTrace, String reportNameSuffix,
+               File statisticsFile, boolean requiresRunHistory, int rerunFailingTestsCount,
+               String xsdSchemaLocation, String encoding, boolean isForkMode,
+               SurefireStatelessReporter xmlReporter, SurefireConsoleOutputReporter consoleOutputReporter,
+               SurefireStatelessTestsetInfoReporter testsetReporter )
     {
         this.useFile = useFile;
         this.printSummary = printSummary;
         this.reportFormat = reportFormat;
         this.redirectTestOutputToFile = redirectTestOutputToFile;
-        this.disableXmlReport = disableXmlReport;
         this.reportsDirectory = reportsDirectory;
         this.trimStackTrace = trimStackTrace;
         this.reportNameSuffix = reportNameSuffix;
@@ -107,8 +120,11 @@ public final class StartupReportConfiguration
         this.rerunFailingTestsCount = rerunFailingTestsCount;
         this.xsdSchemaLocation = xsdSchemaLocation;
         String charset = trimToNull( encoding );
-        this.encoding = charset == null ? Charset.defaultCharset() : Charset.forName( charset );
+        this.encoding = charset == null ? UTF_8 : Charset.forName( charset );
         this.isForkMode = isForkMode;
+        this.xmlReporter = xmlReporter;
+        this.consoleOutputReporter = consoleOutputReporter;
+        this.testsetReporter = testsetReporter;
     }
 
     public boolean isUseFile()
@@ -136,11 +152,6 @@ public final class StartupReportConfiguration
         return redirectTestOutputToFile;
     }
 
-    public boolean isDisableXmlReport()
-    {
-        return disableXmlReport;
-    }
-
     public File getReportsDirectory()
     {
         return reportsDirectory;
@@ -151,10 +162,10 @@ public final class StartupReportConfiguration
         return rerunFailingTestsCount;
     }
 
-    @Deprecated // rename to stateful
-    public StatelessXmlReporter instantiateStatelessXmlReporter( Integer forkNumber )
+    public StatelessReportEventListener<WrappedReportEntry, TestSetStats> instantiateStatelessXmlReporter(
+            Integer forkNumber )
     {
-        assert forkNumber == null || isForkMode;
+        assert ( forkNumber == null ) == !isForkMode;
 
         // If forking TestNG the suites have same name 'TestSuite' and tend to override report statistics in stateful
         // reporter, see Surefire1535TestNGParallelSuitesIT. The testClassMethodRunHistory should be isolated.
@@ -165,30 +176,40 @@ public final class StartupReportConfiguration
                 ? new ConcurrentHashMap<String, Deque<WrappedReportEntry>>()
                 : this.testClassMethodRunHistory;
 
-        return isDisableXmlReport()
-            ? null
-            : new StatelessXmlReporter( resolveReportsDirectory( forkNumber ), reportNameSuffix, trimStackTrace,
-                rerunFailingTestsCount, testClassMethodRunHistory, xsdSchemaLocation );
+        DefaultStatelessReportMojoConfiguration xmlReporterConfig =
+                new DefaultStatelessReportMojoConfiguration( resolveReportsDirectory( forkNumber ), reportNameSuffix,
+                        trimStackTrace, rerunFailingTestsCount, xsdSchemaLocation, testClassMethodRunHistory );
+
+        return xmlReporter.isDisable() ? null : xmlReporter.createListener( xmlReporterConfig );
     }
 
-    public FileReporter instantiateFileReporter( Integer forkNumber )
+    public StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> instantiateFileReporter(
+            Integer forkNumber )
     {
-        return isUseFile() && isBriefOrPlainFormat()
-            ? new FileReporter( resolveReportsDirectory( forkNumber ), reportNameSuffix, encoding )
+        return !testsetReporter.isDisable() && isUseFile() && isBriefOrPlainFormat()
+            ? testsetReporter.createListener( resolveReportsDirectory( forkNumber ), reportNameSuffix, encoding )
             : null;
     }
 
+    public StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> instantiateConsoleReporter(
+            ConsoleLogger consoleLogger )
+    {
+        return !testsetReporter.isDisable() && shouldReportToConsole()
+                ? testsetReporter.createListener( consoleLogger ) : null;
+    }
+
     public boolean isBriefOrPlainFormat()
     {
         String fmt = getReportFormat();
         return BRIEF.equals( fmt ) || PLAIN.equals( fmt );
     }
 
-    public TestcycleConsoleOutputReceiver instantiateConsoleOutputFileReporter( Integer forkNumber )
+    public ConsoleOutputReportEventListener instantiateConsoleOutputFileReporter( Integer forkNum )
     {
-        return isRedirectTestOutputToFile()
-            ? new ConsoleOutputFileReporter( resolveReportsDirectory( forkNumber ), reportNameSuffix, forkNumber )
-            : new DirectConsoleOutput( originalSystemOut, originalSystemErr );
+        ConsoleOutputReportEventListener outputReport = isRedirectTestOutputToFile()
+                ? consoleOutputReporter.createListener( resolveReportsDirectory( forkNum ), reportNameSuffix, forkNum )
+                : consoleOutputReporter.createListener( originalSystemOut, originalSystemErr );
+        return consoleOutputReporter.isDisable() ? null : outputReport;
     }
 
     public synchronized StatisticsReporter getStatisticsReporter()
@@ -215,11 +236,6 @@ public final class StartupReportConfiguration
         return requiresRunHistory;
     }
 
-    public PrintStream getOriginalSystemOut()
-    {
-        return originalSystemOut;
-    }
-
     public String getXsdSchemaLocation()
     {
         return xsdSchemaLocation;
@@ -239,4 +255,24 @@ public final class StartupReportConfiguration
     {
         return forkNumber == null ? reportsDirectory : replaceForkThreadsInPath( reportsDirectory, forkNumber );
     }
+
+    public StatelessReporter<WrappedReportEntry, TestSetStats, DefaultStatelessReportMojoConfiguration> getXmlReporter()
+    {
+        return xmlReporter;
+    }
+
+    public ConsoleOutputReporter getConsoleOutputReporter()
+    {
+        return consoleOutputReporter;
+    }
+
+    public StatelessTestsetInfoReporter<WrappedReportEntry, TestSetStats> getTestsetReporter()
+    {
+        return testsetReporter;
+    }
+
+    private boolean shouldReportToConsole()
+    {
+        return isUseFile() ? isPrintSummary() : isRedirectTestOutputToFile() || isBriefOrPlainFormat();
+    }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
index 170107c..16cb6c8 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
@@ -205,7 +205,7 @@ final class SurefireDependencyResolver
     }
 
     Set<Artifact> addProviderToClasspath( Map<String, Artifact> pluginArtifactMap, Artifact mojoPluginArtifact,
-                                          Artifact surefireCommon, Artifact surefireApi, Artifact surefireLoggerApi )
+                                          Artifact surefireApi, Artifact surefireLoggerApi )
     {
         Set<Artifact> providerArtifacts = new LinkedHashSet<>();
         ArtifactResolutionResult artifactResolutionResult = resolvePluginArtifact( mojoPluginArtifact );
@@ -218,12 +218,7 @@ final class SurefireDependencyResolver
                 {
                     String groupId = dependency.getGroupId();
                     String artifactId = dependency.getArtifactId();
-                    if ( groupId.equals( surefireCommon.getGroupId() )
-                            && artifactId.equals( surefireCommon.getArtifactId() ) )
-                    {
-                        providerArtifacts.add( surefireCommon );
-                    }
-                    else if ( groupId.equals( surefireApi.getGroupId() )
+                    if ( groupId.equals( surefireApi.getGroupId() )
                             && artifactId.equals( surefireApi.getArtifactId() ) )
                     {
                         providerArtifacts.add( surefireApi );
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 7e7ab78..f2a934f 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
@@ -159,7 +159,8 @@ public class ForkClient
         public void handle( RunMode runMode, TestSetReportEntry reportEntry )
         {
             testsInProgress.clear();
-            TestSetReportEntry entry = reportEntry( reportEntry.getSourceName(), reportEntry.getName(),
+            TestSetReportEntry entry = reportEntry( reportEntry.getSourceName(), reportEntry.getSourceText(),
+                    reportEntry.getName(), reportEntry.getNameText(),
                     reportEntry.getGroup(), reportEntry.getStackTraceWriter(), reportEntry.getElapsed(),
                     reportEntry.getMessage(), getTestVmSystemProperties() );
             getTestSetReporter().testSetCompleted( entry );
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoder.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoder.java
index 60ed138..b7950d0 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoder.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoder.java
@@ -261,16 +261,18 @@ public final class ForkedChannelDecoder
                 if ( listener != null && encoding != null && mode != null )
                 {
                     String sourceName = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+                    String sourceText = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
                     String name = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+                    String nameText = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
                     String group = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
                     String message = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
                     String elapsed = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
                     String traceMessage = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
                     String smartTrimmedStackTrace = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
                     String stackTrace = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
-
-                    listener.handle( mode, toReportEntry( encoding, sourceName, name, group, message, elapsed,
-                            traceMessage, smartTrimmedStackTrace, stackTrace ) );
+                    ReportEntry reportEntry = toReportEntry( encoding, sourceName, sourceText, name, nameText,
+                            group, message, elapsed, traceMessage, smartTrimmedStackTrace, stackTrace );
+                    listener.handle( mode, reportEntry );
                 }
             }
             else if ( event.isJvmExitError() )
@@ -294,7 +296,8 @@ public final class ForkedChannelDecoder
 
     static ReportEntry toReportEntry( Charset encoding,
                    // ReportEntry:
-                   String encSource, String encName, String encGroup, String encMessage, String encTimeElapsed,
+                   String encSource, String encSourceText, String encName, String encNameText,
+                                      String encGroup, String encMessage, String encTimeElapsed,
                    // StackTraceWriter:
                    String encTraceMessage, String encSmartTrimmedStackTrace, String encStackTrace )
             throws NumberFormatException
@@ -306,14 +309,16 @@ public final class ForkedChannelDecoder
         }
 
         String source = decode( encSource, encoding );
+        String sourceText = decode( encSourceText, encoding );
         String name = decode( encName, encoding );
+        String nameText = decode( encNameText, encoding );
         String group = decode( encGroup, encoding );
         StackTraceWriter stackTraceWriter =
                 decodeTrace( encoding, encTraceMessage, encSmartTrimmedStackTrace, encStackTrace );
         Integer elapsed = decodeToInteger( encTimeElapsed );
         String message = decode( encMessage, encoding );
-        return reportEntry( source, name, group, stackTraceWriter, elapsed, message,
-                Collections.<String, String>emptyMap() );
+        return reportEntry( source, sourceText, name, nameText,
+                group, stackTraceWriter, elapsed, message, Collections.<String, String>emptyMap() );
     }
 
     static String decode( String line, Charset encoding )
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
index dad88fd..388f16d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
@@ -40,7 +40,7 @@ public final class ThreadedStreamConsumer
 {
     private static final String END_ITEM = "";
 
-    private static final int ITEM_LIMIT_BEFORE_SLEEP = 10 * 1000;
+    private static final int ITEM_LIMIT_BEFORE_SLEEP = 10_000;
 
     private final BlockingQueue<String> items = new ArrayBlockingQueue<>( ITEM_LIMIT_BEFORE_SLEEP );
 
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/DefaultStatelessReportMojoConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/DefaultStatelessReportMojoConfiguration.java
new file mode 100644
index 0000000..0eb6b52
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/DefaultStatelessReportMojoConfiguration.java
@@ -0,0 +1,58 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.surefire.extensions.StatelessReportMojoConfiguration;
+
+import java.io.File;
+import java.util.Deque;
+import java.util.Map;
+
+/**
+ * Why Deprecated: The field {@code testClassMethodRunHistory} makes
+ * {@link org.apache.maven.plugin.surefire.report.StatelessXmlReporter} stateful and overloads reporter by permanently
+ * overriding XML using re-run feature. To fix this issue, the providers should use more events for re-run feature and
+ * events bounding provider's execution. After provider's execution is finished, this reporter should be announced
+ * only once per test class. All test report entries should be cached in
+ * {@link org.apache.maven.plugin.surefire.report.TestSetRunListener} keeping it already stateful.
+ */
+@Deprecated
+public class DefaultStatelessReportMojoConfiguration
+        extends StatelessReportMojoConfiguration
+{
+    private final Map<String, Deque<WrappedReportEntry>> testClassMethodRunHistory;
+
+    public DefaultStatelessReportMojoConfiguration( File reportsDirectory,
+                                                    String reportNameSuffix,
+                                                    boolean trimStackTrace,
+                                                    int rerunFailingTestsCount,
+                                                    String xsdSchemaLocation,
+                                                    Map<String, Deque<WrappedReportEntry>> testClassMethodRunHistory )
+    {
+        super( reportsDirectory, reportNameSuffix, trimStackTrace, rerunFailingTestsCount, xsdSchemaLocation );
+        this.testClassMethodRunHistory = testClassMethodRunHistory;
+    }
+
+    public Map<String, Deque<WrappedReportEntry>> getTestClassMethodRunHistory()
+    {
+        return testClassMethodRunHistory;
+    }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireConsoleOutputReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireConsoleOutputReporter.java
new file mode 100644
index 0000000..e4ac7a6
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireConsoleOutputReporter.java
@@ -0,0 +1,86 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.report.ConsoleOutputFileReporter;
+import org.apache.maven.plugin.surefire.report.DirectConsoleOutput;
+import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
+import org.apache.maven.surefire.extensions.ConsoleOutputReporter;
+import org.apache.maven.surefire.util.ReflectionUtils;
+
+import java.io.File;
+import java.io.PrintStream;
+
+/**
+ * Default implementation for extension of console logger.
+ * Signatures can be changed between major, minor versions or milestones.
+ * <br>
+ * Builds {@link ConsoleOutputReportEventListener listeners}.
+ * The listeners handle test set events.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ */
+public class SurefireConsoleOutputReporter
+        extends ConsoleOutputReporter
+{
+    @Override
+    public ConsoleOutputReportEventListener createListener( File reportsDirectory, String reportNameSuffix,
+                                                            Integer forkNumber )
+    {
+        return new ConsoleOutputFileReporter( reportsDirectory, reportNameSuffix, false, forkNumber, getEncoding() );
+    }
+
+    @Override
+    public ConsoleOutputReportEventListener createListener( PrintStream out, PrintStream err )
+    {
+        return new DirectConsoleOutput( out, err );
+    }
+
+    @Override
+    public Object clone( ClassLoader target )
+    {
+        try
+        {
+            Class<?> cls = ReflectionUtils.reloadClass( target, this );
+            Object clone = cls.newInstance();
+
+            cls.getMethod( "setDisable", boolean.class )
+                    .invoke( clone, isDisable() );
+            cls.getMethod( "setEncoding", String.class )
+                    .invoke( clone, getEncoding() );
+
+            return clone;
+        }
+        catch ( ReflectiveOperationException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage() );
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return "SurefireConsoleOutputReporter{"
+                + "disable=" + isDisable()
+                + ", encoding=" + getEncoding()
+                + '}';
+    }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessReporter.java
new file mode 100644
index 0000000..e907bfd
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessReporter.java
@@ -0,0 +1,98 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.report.StatelessXmlReporter;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.surefire.extensions.StatelessReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessReporter;
+import org.apache.maven.surefire.util.ReflectionUtils;
+
+/**
+ * Default implementation for extension of {@link StatelessXmlReporter} in plugin.
+ * Signatures can be changed between major, minor versions or milestones.
+ * <br>
+ * This is a builder of {@link StatelessReportEventListener listener}.
+ * The listener handles <em>testSetCompleted</em> event.
+ *
+ * author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ */
+public class SurefireStatelessReporter
+        extends StatelessReporter<WrappedReportEntry, TestSetStats, DefaultStatelessReportMojoConfiguration>
+{
+    /**
+     * Activated in the injection point of MOJO.
+     */
+    public SurefireStatelessReporter()
+    {
+        this( false, "3.0" );
+    }
+
+    /**
+     * Activated if null injection point in MOJO.
+     * @param disable             {@code true} to disable performing the report
+     * @param version             (xsd 3.0) version of the schema
+     */
+    public SurefireStatelessReporter( boolean disable, String version )
+    {
+        setDisable( disable );
+        setVersion( version );
+    }
+
+    @Override
+    public StatelessReportEventListener<WrappedReportEntry, TestSetStats> createListener(
+            DefaultStatelessReportMojoConfiguration configuration )
+    {
+        return new StatelessXmlReporter( configuration.getReportsDirectory(),
+                configuration.getReportNameSuffix(),
+                configuration.isTrimStackTrace(),
+                configuration.getRerunFailingTestsCount(),
+                configuration.getTestClassMethodRunHistory(),
+                configuration.getXsdSchemaLocation(),
+                getVersion(),
+                false,
+                false,
+                false,
+                false );
+    }
+
+    @Override
+    public Object clone( ClassLoader target )
+    {
+        try
+        {
+            Class<?> cls = ReflectionUtils.reloadClass( target, this );
+            Object clone = cls.newInstance();
+
+            cls.getMethod( "setDisable", boolean.class )
+                    .invoke( clone, isDisable() );
+            cls.getMethod( "setVersion", String.class )
+                    .invoke( clone, getVersion() );
+
+            return clone;
+        }
+        catch ( ReflectiveOperationException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage() );
+        }
+    }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessTestsetInfoReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessTestsetInfoReporter.java
new file mode 100644
index 0000000..73a099c
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessTestsetInfoReporter.java
@@ -0,0 +1,87 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.report.ConsoleReporter;
+import org.apache.maven.plugin.surefire.report.FileReporter;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoReporter;
+import org.apache.maven.surefire.util.ReflectionUtils;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+/**
+ * Default implementation for extension of
+ * {@link StatelessTestsetInfoFileReportEventListener test-set event listener for stateless file and console reporter}
+ * in plugin. Signatures can be changed between major, minor versions or milestones.
+ * <br>
+ * Builds {@link StatelessTestsetInfoFileReportEventListener listeners}.
+ * The listener handles <em>testSetCompleted</em> event.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ */
+public class SurefireStatelessTestsetInfoReporter
+        extends StatelessTestsetInfoReporter<WrappedReportEntry, TestSetStats>
+{
+    @Override
+    public StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> createListener(
+            ConsoleLogger logger )
+    {
+        return new ConsoleReporter( logger, false, false );
+    }
+
+    @Override
+    public StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> createListener(
+            File reportsDirectory, String reportNameSuffix, Charset encoding )
+    {
+        return new FileReporter( reportsDirectory, reportNameSuffix, encoding, false, false, false );
+    }
+
+    @Override
+    public Object clone( ClassLoader target )
+    {
+        try
+        {
+            Class<?> cls = ReflectionUtils.reloadClass( target, this );
+            Object clone = cls.newInstance();
+
+            cls.getMethod( "setDisable", boolean.class )
+                    .invoke( clone, isDisable() );
+
+            return clone;
+        }
+        catch ( ReflectiveOperationException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage() );
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return "SurefireStatelessTestsetInfoReporter{disable=" + isDisable() + "}";
+    }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5ConsoleOutputReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5ConsoleOutputReporter.java
new file mode 100644
index 0000000..f5802b7
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5ConsoleOutputReporter.java
@@ -0,0 +1,92 @@
+package org.apache.maven.plugin.surefire.extensions.junit5;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
+import org.apache.maven.plugin.surefire.report.ConsoleOutputFileReporter;
+import org.apache.maven.plugin.surefire.report.StatelessXmlReporter;
+import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
+
+import java.io.File;
+
+/**
+ * The extension of {@link StatelessXmlReporter logger} for JUnit5.
+ * Selectively enables report files upon JUnit5 annotation <em>DisplayName</em>.
+ *
+ * author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ */
+public class JUnit5ConsoleOutputReporter
+        extends SurefireConsoleOutputReporter
+{
+    /**
+     * {@code false} by default.
+     * <br>
+     * This action takes effect only in JUnit5 provider together with a test class annotated <em>DisplayName</em>.
+     */
+    private boolean usePhrasedFileName;
+
+    public boolean isUsePhrasedFileName()
+    {
+        return usePhrasedFileName;
+    }
+
+    public void setUsePhrasedFileName( boolean usePhrasedFileName )
+    {
+        this.usePhrasedFileName = usePhrasedFileName;
+    }
+
+    @Override
+    public ConsoleOutputReportEventListener createListener( File reportsDirectory, String reportNameSuffix,
+                                                            Integer forkNumber )
+    {
+        return new ConsoleOutputFileReporter( reportsDirectory, reportNameSuffix, isUsePhrasedFileName(), forkNumber,
+                getEncoding() );
+    }
+
+    @Override
+    public Object clone( ClassLoader target )
+    {
+        try
+        {
+            Object clone = super.clone( target );
+
+            Class<?> cls = clone.getClass();
+            cls.getMethod( "setUsePhrasedFileName", boolean.class )
+                    .invoke( clone, isUsePhrasedFileName() );
+
+            return clone;
+        }
+        catch ( ReflectiveOperationException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage() );
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return "JUnit5ConsoleOutputReporter{"
+                + "disable=" + isDisable()
+                + ", encoding=" + getEncoding()
+                + ", usePhrasedFileName=" + isUsePhrasedFileName()
+                + '}';
+    }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5StatelessTestsetInfoReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5StatelessTestsetInfoReporter.java
new file mode 100644
index 0000000..3c1b76b
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5StatelessTestsetInfoReporter.java
@@ -0,0 +1,148 @@
+package org.apache.maven.plugin.surefire.extensions.junit5;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.report.ConsoleReporter;
+import org.apache.maven.plugin.surefire.report.FileReporter;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+/**
+ * Extension of {@link StatelessTestsetInfoConsoleReportEventListener file and console reporter of test-set} for JUnit5.
+ * Signatures can be changed between major, minor versions or milestones.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ */
+public class JUnit5StatelessTestsetInfoReporter
+        extends SurefireStatelessTestsetInfoReporter
+{
+    /**
+     * {@code false} by default.
+     * <br>
+     * This action takes effect only in JUnit5 provider together with a test class annotated <em>DisplayName</em>.
+     */
+    private boolean usePhrasedFileName;
+
+    /**
+     * Phrased class name of test case in the console log (see xxx)
+     * <em>Running xxx</em> or file report log <em>Test set: xxx</em>.
+     * {@code false} by default.
+     * <br>
+     * This action takes effect only in JUnit5 provider together with a test class annotated <em>DisplayName</em>.
+     */
+    private boolean usePhrasedClassNameInRunning;
+
+    /**
+     * Phrased class name of test case in the log (see xxx)
+     * <em>Tests run: ., Failures: ., Errors: ., Skipped: ., Time elapsed: . s, - in xxx</em>.
+     * {@code false} by default.
+     * <br>
+     * This action takes effect only in JUnit5 provider together with a test class annotated <em>DisplayName</em>.
+     */
+    private boolean usePhrasedClassNameInTestCaseSummary;
+
+    public boolean isUsePhrasedFileName()
+    {
+        return usePhrasedFileName;
+    }
+
+    public void setUsePhrasedFileName( boolean usePhrasedFileName )
+    {
+        this.usePhrasedFileName = usePhrasedFileName;
+    }
+
+    public boolean isUsePhrasedClassNameInRunning()
+    {
+        return usePhrasedClassNameInRunning;
+    }
+
+    public void setUsePhrasedClassNameInRunning( boolean usePhrasedClassNameInRunning )
+    {
+        this.usePhrasedClassNameInRunning = usePhrasedClassNameInRunning;
+    }
+
+    public boolean isUsePhrasedClassNameInTestCaseSummary()
+    {
+        return usePhrasedClassNameInTestCaseSummary;
+    }
+
+    public void setUsePhrasedClassNameInTestCaseSummary( boolean usePhrasedClassNameInTestCaseSummary )
+    {
+        this.usePhrasedClassNameInTestCaseSummary = usePhrasedClassNameInTestCaseSummary;
+    }
+
+    @Override
+    public StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> createListener(
+            ConsoleLogger logger )
+    {
+        return new ConsoleReporter( logger, isUsePhrasedClassNameInRunning(),
+                                    isUsePhrasedClassNameInTestCaseSummary() );
+    }
+
+    @Override
+    public StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> createListener(
+            File reportsDirectory, String reportNameSuffix, Charset encoding )
+    {
+        return new FileReporter( reportsDirectory, reportNameSuffix, encoding, isUsePhrasedFileName(),
+                                 isUsePhrasedClassNameInRunning(), isUsePhrasedClassNameInTestCaseSummary() );
+    }
+
+    @Override
+    public Object clone( ClassLoader target )
+    {
+        try
+        {
+            Object clone = super.clone( target );
+
+            Class<?> cls = clone.getClass();
+            cls.getMethod( "setUsePhrasedFileName", boolean.class )
+                    .invoke( clone, isUsePhrasedFileName() );
+            cls.getMethod( "setUsePhrasedClassNameInTestCaseSummary", boolean.class )
+                    .invoke( clone, isUsePhrasedFileName() );
+            cls.getMethod( "setUsePhrasedClassNameInRunning", boolean.class )
+                    .invoke( clone, isUsePhrasedFileName() );
+
+            return clone;
+        }
+        catch ( ReflectiveOperationException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage() );
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return "JUnit5StatelessTestsetInfoReporter{"
+                + "disable=" + isDisable()
+                + ", usePhrasedFileName=" + isUsePhrasedFileName()
+                + ", usePhrasedClassNameInRunning=" + isUsePhrasedClassNameInRunning()
+                + ", usePhrasedClassNameInTestCaseSummary=" + isUsePhrasedClassNameInTestCaseSummary()
+                + "}";
+    }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5Xml30StatelessReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5Xml30StatelessReporter.java
new file mode 100644
index 0000000..83dca7d
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5Xml30StatelessReporter.java
@@ -0,0 +1,161 @@
+package org.apache.maven.plugin.surefire.extensions.junit5;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.extensions.DefaultStatelessReportMojoConfiguration;
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
+import org.apache.maven.plugin.surefire.report.StatelessXmlReporter;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.surefire.extensions.StatelessReportEventListener;
+
+/**
+ * The extension of {@link StatelessXmlReporter xml reporter} based on XSD version 3.0 for JUnit5.
+ * Selectively enables phrased classes, methods and report files upon JUnit5 annotation <em>DisplayName</em>.
+ *
+ * author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ */
+public class JUnit5Xml30StatelessReporter
+        extends SurefireStatelessReporter
+{
+    /**
+     * {@code false} by default.
+     * <br>
+     * This action takes effect only in JUnit5 provider together with a test class annotated <em>DisplayName</em>.
+     */
+    private boolean usePhrasedFileName;
+
+    /**
+     * {@code false} by default.
+     * <br>
+     * This action takes effect only in JUnit5 provider together with a test class annotated <em>DisplayName</em>.
+     */
+    private boolean usePhrasedTestSuiteClassName;
+
+    /**
+     * {@code false} by default.
+     * <br>
+     * This action takes effect only in JUnit5 provider together with a test class annotated <em>DisplayName</em>.
+     */
+    private boolean usePhrasedTestCaseClassName;
+
+    /**
+     * {@code false} by default.
+     * <br>
+     * This action takes effect only in JUnit5 provider together with a test method annotated <em>DisplayName</em>.
+     */
+    private boolean usePhrasedTestCaseMethodName;
+
+    public boolean getUsePhrasedFileName()
+    {
+        return usePhrasedFileName;
+    }
+
+    public void setUsePhrasedFileName( boolean usePhrasedFileName )
+    {
+        this.usePhrasedFileName = usePhrasedFileName;
+    }
+
+    public boolean getUsePhrasedTestSuiteClassName()
+    {
+        return usePhrasedTestSuiteClassName;
+    }
+
+    public void setUsePhrasedTestSuiteClassName( boolean usePhrasedTestSuiteClassName )
+    {
+        this.usePhrasedTestSuiteClassName = usePhrasedTestSuiteClassName;
+    }
+
+    public boolean getUsePhrasedTestCaseClassName()
+    {
+        return usePhrasedTestCaseClassName;
+    }
+
+    public void setUsePhrasedTestCaseClassName( boolean usePhrasedTestCaseClassName )
+    {
+        this.usePhrasedTestCaseClassName = usePhrasedTestCaseClassName;
+    }
+
+    public boolean getUsePhrasedTestCaseMethodName()
+    {
+        return usePhrasedTestCaseMethodName;
+    }
+
+    public void setUsePhrasedTestCaseMethodName( boolean usePhrasedTestCaseMethodName )
+    {
+        this.usePhrasedTestCaseMethodName = usePhrasedTestCaseMethodName;
+    }
+
+    @Override
+    public StatelessReportEventListener<WrappedReportEntry, TestSetStats> createListener(
+            DefaultStatelessReportMojoConfiguration configuration )
+    {
+        return new StatelessXmlReporter( configuration.getReportsDirectory(),
+                configuration.getReportNameSuffix(),
+                configuration.isTrimStackTrace(),
+                configuration.getRerunFailingTestsCount(),
+                configuration.getTestClassMethodRunHistory(),
+                configuration.getXsdSchemaLocation(),
+                getVersion(),
+                getUsePhrasedFileName(),
+                getUsePhrasedTestSuiteClassName(),
+                getUsePhrasedTestCaseClassName(),
+                getUsePhrasedTestCaseMethodName() );
+    }
+
+    @Override
+    public Object clone( ClassLoader target )
+    {
+        try
+        {
+            Object clone = super.clone( target );
+
+            Class<?> cls = clone.getClass();
+            cls.getMethod( "setUsePhrasedFileName", boolean.class )
+                    .invoke( clone, getUsePhrasedFileName() );
+            cls.getMethod( "setUsePhrasedTestSuiteClassName", boolean.class )
+                    .invoke( clone, getUsePhrasedTestSuiteClassName() );
+            cls.getMethod( "setUsePhrasedTestCaseClassName", boolean.class )
+                    .invoke( clone, getUsePhrasedTestCaseClassName() );
+            cls.getMethod( "setUsePhrasedTestCaseMethodName", boolean.class )
+                    .invoke( clone, getUsePhrasedTestCaseMethodName() );
+
+            return clone;
+        }
+        catch ( ReflectiveOperationException e )
+        {
+            throw new IllegalStateException( e.getLocalizedMessage() );
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return "JUnit5Xml30StatelessReporter{"
+                + "version=" + getVersion()
+                + ", disable=" + isDisable()
+                + ", usePhrasedFileName=" + getUsePhrasedFileName()
+                + ", usePhrasedTestSuiteClassName=" + getUsePhrasedTestSuiteClassName()
+                + ", usePhrasedTestCaseClassName=" + getUsePhrasedTestCaseClassName()
+                + ", usePhrasedTestCaseMethodName=" + getUsePhrasedTestCaseMethodName()
+                + '}';
+    }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
index 857b68e..3bc3f2c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
@@ -21,16 +21,17 @@ package org.apache.maven.plugin.surefire.report;
 
 import org.apache.maven.plugin.surefire.booterclient.output.InPluginProcessDumpSingleton;
 import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FilterOutputStream;
 import java.io.IOException;
+import java.nio.charset.Charset;
 import java.util.concurrent.atomic.AtomicStampedReference;
 import java.util.concurrent.locks.ReentrantLock;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.maven.plugin.surefire.report.FileReporter.getReportFile;
 import static org.apache.maven.surefire.util.internal.StringUtils.NL;
 
@@ -43,14 +44,16 @@ import static org.apache.maven.surefire.util.internal.StringUtils.NL;
 public class ConsoleOutputFileReporter
     implements TestcycleConsoleOutputReceiver
 {
-    private static final int STREAM_BUFFER_SIZE = 16 * 1024;
+    private static final int STREAM_BUFFER_SIZE = 64 * 1024;
     private static final int OPEN = 0;
     private static final int CLOSED_TO_REOPEN = 1;
     private static final int CLOSED = 2;
 
     private final File reportsDirectory;
     private final String reportNameSuffix;
+    private final boolean usePhrasedFileName;
     private final Integer forkNumber;
+    private final String encoding;
 
     private final AtomicStampedReference<FilterOutputStream> fileOutputStream =
             new AtomicStampedReference<>( null, OPEN );
@@ -59,15 +62,18 @@ public class ConsoleOutputFileReporter
 
     private volatile String reportEntryName;
 
-    public ConsoleOutputFileReporter( File reportsDirectory, String reportNameSuffix, Integer forkNumber )
+    public ConsoleOutputFileReporter( File reportsDirectory, String reportNameSuffix, boolean usePhrasedFileName,
+                                      Integer forkNumber, String encoding )
     {
         this.reportsDirectory = reportsDirectory;
         this.reportNameSuffix = reportNameSuffix;
+        this.usePhrasedFileName = usePhrasedFileName;
         this.forkNumber = forkNumber;
+        this.encoding = encoding;
     }
 
     @Override
-    public void testSetStarting( ReportEntry reportEntry )
+    public void testSetStarting( TestSetReportEntry reportEntry )
     {
         lock.lock();
         try
@@ -81,7 +87,7 @@ public class ConsoleOutputFileReporter
     }
 
     @Override
-    public void testSetCompleted( ReportEntry report )
+    public void testSetCompleted( TestSetReportEntry report )
     {
     }
 
@@ -128,10 +134,11 @@ public class ConsoleOutputFileReporter
                 {
                     output = "null";
                 }
-                os.write( output.getBytes( UTF_8 ) );
+                Charset charset = Charset.forName( encoding );
+                os.write( output.getBytes( charset ) );
                 if ( newLine )
                 {
-                    os.write( NL.getBytes( UTF_8 ) );
+                    os.write( NL.getBytes( charset ) );
                 }
             }
         }
@@ -162,7 +169,7 @@ public class ConsoleOutputFileReporter
         finally
         {
             // prepare <class>-output.txt report file
-            reportEntryName = reportEntry.getSourceName();
+            reportEntryName = usePhrasedFileName ? reportEntry.getSourceText() : reportEntry.getSourceName();
         }
     }
 
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
index 36f311b..0bee687 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
@@ -23,8 +23,9 @@ import java.util.List;
 
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.shared.utils.logging.MessageBuilder;
-import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.plugin.surefire.log.api.Level;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 
 import static org.apache.maven.plugin.surefire.log.api.Level.resolveLevel;
 import static org.apache.maven.plugin.surefire.report.TestSetStats.concatenateWithTestGroup;
@@ -37,31 +38,33 @@ import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
  * @author Kristian Rosenvold
  */
 public class ConsoleReporter
+        extends StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats>
 {
     public static final String BRIEF = "brief";
-
     public static final String PLAIN = "plain";
-
     private static final String TEST_SET_STARTING_PREFIX = "Running ";
 
-    private final ConsoleLogger logger;
-
-    public ConsoleReporter( ConsoleLogger logger )
-    {
-        this.logger = logger;
-    }
+    private final boolean usePhrasedClassNameInRunning;
+    private final boolean usePhrasedClassNameInTestCaseSummary;
 
-    public ConsoleLogger getConsoleLogger()
+    public ConsoleReporter( ConsoleLogger logger,
+                            boolean usePhrasedClassNameInRunning, boolean usePhrasedClassNameInTestCaseSummary )
     {
-        return logger;
+        super( logger );
+        this.usePhrasedClassNameInRunning = usePhrasedClassNameInRunning;
+        this.usePhrasedClassNameInTestCaseSummary = usePhrasedClassNameInTestCaseSummary;
     }
 
-    public void testSetStarting( ReportEntry report )
+    @Override
+    public void testSetStarting( TestSetReportEntry report )
     {
-        MessageBuilder builder = buffer();
-        logger.info( concatenateWithTestGroup( builder.a( TEST_SET_STARTING_PREFIX ), report ) );
+        MessageBuilder builder = buffer().a( TEST_SET_STARTING_PREFIX );
+        String runningTestCase = concatenateWithTestGroup( builder, report, usePhrasedClassNameInRunning );
+        getConsoleLogger()
+                .info( runningTestCase );
     }
 
+    @Override
     public void testSetCompleted( WrappedReportEntry report, TestSetStats testSetStats, List<String> testResults )
     {
         boolean success = testSetStats.getCompletedCount() > 0;
@@ -71,13 +74,14 @@ public class ConsoleReporter
         boolean flakes = testSetStats.getSkipped() > 0;
         Level level = resolveLevel( success, failures, errors, skipped, flakes );
 
-        println( testSetStats.getColoredTestSetSummary( report ), level );
+        println( testSetStats.getColoredTestSetSummary( report, usePhrasedClassNameInTestCaseSummary ), level );
         for ( String testResult : testResults )
         {
             println( testResult, level );
         }
     }
 
+    @Override
     public void reset()
     {
     }
@@ -87,13 +91,16 @@ public class ConsoleReporter
         switch ( level )
         {
             case FAILURE:
-                logger.error( message );
+                getConsoleLogger()
+                        .error( message );
                 break;
             case UNSTABLE:
-                logger.warning( message );
+                getConsoleLogger()
+                        .warning( message );
                 break;
             default:
-                logger.info( message );
+                getConsoleLogger()
+                        .info( message );
         }
     }
 }
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 d51b3e9..ed93a65 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
@@ -24,6 +24,10 @@ 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.shared.utils.logging.MessageBuilder;
+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.report.ReporterFactory;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.RunStatistics;
@@ -113,26 +117,32 @@ public class DefaultReporterFactory
         return reportConfiguration.getReportsDirectory();
     }
 
-    private ConsoleReporter createConsoleReporter()
+    private StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> createConsoleReporter()
     {
-        return shouldReportToConsole() ? new ConsoleReporter( consoleLogger ) : NullConsoleReporter.INSTANCE;
+        StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> consoleReporter =
+                reportConfiguration.instantiateConsoleReporter( consoleLogger );
+        return useNonNull( consoleReporter, NullConsoleReporter.INSTANCE );
     }
 
-    private FileReporter createFileReporter()
+    private StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> createFileReporter()
     {
-        FileReporter fileReporter = reportConfiguration.instantiateFileReporter( forkNumber );
+        StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> fileReporter =
+                reportConfiguration.instantiateFileReporter( forkNumber );
         return useNonNull( fileReporter, NullFileReporter.INSTANCE );
     }
 
-    private StatelessXmlReporter createSimpleXMLReporter()
+    private StatelessReportEventListener<WrappedReportEntry, TestSetStats> createSimpleXMLReporter()
     {
-        StatelessXmlReporter xmlReporter = reportConfiguration.instantiateStatelessXmlReporter( forkNumber );
+        StatelessReportEventListener<WrappedReportEntry, TestSetStats> xmlReporter =
+                reportConfiguration.instantiateStatelessXmlReporter( forkNumber );
         return useNonNull( xmlReporter, NullStatelessXmlReporter.INSTANCE );
     }
 
-    private TestcycleConsoleOutputReceiver createConsoleOutputReceiver()
+    private ConsoleOutputReportEventListener createConsoleOutputReceiver()
     {
-        return reportConfiguration.instantiateConsoleOutputFileReporter( forkNumber );
+        ConsoleOutputReportEventListener outputReporter =
+                reportConfiguration.instantiateConsoleOutputFileReporter( forkNumber );
+        return useNonNull( outputReporter, NullConsoleOutputReceiver.INSTANCE );
     }
 
     private StatisticsReporter createStatisticsReporter()
@@ -141,13 +151,6 @@ public class DefaultReporterFactory
         return useNonNull( statisticsReporter, NullStatisticsReporter.INSTANCE );
     }
 
-    private boolean shouldReportToConsole()
-    {
-        return reportConfiguration.isUseFile()
-                       ? reportConfiguration.isPrintSummary()
-                       : reportConfiguration.isRedirectTestOutputToFile() || reportConfiguration.isBriefOrPlainFormat();
-    }
-
     public void mergeFromOtherFactories( Collection<DefaultReporterFactory> factories )
     {
         for ( DefaultReporterFactory factory : factories )
@@ -272,7 +275,7 @@ public class DefaultReporterFactory
      * Merge all the TestMethodStats in each TestRunListeners and put results into flakyTests, failedTests and
      * errorTests, indexed by test class and method name. Update globalStatistics based on the result of the merge.
      */
-    void mergeTestHistoryResult()
+    private void mergeTestHistoryResult()
     {
         globalStats = new RunStatistics();
         flakyTests = new TreeMap<>();
@@ -388,7 +391,6 @@ public class DefaultReporterFactory
 
         for ( Map.Entry<String, List<TestMethodStats>> entry : testStats.entrySet() )
         {
-            printed = true;
             List<TestMethodStats> testMethodStats = entry.getValue();
             if ( testMethodStats.size() == 1 )
             {
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
index 6a94562..7ef2d82 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
@@ -19,7 +19,7 @@ package org.apache.maven.plugin.surefire.report;
  * under the License.
  */
 
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 
 import java.io.PrintStream;
 
@@ -36,20 +36,20 @@ import static java.util.Objects.requireNonNull;
 public class DirectConsoleOutput
     implements TestcycleConsoleOutputReceiver
 {
-    private final PrintStream sout;
+    private final PrintStream out;
 
-    private final PrintStream serr;
+    private final PrintStream err;
 
-    public DirectConsoleOutput( PrintStream sout, PrintStream serr )
+    public DirectConsoleOutput( PrintStream out, PrintStream err )
     {
-        this.sout = requireNonNull( sout );
-        this.serr = requireNonNull( serr );
+        this.out = requireNonNull( out );
+        this.err = requireNonNull( err );
     }
 
     @Override
     public void writeTestOutput( String output, boolean newLine, boolean stdout )
     {
-        PrintStream stream = stdout ? sout : serr;
+        PrintStream stream = stdout ? out : err;
         if ( newLine )
         {
             stream.println( output );
@@ -61,12 +61,12 @@ public class DirectConsoleOutput
     }
 
     @Override
-    public void testSetStarting( ReportEntry reportEntry )
+    public void testSetStarting( TestSetReportEntry reportEntry )
     {
     }
 
     @Override
-    public void testSetCompleted( ReportEntry report )
+    public void testSetCompleted( TestSetReportEntry report )
     {
     }
 
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
index 941b88a..2301e34 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
@@ -19,6 +19,7 @@ package org.apache.maven.plugin.surefire.report;
  * under the License.
  */
 
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
 import org.apache.maven.surefire.report.ReporterException;
 
 import java.io.BufferedWriter;
@@ -40,47 +41,55 @@ import static org.apache.maven.surefire.util.internal.StringUtils.isNotBlank;
  * @author Kristian Rosenvold
  */
 public class FileReporter
+        extends StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats>
 {
-    private final File reportsDirectory;
-    private final String reportNameSuffix;
-    private final Charset encoding;
+    private final boolean usePhrasedFileName;
+    private final boolean usePhrasedClassNameInRunning;
+    private final boolean usePhrasedClassNameInTestCaseSummary;
 
-    public FileReporter( File reportsDirectory, String reportNameSuffix, Charset encoding )
+    public FileReporter( File reportsDirectory, String reportNameSuffix, Charset encoding, boolean usePhrasedFileName,
+                         boolean usePhrasedClassNameInRunning, boolean usePhrasedClassNameInTestCaseSummary )
     {
-        this.reportsDirectory = reportsDirectory;
-        this.reportNameSuffix = reportNameSuffix;
-        this.encoding = encoding;
+        super( reportsDirectory, reportNameSuffix, encoding );
+        this.usePhrasedFileName = usePhrasedFileName;
+        this.usePhrasedClassNameInRunning = usePhrasedClassNameInRunning;
+        this.usePhrasedClassNameInTestCaseSummary = usePhrasedClassNameInTestCaseSummary;
     }
 
     static File getReportFile( File reportsDirectory, String reportEntryName, String reportNameSuffix,
-                                      String fileExtension )
+                               String fileExtension )
     {
         String fileName =
                 reportEntryName + ( isNotBlank( reportNameSuffix ) ? "-" + reportNameSuffix : "" ) + fileExtension;
         return new File( reportsDirectory, stripIllegalFilenameChars( fileName ) );
     }
 
+    @Override
     public void testSetCompleted( WrappedReportEntry report, TestSetStats testSetStats, List<String> testResults )
     {
-        File reportFile = getReportFile( reportsDirectory, report.getSourceName(), reportNameSuffix, ".txt" );
+        File reportFile = getReportFile( getReportsDirectory(),
+                                         usePhrasedFileName ? report.getReportSourceName() : report.getSourceName(),
+                                         getReportNameSuffix(),
+                                         ".txt" );
 
         File reportDir = reportFile.getParentFile();
 
         // noinspection ResultOfMethodCallIgnored
         reportDir.mkdirs();
 
-        try ( BufferedWriter writer = createFileReporterWriter( reportFile, encoding ) )
+        try ( BufferedWriter writer = createFileReporterWriter( reportFile, getEncoding() ) )
         {
             writer.write( "-------------------------------------------------------------------------------" );
             writer.newLine();
 
-            writer.write( "Test set: " + report.getSourceName() );
+            String tesSet = usePhrasedClassNameInRunning ? report.getReportSourceName() : report.getSourceName();
+            writer.write( "Test set: " + tesSet );
             writer.newLine();
 
             writer.write( "-------------------------------------------------------------------------------" );
             writer.newLine();
 
-            writer.write( testSetStats.getTestSetSummary( report ) );
+            writer.write( testSetStats.getTestSetSummary( report, usePhrasedClassNameInTestCaseSummary ) );
             writer.newLine();
             for ( String testResult : testResults )
             {
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleOutputReceiver.java
similarity index 57%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java
copy to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleOutputReceiver.java
index af68d1e..a826b67 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleOutputReceiver.java
@@ -19,40 +19,45 @@ package org.apache.maven.plugin.surefire.report;
  * under the License.
  */
 
-import java.util.List;
-
-import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 
 /**
- * ConsoleReporter doing nothing rather than using null.
+ * TestcycleConsoleOutputReceiver doing nothing rather than using null.
  *
- * @author <a href="mailto:britter@apache.org">Benedikt Ritter</a>
- * @since 2.20
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
  */
-class NullConsoleReporter
-    extends ConsoleReporter
+public class NullConsoleOutputReceiver
+    implements TestcycleConsoleOutputReceiver
 {
 
-    static final NullConsoleReporter INSTANCE = new NullConsoleReporter();
+    static final NullConsoleOutputReceiver INSTANCE = new NullConsoleOutputReceiver();
 
-    private NullConsoleReporter()
+    private NullConsoleOutputReceiver()
     {
-        super( new NullConsoleLogger() );
     }
 
     @Override
-    public void testSetStarting( ReportEntry report )
+    public void testSetStarting( TestSetReportEntry reportEntry )
     {
+
     }
 
     @Override
-    public void testSetCompleted( WrappedReportEntry report, TestSetStats testSetStats, List<String> testResults )
+    public void testSetCompleted( TestSetReportEntry report )
     {
+
     }
 
     @Override
-    public void reset()
+    public void close()
     {
+
+    }
+
+    @Override
+    public void writeTestOutput( String output, boolean newLine, boolean stdout )
+    {
+
     }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java
index af68d1e..f33fca1 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java
@@ -22,7 +22,7 @@ package org.apache.maven.plugin.surefire.report;
 import java.util.List;
 
 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 
 /**
  * ConsoleReporter doing nothing rather than using null.
@@ -38,11 +38,11 @@ class NullConsoleReporter
 
     private NullConsoleReporter()
     {
-        super( new NullConsoleLogger() );
+        super( new NullConsoleLogger(), false, false );
     }
 
     @Override
-    public void testSetStarting( ReportEntry report )
+    public void testSetStarting( TestSetReportEntry report )
     {
     }
 
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullFileReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullFileReporter.java
index df1bf9d..be217eb 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullFileReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullFileReporter.java
@@ -35,7 +35,7 @@ class NullFileReporter
 
     private NullFileReporter()
     {
-        super( null, null, null );
+        super( null, null, null, false, false, false );
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullStatelessXmlReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullStatelessXmlReporter.java
index 7f5d202..d43369d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullStatelessXmlReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullStatelessXmlReporter.java
@@ -33,7 +33,7 @@ class NullStatelessXmlReporter
 
     private NullStatelessXmlReporter()
     {
-        super( null, null, false, 0, null, null );
+        super( null, null, false, 0, null, null, null, false, false, false, false );
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
index 11cec8d..4e41d54 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
@@ -22,7 +22,7 @@ package org.apache.maven.plugin.surefire.report;
 import org.apache.maven.plugin.surefire.booterclient.output.InPluginProcessDumpSingleton;
 import org.apache.maven.shared.utils.xml.PrettyPrintXMLWriter;
 import org.apache.maven.shared.utils.xml.XMLWriter;
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.extensions.StatelessReportEventListener;
 import org.apache.maven.surefire.report.ReporterException;
 import org.apache.maven.surefire.report.SafeThrowable;
 
@@ -82,8 +82,9 @@ import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
  * @see <a href="http://wiki.apache.org/ant/Proposals/EnhancedTestReports">Ant's format enhancement proposal</a>
  *      (not yet implemented by Ant 1.8.2)
  */
-@Deprecated // this is no more stateless due to existence of testClassMethodRunHistoryMap since of 2.19. Rename to StatefulXmlReporter in 3.0.
+//todo this is no more stateless due to existence of testClassMethodRunHistoryMap since of 2.19.
 public class StatelessXmlReporter
+        implements StatelessReportEventListener<WrappedReportEntry, TestSetStats>
 {
     private final File reportsDirectory;
 
@@ -95,14 +96,25 @@ public class StatelessXmlReporter
 
     private final String xsdSchemaLocation;
 
+    private final String xsdVersion;
+
     // Map between test class name and a map between test method name
     // and the list of runs for each test method
     private final Map<String, Deque<WrappedReportEntry>> testClassMethodRunHistoryMap;
 
+    private final boolean phrasedFileName;
+
+    private final boolean phrasedSuiteName;
+
+    private final boolean phrasedClassName;
+
+    private final boolean phrasedMethodName;
+
     public StatelessXmlReporter( File reportsDirectory, String reportNameSuffix, boolean trimStackTrace,
                                  int rerunFailingTestsCount,
                                  Map<String, Deque<WrappedReportEntry>> testClassMethodRunHistoryMap,
-                                 String xsdSchemaLocation )
+                                 String xsdSchemaLocation, String xsdVersion, boolean phrasedFileName,
+                                 boolean phrasedSuiteName, boolean phrasedClassName, boolean phrasedMethodName )
     {
         this.reportsDirectory = reportsDirectory;
         this.reportNameSuffix = reportNameSuffix;
@@ -110,8 +122,14 @@ public class StatelessXmlReporter
         this.rerunFailingTestsCount = rerunFailingTestsCount;
         this.testClassMethodRunHistoryMap = testClassMethodRunHistoryMap;
         this.xsdSchemaLocation = xsdSchemaLocation;
+        this.xsdVersion = xsdVersion;
+        this.phrasedFileName = phrasedFileName;
+        this.phrasedSuiteName = phrasedSuiteName;
+        this.phrasedClassName = phrasedClassName;
+        this.phrasedMethodName = phrasedMethodName;
     }
 
+    @Override
     public void testSetCompleted( WrappedReportEntry testSetReportEntry, TestSetStats testSetStats )
     {
         Map<String, Map<String, List<WrappedReportEntry>>> classMethodStatistics =
@@ -344,9 +362,9 @@ public class StatelessXmlReporter
         return new OutputStreamWriter( fos, UTF_8 );
     }
 
-    private File getReportFile( ReportEntry report )
+    private File getReportFile( WrappedReportEntry report )
     {
-        String reportName = "TEST-" + report.getSourceName();
+        String reportName = "TEST-" + ( phrasedFileName ? report.getReportSourceName() : report.getSourceName() );
         String customizedReportName = isBlank( reportNameSuffix ) ? reportName : reportName + "-" + reportNameSuffix;
         return new File( reportsDirectory, stripIllegalFilenameChars( customizedReportName + ".xml" ) );
     }
@@ -354,14 +372,16 @@ public class StatelessXmlReporter
     private void startTestElement( XMLWriter ppw, WrappedReportEntry report )
     {
         ppw.startElement( "testcase" );
-        ppw.addAttribute( "name", report.getName() == null ? "" : extraEscape( report.getName(), true ) );
+        String name = phrasedMethodName ? report.getReportName() : report.getName();
+        ppw.addAttribute( "name", name == null ? "" : extraEscape( name, true ) );
 
         if ( report.getGroup() != null )
         {
             ppw.addAttribute( "group", report.getGroup() );
         }
 
-        String className = report.getReportName( reportNameSuffix );
+        String className = phrasedClassName ? report.getReportSourceName( reportNameSuffix )
+                : report.getSourceName( reportNameSuffix );
         if ( className != null )
         {
             ppw.addAttribute( "classname", extraEscape( className, true ) );
@@ -376,9 +396,10 @@ public class StatelessXmlReporter
 
         ppw.addAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
         ppw.addAttribute( "xsi:noNamespaceSchemaLocation", xsdSchemaLocation );
-        ppw.addAttribute( "version", "3.0" );
+        ppw.addAttribute( "version", xsdVersion );
 
-        String reportName = report.getReportName( reportNameSuffix );
+        String reportName = phrasedSuiteName ? report.getReportSourceName( reportNameSuffix )
+                : report.getSourceName( reportNameSuffix );
         ppw.addAttribute( "name", reportName == null ? "" : extraEscape( reportName, true ) );
 
         if ( report.getGroup() != null )
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 decf63c..2adbac4 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
@@ -27,6 +27,10 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
+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.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
@@ -53,15 +57,15 @@ public class TestSetRunListener
 
     private final TestSetStats detailsForThis;
 
-    private final TestcycleConsoleOutputReceiver consoleOutputReceiver;
+    private final ConsoleOutputReportEventListener consoleOutputReceiver;
 
     private final boolean briefOrPlainFormat;
 
-    private final StatelessXmlReporter simpleXMLReporter;
+    private final StatelessReportEventListener<WrappedReportEntry, TestSetStats> simpleXMLReporter;
 
-    private final ConsoleReporter consoleReporter;
+    private final StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> consoleReporter;
 
-    private final FileReporter fileReporter;
+    private final StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> fileReporter;
 
     private final StatisticsReporter statisticsReporter;
 
@@ -72,9 +76,12 @@ public class TestSetRunListener
     private volatile RunMode runMode = NORMAL_RUN;
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    public TestSetRunListener( ConsoleReporter consoleReporter, FileReporter fileReporter,
-                               StatelessXmlReporter simpleXMLReporter,
-                               TestcycleConsoleOutputReceiver consoleOutputReceiver,
+    public TestSetRunListener( StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats>
+                                           consoleReporter,
+                               StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats>
+                                       fileReporter,
+                               StatelessReportEventListener<WrappedReportEntry, TestSetStats> simpleXMLReporter,
+                               ConsoleOutputReportEventListener consoleOutputReceiver,
                                StatisticsReporter statisticsReporter, boolean trimStackTrace,
                                boolean isPlainFormat, boolean briefOrPlainFormat )
     {
@@ -138,7 +145,7 @@ public class TestSetRunListener
     @Override
     public void error( String message, Throwable t )
     {
-        consoleReporter.getConsoleLogger().error( message, t );
+        consoleReporter.getConsoleLogger().error( trimTrailingNewLine( message ), t );
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
index 8698159..3e360c1 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
@@ -29,6 +29,7 @@ import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
+import static org.apache.maven.surefire.report.CategorizedReportEntry.GROUP_PREFIX;
 
 /**
  * Maintains per-thread test result state. Not thread safe.
@@ -169,7 +170,7 @@ public class TestSetStats
         completedCount += 1;
     }
 
-    public String getTestSetSummary( WrappedReportEntry reportEntry )
+    public String getTestSetSummary( WrappedReportEntry reportEntry, boolean phrasedClassName )
     {
         String summary = TESTS_RUN + completedCount
                                  + COMMA
@@ -186,12 +187,13 @@ public class TestSetStats
             summary += FAILURE_MARKER;
         }
 
-        summary += IN_MARKER + reportEntry.getNameWithGroup();
+        summary += IN_MARKER;
+        summary += phrasedClassName ? reportEntry.getReportNameWithGroup() : reportEntry.getNameWithGroup();
 
         return summary;
     }
 
-    public String getColoredTestSetSummary( WrappedReportEntry reportEntry )
+    public String getColoredTestSetSummary( WrappedReportEntry reportEntry, boolean phrasedClassName )
     {
         final boolean isSuccessful = failures == 0 && errors == 0 && skipped == 0;
         final boolean isFailure = failures > 0;
@@ -255,7 +257,7 @@ public class TestSetStats
             builder.failure( FAILURE_MARKER );
         }
         builder.a( IN_MARKER );
-        return concatenateWithTestGroup( builder, reportEntry );
+        return concatenateWithTestGroup( builder, reportEntry, phrasedClassName );
     }
 
     public List<String> getTestResults()
@@ -269,7 +271,7 @@ public class TestSetStats
             }
             else if ( plainFormat && testResult.isSkipped() )
             {
-                result.add( testResult.getName() + " skipped" );
+                result.add( testResult.getSourceName() + " skipped" );
             }
             else if ( plainFormat && testResult.isSucceeded() )
             {
@@ -287,20 +289,29 @@ public class TestSetStats
 
     /**
      * Append the test set message for a report.
-     * e.g. "org.foo.BarTest ( of group )"
+     * e.g. "org.foo.BarTest ( of group )" or phrased text "test class description ( of group )".
      *
      * @param builder    MessageBuilder with preceded text inside
      * @param report     report whose test set is starting
      * @return the message
      */
-    static String concatenateWithTestGroup( MessageBuilder builder, ReportEntry report )
+    static String concatenateWithTestGroup( MessageBuilder builder, ReportEntry report, boolean phrasedClassName )
     {
-        final String testClass = report.getNameWithGroup();
-        int delimiter = testClass.lastIndexOf( '.' );
-        String pkg = testClass.substring( 0, 1 + delimiter );
-        String cls = testClass.substring( 1 + delimiter );
-        return builder.a( pkg )
-                       .strong( cls )
-                       .toString();
+        if ( phrasedClassName )
+        {
+            return builder.strong( report.getReportNameWithGroup() )
+                    .toString();
+        }
+        else
+        {
+            String testClass = report.getNameWithGroup();
+            int indexOfGroup = testClass.indexOf( GROUP_PREFIX );
+            int delimiter = testClass.lastIndexOf( '.', indexOfGroup == -1 ? testClass.length() : indexOfGroup );
+            String pkg = testClass.substring( 0, 1 + delimiter );
+            String cls = testClass.substring( 1 + delimiter );
+            return builder.a( pkg )
+                    .strong( cls )
+                    .toString();
+        }
     }
 }
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 f1b5b5a..cdd4b3a 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
@@ -19,18 +19,19 @@ package org.apache.maven.plugin.surefire.report;
  * under the License.
  */
 
+import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 
 /**
  * @author Kristian Rosenvold
  */
 public interface TestcycleConsoleOutputReceiver
-    extends ConsoleOutputReceiver
+    extends ConsoleOutputReceiver, ConsoleOutputReportEventListener
 {
-    void testSetStarting( ReportEntry reportEntry );
+    void testSetStarting( TestSetReportEntry reportEntry );
 
-    void testSetCompleted( ReportEntry report );
+    void testSetCompleted( TestSetReportEntry report );
 
     void close();
 }
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 bb4c1b1..102eea4 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
@@ -103,14 +103,26 @@ public class WrappedReportEntry
     }
 
     @Override
+    public String getSourceText()
+    {
+        return original.getSourceText();
+    }
+
+    @Override
     public String getName()
     {
         return original.getName();
     }
 
+    @Override
+    public String getNameText()
+    {
+        return original.getNameText();
+    }
+
     public String getClassMethodName()
     {
-        return getSourceName() + "." + getName();
+        return original.getSourceName() + "." + original.getName();
     }
 
     @Override
@@ -142,14 +154,28 @@ public class WrappedReportEntry
         return formatElapsedTime( getElapsed() );
     }
 
-    public String getReportName()
+    String getReportSourceName()
+    {
+        String sourceName = getSourceName();
+        String sourceText = getSourceText();
+        return isBlank( sourceText ) ? sourceName : sourceText;
+    }
+
+    String getReportSourceName( String suffix )
+    {
+        return isBlank( suffix ) ? getReportSourceName() : getReportSourceName() + "(" + suffix + ")";
+    }
+
+    String getSourceName( String suffix )
     {
-        return getSourceName();
+        return isBlank( suffix ) ? getSourceName() : getSourceName() + "(" + suffix + ")";
     }
 
-    public String getReportName( String suffix )
+    String getReportName()
     {
-        return isBlank( suffix ) ? getReportName() : getReportName() + "(" + suffix + ")";
+        String name = getName();
+        String nameText = getNameText();
+        return isBlank( nameText ) ? name : nameText;
     }
 
     public String getOutput( boolean trimStackTrace )
@@ -193,6 +219,12 @@ public class WrappedReportEntry
     }
 
     @Override
+    public String getReportNameWithGroup()
+    {
+        return original.getReportNameWithGroup();
+    }
+
+    @Override
     public Map<String, String> getSystemProperties()
     {
         return systemProperties;
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/RunStatistics.java b/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/RunStatistics.java
index 29aa8dc..7c911ec 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/RunStatistics.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/RunStatistics.java
@@ -19,13 +19,12 @@ package org.apache.maven.surefire.report;
  * under the License.
  */
 
-import org.apache.maven.plugin.surefire.report.TestSetStats;
 import org.apache.maven.surefire.suite.RunResult;
 
 /**
  * @author Kristian Rosenvold
  */
-public class RunStatistics
+public final class RunStatistics
 {
     private int completedCount;
 
@@ -37,16 +36,6 @@ public class RunStatistics
 
     private int flakes;
 
-    public synchronized boolean hadFailures()
-    {
-        return failures > 0;
-    }
-
-    public synchronized boolean hadErrors()
-    {
-        return errors > 0;
-    }
-
     public synchronized int getCompletedCount()
     {
         return completedCount;
@@ -72,14 +61,6 @@ public class RunStatistics
         return flakes;
     }
 
-    public synchronized void add( TestSetStats testSetStats )
-    {
-        this.completedCount += testSetStats.getCompletedCount();
-        this.errors += testSetStats.getErrors();
-        this.failures += testSetStats.getFailures();
-        this.skipped += testSetStats.getSkipped();
-    }
-
     public synchronized void set( int completedCount, int errors, int failures, int skipped, int flakes )
     {
         this.completedCount = completedCount;
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
index 2d268bc..b178d64 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
@@ -39,6 +39,7 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
@@ -60,7 +61,6 @@ import static org.powermock.api.mockito.PowerMockito.doNothing;
 import static org.powermock.api.mockito.PowerMockito.mock;
 import static org.powermock.api.mockito.PowerMockito.mockStatic;
 import static org.powermock.api.mockito.PowerMockito.spy;
-import static org.powermock.api.mockito.PowerMockito.verifyPrivate;
 import static org.powermock.api.mockito.PowerMockito.verifyStatic;
 import static org.powermock.reflect.Whitebox.invokeMethod;
 
@@ -69,6 +69,7 @@ import static org.powermock.reflect.Whitebox.invokeMethod;
  */
 @RunWith( PowerMockRunner.class )
 @PrepareForTest( { AbstractSurefireMojo.class, ResolvePathsRequest.class } )
+@PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
 public class AbstractSurefireMojoJava7PlusTest
 {
     @Mock
@@ -155,6 +156,10 @@ public class AbstractSurefireMojoJava7PlusTest
                 "jar", "", handler );
         common.setFile( mockFile( "maven-surefire-common.jar" ) );
 
+        Artifact ext = new DefaultArtifact( "org.apache.maven.surefire", "surefire-extensions-api", v5, "runtime",
+                "jar", "", handler );
+        ext.setFile( mockFile( "surefire-extensions-api.jar" ) );
+
         Artifact api = new DefaultArtifact( "org.apache.maven.surefire", "surefire-api", v5, "runtime",
                 "jar", "", handler );
         api.setFile( mockFile( "surefire-api.jar" ) );
@@ -165,6 +170,7 @@ public class AbstractSurefireMojoJava7PlusTest
 
         Map<String, Artifact> artifacts = new HashMap<>();
         artifacts.put( "org.apache.maven.surefire:maven-surefire-common", common );
+        artifacts.put( "org.apache.maven.surefire:surefire-extensions-api", ext );
         artifacts.put( "org.apache.maven.surefire:surefire-api", api );
         artifacts.put( "org.apache.maven.surefire:surefire-logger-api", loggerApi );
         when( mojo.getPluginArtifactMap() ).thenReturn( artifacts );
@@ -200,8 +206,8 @@ public class AbstractSurefireMojoJava7PlusTest
                         "test(compact) classpath:  non-modular.jar  junit.jar  hamcrest.jar",
                         "test(compact) modulepath:  modular.jar  classes",
                         "provider(compact) classpath:  surefire-provider.jar",
-                        "in-process classpath:  surefire-provider.jar  maven-surefire-common.jar  surefire-api.jar  surefire-logger-api.jar",
-                        "in-process(compact) classpath:  surefire-provider.jar  maven-surefire-common.jar  surefire-api.jar  surefire-logger-api.jar"
+                        "in-process classpath:  surefire-provider.jar  maven-surefire-common.jar  surefire-extensions-api.jar  surefire-api.jar  surefire-logger-api.jar",
+                        "in-process(compact) classpath:  surefire-provider.jar  maven-surefire-common.jar  surefire-extensions-api.jar  surefire-api.jar  surefire-logger-api.jar"
                 );
 
         assertThat( conf ).isNotNull();
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
index 080f2b5..cec6760 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
@@ -50,6 +50,7 @@ import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
@@ -90,6 +91,7 @@ import static org.powermock.reflect.Whitebox.invokeMethod;
  */
 @RunWith( PowerMockRunner.class )
 @PrepareForTest( AbstractSurefireMojo.class )
+@PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
 public class AbstractSurefireMojoTest
 {
     @Mock
@@ -236,6 +238,10 @@ public class AbstractSurefireMojoTest
                 createFromVersion( "1" ), "runtime", "jar", "", handler );
         common.setFile( mockFile( "maven-surefire-common.jar" ) );
 
+        Artifact ext = new DefaultArtifact( "org.apache.maven.surefire", "surefire-extensions-api",
+                createFromVersion( "1" ), "runtime", "jar", "", handler );
+        ext.setFile( mockFile( "surefire-extensions-api.jar" ) );
+
         Artifact api = new DefaultArtifact( "org.apache.maven.surefire", "surefire-api",
                 createFromVersion( "1" ), "runtime", "jar", "", handler );
         api.setFile( mockFile( "surefire-api.jar" ) );
@@ -246,6 +252,7 @@ public class AbstractSurefireMojoTest
 
         Map<String, Artifact> providerArtifactsMap = new HashMap<>();
         providerArtifactsMap.put( "org.apache.maven.surefire:maven-surefire-common", common );
+        providerArtifactsMap.put( "org.apache.maven.surefire:surefire-extensions-api", ext );
         providerArtifactsMap.put( "org.apache.maven.surefire:surefire-api", api );
         providerArtifactsMap.put( "org.apache.maven.surefire:surefire-logger-api", loggerApi );
 
@@ -297,8 +304,8 @@ public class AbstractSurefireMojoTest
                 "provider classpath:  surefire-provider.jar",
                 "test(compact) classpath:  test-classes  classes  junit.jar  hamcrest.jar",
                 "provider(compact) classpath:  surefire-provider.jar",
-                "in-process classpath:  surefire-provider.jar  maven-surefire-common.jar  surefire-api.jar  surefire-logger-api.jar",
-                "in-process(compact) classpath:  surefire-provider.jar  maven-surefire-common.jar  surefire-api.jar  surefire-logger-api.jar"
+                "in-process classpath:  surefire-provider.jar  maven-surefire-common.jar  surefire-extensions-api.jar  surefire-api.jar  surefire-logger-api.jar",
+                "in-process(compact) classpath:  surefire-provider.jar  maven-surefire-common.jar  surefire-extensions-api.jar  surefire-api.jar  surefire-logger-api.jar"
                 );
 
         assertThat( conf.getClassLoaderConfiguration() )
@@ -1571,7 +1578,7 @@ public class AbstractSurefireMojoTest
     }
 
     public static class Mojo
-            extends AbstractSurefireMojo
+            extends AbstractSurefireMojo implements SurefireReportParameters
     {
         private JUnitPlatformProviderInfo createJUnitPlatformProviderInfo( Artifact providerArtifact,
                                                                            TestClassPath testClasspathWrapper )
@@ -1634,6 +1641,18 @@ public class AbstractSurefireMojoTest
         }
 
         @Override
+        public boolean isTestFailureIgnore()
+        {
+            return false;
+        }
+
+        @Override
+        public void setTestFailureIgnore( boolean testFailureIgnore )
+        {
+
+        }
+
+        @Override
         public File getBasedir()
         {
             return null;
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/CommonReflectorTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/CommonReflectorTest.java
new file mode 100644
index 0000000..b00d131
--- /dev/null
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/CommonReflectorTest.java
@@ -0,0 +1,96 @@
+package org.apache.maven.plugin.surefire;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.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.plugin.surefire.report.DefaultReporterFactory;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.powermock.reflect.Whitebox.getInternalState;
+
+public class CommonReflectorTest
+{
+    private StartupReportConfiguration startupReportConfiguration;
+    private ConsoleLogger consoleLogger;
+    private File reportsDirectory;
+    private File statistics;
+    private SurefireStatelessReporter xmlReporter;
+    private SurefireConsoleOutputReporter consoleOutputReporter = new SurefireConsoleOutputReporter();
+    private SurefireStatelessTestsetInfoReporter infoReporter = new SurefireStatelessTestsetInfoReporter();
+
+    @Before
+    public void setup()
+    {
+        File target = new File( System.getProperty( "user.dir" ), "target" );
+        reportsDirectory = new File( target, "tmp6" );
+        statistics = new File( reportsDirectory, "TESTHASH" );
+        xmlReporter = new SurefireStatelessReporter();
+        infoReporter = new SurefireStatelessTestsetInfoReporter();
+
+        startupReportConfiguration = new StartupReportConfiguration( true, true, "PLAIN", false, reportsDirectory,
+                false, null, statistics, false, 1, null, null, false,
+                xmlReporter, consoleOutputReporter, infoReporter);
+
+        consoleLogger = mock( ConsoleLogger.class );
+    }
+
+    @Test
+    public void createReportingReporterFactory()
+    {
+        CommonReflector reflector = new CommonReflector( Thread.currentThread().getContextClassLoader() );
+        DefaultReporterFactory factory = (DefaultReporterFactory) reflector.createReportingReporterFactory(
+                startupReportConfiguration, consoleLogger );
+
+        assertThat( factory )
+                .isNotNull();
+
+        StartupReportConfiguration reportConfiguration = getInternalState( factory, "reportConfiguration" );
+        assertThat( reportConfiguration )
+                .isNotSameAs( startupReportConfiguration );
+        assertThat( reportConfiguration.isUseFile() ).isTrue();
+        assertThat( reportConfiguration.isPrintSummary() ).isTrue();
+        assertThat( reportConfiguration.getReportFormat() ).isEqualTo( "PLAIN" );
+        assertThat( reportConfiguration.isRedirectTestOutputToFile() ).isFalse();
+        assertThat( reportConfiguration.getReportsDirectory() ).isSameAs( reportsDirectory );
+        assertThat( reportConfiguration.isTrimStackTrace() ).isFalse();
+        assertThat( reportConfiguration.getReportNameSuffix() ).isNull();
+        assertThat( reportConfiguration.getStatisticsFile() ).isSameAs( statistics );
+        assertThat( reportConfiguration.isRequiresRunHistory() ).isFalse();
+        assertThat( reportConfiguration.getRerunFailingTestsCount() ).isEqualTo( 1 );
+        assertThat( reportConfiguration.getXsdSchemaLocation() ).isNull();
+        assertThat( reportConfiguration.getEncoding() ).isEqualTo( UTF_8 );
+        assertThat( reportConfiguration.isForkMode() ).isFalse();
+        assertThat( reportConfiguration.getXmlReporter().toString() )
+                .isEqualTo( xmlReporter.toString() );
+        assertThat( reportConfiguration.getTestsetReporter().toString() )
+                .isEqualTo( infoReporter.toString() );
+        assertThat( reportConfiguration.getConsoleOutputReporter().toString() )
+                .isEqualTo( consoleOutputReporter.toString() );
+    }
+}
\ No newline at end of file
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
index f8ba15f..4371638 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
@@ -25,8 +25,12 @@ import org.apache.maven.artifact.handler.ArtifactHandler;
 import org.apache.maven.artifact.handler.DefaultArtifactHandler;
 import org.apache.maven.artifact.versioning.VersionRange;
 import org.apache.maven.plugin.MojoFailureException;
+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.surefire.suite.RunResult;
 import org.apache.maven.surefire.util.DefaultScanResult;
+import org.apache.maven.toolchain.Toolchain;
 import org.junit.Test;
 
 import java.io.File;
@@ -38,10 +42,123 @@ import java.util.zip.ZipOutputStream;
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
 import static org.fest.assertions.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.powermock.reflect.Whitebox.invokeMethod;
+import static org.powermock.reflect.Whitebox.setInternalState;
 
 public class MojoMocklessTest
 {
     @Test
+    public void testGetStartupReportConfiguration() throws Exception
+    {
+        AbstractSurefireMojo surefirePlugin = new Mojo( null, null );
+        StartupReportConfiguration config = invokeMethod( surefirePlugin, "getStartupReportConfiguration", "", false );
+
+        assertThat( config.getXmlReporter() )
+                .isNotNull()
+                .isInstanceOf( SurefireStatelessReporter.class );
+
+        assertThat( config.getConsoleOutputReporter() )
+                .isNotNull()
+                .isInstanceOf( SurefireConsoleOutputReporter.class );
+
+        assertThat( config.getTestsetReporter() )
+                .isNotNull()
+                .isInstanceOf( SurefireStatelessTestsetInfoReporter.class );
+    }
+
+    @Test
+    public void testGetStartupReportConfiguration2() throws Exception
+    {
+        AbstractSurefireMojo surefirePlugin = new Mojo( null, null );
+        SurefireStatelessReporter xmlReporter = new SurefireStatelessReporter( false, "3.0" );
+        SurefireConsoleOutputReporter consoleReporter = new SurefireConsoleOutputReporter();
+        SurefireStatelessTestsetInfoReporter testsetInfoReporter = new SurefireStatelessTestsetInfoReporter();
+        setInternalState( surefirePlugin, "statelessTestsetReporter", xmlReporter );
+        setInternalState( surefirePlugin, "consoleOutputReporter", consoleReporter );
+        setInternalState( surefirePlugin, "statelessTestsetInfoReporter", testsetInfoReporter );
+
+        StartupReportConfiguration config = invokeMethod( surefirePlugin, "getStartupReportConfiguration", "", false );
+
+        assertThat( config.getXmlReporter() )
+                .isNotNull()
+                .isSameAs( xmlReporter );
+
+        assertThat( config.getConsoleOutputReporter() )
+                .isNotNull()
+                .isSameAs( consoleReporter );
+
+        assertThat( config.getTestsetReporter() )
+                .isNotNull()
+                .isSameAs( testsetInfoReporter );
+    }
+
+    @Test
+    public void testForkMode()
+    {
+        AbstractSurefireMojo surefirePlugin = new Mojo( null, null );
+        setInternalState( surefirePlugin, "toolchain", new MyToolChain() );
+        setInternalState( surefirePlugin, "forkMode", "never" );
+        assertThat( surefirePlugin.getEffectiveForkMode() )
+                .isEqualTo( "once" );
+    }
+
+    @Test
+    public void testForkCountComputation()
+    {
+        AbstractSurefireMojo surefirePlugin = new Mojo( null, null );
+        assertConversionFails( surefirePlugin, "nothing" );
+
+        assertConversionFails( surefirePlugin, "5,0" );
+        assertConversionFails( surefirePlugin, "5.0" );
+        assertConversionFails( surefirePlugin, "5,0C" );
+        assertConversionFails( surefirePlugin, "5.0CC" );
+
+        assertForkCount( surefirePlugin, 5, "5" );
+
+        int availableProcessors = Runtime.getRuntime().availableProcessors();
+        assertForkCount( surefirePlugin, 3 * availableProcessors, "3C" );
+        assertForkCount( surefirePlugin, (int) ( 2.5 * availableProcessors ), "2.5C" );
+        assertForkCount( surefirePlugin, availableProcessors, "1.0001 C" );
+        assertForkCount( surefirePlugin, 1, 1d / ( (double) availableProcessors + 1 ) + "C" );
+        assertForkCount( surefirePlugin, 0, "0 C" );
+    }
+
+    private static void assertForkCount( AbstractSurefireMojo surefirePlugin, int expected, String value )
+    {
+        assertThat( surefirePlugin.convertWithCoreCount( value ) )
+                .isEqualTo( expected );
+    }
+
+    private static void assertConversionFails( AbstractSurefireMojo surefirePlugin, String value )
+    {
+        try
+        {
+            surefirePlugin.convertWithCoreCount( value );
+        }
+        catch ( NumberFormatException e )
+        {
+            return;
+        }
+        fail( "Expected NumberFormatException when converting " + value );
+    }
+
+    private static class MyToolChain implements Toolchain
+    {
+        @Override
+        public String getType()
+        {
+            return null;
+        }
+
+        @Override
+        public String findTool( String s )
+        {
+            return null;
+        }
+    }
+
+    @Test
     public void scanDependenciesShouldReturnNull()
             throws MojoFailureException
     {
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireDependencyResolverTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireDependencyResolverTest.java
index fdb1bbe..7088f77 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireDependencyResolverTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireDependencyResolverTest.java
@@ -359,7 +359,7 @@ public class SurefireDependencyResolverTest
         pluginArtifactsMapping.put( provider.getGroupId() + ":" + provider.getArtifactId(), provider );
         pluginArtifactsMapping.put( ext.getGroupId() + ":" + ext.getArtifactId(), ext );
 
-        Set<Artifact> cp = surefireDependencyResolver.addProviderToClasspath( pluginArtifactsMapping, plugin, common, api, logger );
+        Set<Artifact> cp = surefireDependencyResolver.addProviderToClasspath( pluginArtifactsMapping, plugin, api, logger );
         assertThat( cp )
                 .hasSize( 4 )
                 .containsOnly( provider, api, logger, ext );
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireHelperTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireHelperTest.java
index e5f8eb8..9541d3a 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireHelperTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireHelperTest.java
@@ -19,6 +19,9 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.surefire.AbstractSurefireMojoTest.Mojo;
+import org.apache.maven.surefire.suite.RunResult;
 import org.junit.Test;
 
 import java.io.File;
@@ -29,7 +32,9 @@ import static java.util.Collections.addAll;
 import static java.util.Collections.singleton;
 import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
 import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
+import static org.apache.maven.plugin.surefire.SurefireHelper.reportExecution;
 import static org.fest.assertions.Assertions.assertThat;
+import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
 /**
@@ -108,4 +113,46 @@ public class SurefireHelperTest
         escaped = escapeToPlatformPath( path );
         assertThat( escaped ).isEqualTo( root + "\\" + pathToJar );
     }
+
+    @Test
+    public void shouldHandleFailIfNoTests() throws Exception
+    {
+        RunResult summary = new RunResult( 0, 0, 0, 0 );
+        try
+        {
+            Mojo plugin = new Mojo();
+            plugin.setFailIfNoTests( true );
+            reportExecution( plugin, summary, null, null );
+        }
+        catch ( MojoFailureException e )
+        {
+            assertThat( e.getLocalizedMessage() )
+                    .isEqualTo( "No tests were executed!  (Set -DfailIfNoTests=false to ignore this error.)" );
+            return;
+        }
+        fail( "Expected MojoFailureException with message "
+                + "'No tests were executed!  (Set -DfailIfNoTests=false to ignore this error.)'" );
+    }
+
+    @Test
+    public void shouldHandleTestFailure() throws Exception
+    {
+        RunResult summary = new RunResult( 1, 0, 1, 0 );
+        try
+        {
+            reportExecution( new Mojo(), summary, null, null );
+        }
+        catch ( MojoFailureException e )
+        {
+            assertThat( e.getLocalizedMessage() )
+                    .isEqualTo( "There are test failures.\n\nPlease refer to null "
+                            + "for the individual test results.\nPlease refer to dump files (if any exist) "
+                            + "[date].dump, [date]-jvmRun[N].dump and [date].dumpstream." );
+            return;
+        }
+        fail( "Expected MojoFailureException with message "
+                + "'There are test failures.\n\nPlease refer to null "
+                + "for the individual test results.\nPlease refer to dump files (if any exist) "
+                + "[date].dump, [date]-jvmRun[N].dump and [date].dumpstream.'");
+    }
 }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java
index a7c8b9d..2d5cfee 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java
@@ -1 +1 @@
-package org.apache.maven.plugin.surefire;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerDecorator;
import o
 rg.apache.maven.plugin.surefire.log.api.PrintStreamLogger;
import org.apache.maven.surefire.booter.IsolatedClassLoader;
import org.apache.maven.surefire.booter.SurefireReflector;
import org.junit.Before;
import org.junit.Test;

import static org.apache.maven.surefire.util.ReflectionUtils.getMethod;
import static org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

/**
 * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
 * @see ConsoleLogger
 * @see SurefireReflector
 * @since 2.20
 */
public class SurefireReflectorTest
{
  
   private ConsoleLogger logger;
    private SurefireReflector reflector;

    @Before
    public void prepareData()
    {
        logger = spy( new PrintStreamLogger( System.out ) );
        ClassLoader cl = new IsolatedClassLoader( Thread.currentThread().getContextClassLoader(), false, "role" );
        reflector = new SurefireReflector( cl );
    }

    @Test
    public void shouldProxyConsoleLogger()
    {
        Object mirror = reflector.createConsoleLogger( logger );
        assertThat( mirror, is( notNullValue() ) );
        assertThat( mirror.getClass().getInterfaces()[0].getName(), is( ConsoleLogger.class.getName() ) );
        assertThat( mirror, is( not( sameInstance( (Object) logger ) ) ) );
        assertThat( mirror, is( instanceOf( ConsoleLoggerDecorator.class ) ) );
        invokeMethodWithArray( mirror, getMethod( mirror, "info", String.class ), "Hi There!" );
        verify( logger, times( 1 ) ).info( "Hi There!" );
    }
}
\ No newline at end of file
+package org.apache.maven.plugin.surefire;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerDecorator;
import o
 rg.apache.maven.plugin.surefire.log.api.PrintStreamLogger;
import org.apache.maven.surefire.booter.IsolatedClassLoader;
import org.apache.maven.surefire.booter.SurefireReflector;
import org.junit.Before;
import org.junit.Test;

import static org.apache.maven.surefire.util.ReflectionUtils.getMethod;
import static org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

/**
 * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
 * @see ConsoleLogger
 * @see SurefireReflector
 * @since 2.20
 */
public class SurefireReflectorTest
{
  
   private ConsoleLogger logger;
    private ClassLoader cl;

    @Before
    public void prepareData()
    {
        logger = spy( new PrintStreamLogger( System.out ) );
        cl = new IsolatedClassLoader( Thread.currentThread().getContextClassLoader(), false, "role" );
    }

    @Test
    public void shouldProxyConsoleLogger()
    {
        Object mirror = SurefireReflector.createConsoleLogger( logger, cl );
        assertThat( mirror, is( notNullValue() ) );
        assertThat( mirror.getClass().getInterfaces()[0].getName(), is( ConsoleLogger.class.getName() ) );
        assertThat( mirror, is( not( sameInstance( (Object) logger ) ) ) );
        assertThat( mirror, is( instanceOf( ConsoleLoggerDecorator.class ) ) );
        invokeMethodWithArray( mirror, getMethod( mirror, "info", String.class ), "Hi There!" );
        verify( logger, times( 1 ) ).info( "Hi There!" );
    }
}
\ No newline at end of file
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java
index 6bedab9..a8f6c69 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java
@@ -31,6 +31,7 @@ import org.apache.maven.surefire.booter.SurefireBooterForkException;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
@@ -58,6 +59,7 @@ import static org.powermock.reflect.Whitebox.invokeMethod;
  */
 @RunWith( PowerMockRunner.class )
 @PrepareForTest( { DefaultForkConfiguration.class, Relocator.class } )
+@PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
 public class DefaultForkConfigurationTest
 {
     private Classpath booterClasspath;
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 6b693bd..ab84690 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
@@ -268,7 +268,7 @@ public class ForkingRunListenerTest
 
     private SimpleReportEntry createDefaultReportEntry( Map<String, String> sysProps )
     {
-        return new SimpleReportEntry( "com.abc.TestClass", "testMethod", null, 22, sysProps );
+        return new SimpleReportEntry( "com.abc.TestClass", null, "testMethod", null, null, 22, sysProps );
     }
 
     private SimpleReportEntry createDefaultReportEntry()
@@ -278,7 +278,7 @@ public class ForkingRunListenerTest
 
     private SimpleReportEntry createAnotherDefaultReportEntry()
     {
-        return new SimpleReportEntry( "com.abc.AnotherTestClass", "testAnotherMethod", 42 );
+        return new SimpleReportEntry( "com.abc.AnotherTestClass", null, "testAnotherMethod", null, 42 );
     }
 
     private SimpleReportEntry createReportEntryWithStackTrace()
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfigurationTest.java
index 08b3030..8f8cd8f 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfigurationTest.java
@@ -53,6 +53,7 @@ import static org.powermock.api.mockito.PowerMockito.when;
 
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
@@ -61,6 +62,7 @@ import org.powermock.modules.junit4.PowerMockRunner;
  */
 @RunWith( PowerMockRunner.class )
 @PrepareForTest( { JarManifestForkConfiguration.class, InPluginProcessDumpSingleton.class } )
+@PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
 public class JarManifestForkConfigurationTest
 {
     private static final File TMP = newTemporaryFolder();
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 0ab24b9..3770fa0 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
@@ -20,6 +20,9 @@ package org.apache.maven.plugin.surefire.booterclient;
  */
 
 import org.apache.maven.plugin.surefire.StartupReportConfiguration;
+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.report.DefaultReporterFactory;
 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
 import org.apache.maven.surefire.report.RunListener;
@@ -55,7 +58,8 @@ public class TestSetMockReporterFactory
     {
         File target = new File( "./target" );
         File statisticsFile = new File( target, "TESTHASH" );
-        return new StartupReportConfiguration( true, true, "PLAIN", false, false, target, false, null, statisticsFile,
-                false, 0, null, null, true );
+        return new StartupReportConfiguration( true, true, "PLAIN", false, target, false, null, statisticsFile,
+                false, 0, null, null, true, new SurefireStatelessReporter(), new SurefireConsoleOutputReporter(),
+                new SurefireStatelessTestsetInfoReporter() );
     }
 }
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 e4107da..a2405c5 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
@@ -907,8 +907,12 @@ public class ForkClientTest
         client.consumeMultiLineContent( ":maven:surefire:std:out:testset-starting:normal-run:UTF-8:"
                 + encodedSourceName
                 + ":"
+                + "-"
+                + ":"
                 + encodedName
                 + ":"
+                + "-"
+                + ":"
                 + encodedGroup
                 + ":"
                 + encodedMessage
@@ -939,8 +943,12 @@ public class ForkClientTest
                 .hasSize( 1 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getName() )
                 .isEqualTo( "my test" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getNameText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getElapsed() )
                 .isEqualTo( 102 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getMessage() )
@@ -1017,12 +1025,16 @@ public class ForkClientTest
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
         when( reportEntry.getName() ).thenReturn( "my test" );
+        when( reportEntry.getNameText() ).thenReturn( "dn2" );
         when( reportEntry.getNameWithGroup() ).thenReturn( "name with group" );
         when( reportEntry.getSourceName() ).thenReturn( "pkg.MyTest" );
+        when( reportEntry.getSourceText() ).thenReturn( "dn1" );
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         String encodedSourceName = encodeBase64String( toArray( UTF_8.encode( reportEntry.getSourceName() ) ) );
+        String encodedSourceText = encodeBase64String( toArray( UTF_8.encode( reportEntry.getSourceText() ) ) );
         String encodedName = encodeBase64String( toArray( UTF_8.encode( reportEntry.getName() ) ) );
+        String encodedNameText = encodeBase64String( toArray( UTF_8.encode( reportEntry.getNameText() ) ) );
         String encodedGroup = encodeBase64String( toArray( UTF_8.encode( reportEntry.getGroup() ) ) );
         String encodedMessage = encodeBase64String( toArray( UTF_8.encode( reportEntry.getMessage() ) ) );
 
@@ -1030,8 +1042,12 @@ public class ForkClientTest
         client.consumeMultiLineContent( ":maven:surefire:std:out:testset-starting:normal-run:UTF-8:"
                 + encodedSourceName
                 + ":"
+                + encodedSourceText
+                + ":"
                 + encodedName
                 + ":"
+                + encodedNameText
+                + ":"
                 + encodedGroup
                 + ":"
                 + encodedMessage
@@ -1060,8 +1076,12 @@ public class ForkClientTest
                 .hasSize( 1 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceText() )
+                .isEqualTo( "dn1" );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getName() )
                 .isEqualTo( "my test" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getNameText() )
+                .isEqualTo( "dn2" );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getElapsed() )
                 .isEqualTo( 102 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getMessage() )
@@ -1153,8 +1173,12 @@ public class ForkClientTest
         client.consumeMultiLineContent( ":maven:surefire:std:out:testset-completed:normal-run:UTF-8:"
                 + encodedSourceName
                 + ":"
+                + "-"
+                + ":"
                 + encodedName
                 + ":"
+                + "-"
+                + ":"
                 + encodedGroup
                 + ":"
                 + encodedMessage
@@ -1181,8 +1205,12 @@ public class ForkClientTest
                 .hasSize( 1 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getName() )
                 .isEqualTo( "my test" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getNameText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getElapsed() )
                 .isEqualTo( 102 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getMessage() )
@@ -1274,8 +1302,12 @@ public class ForkClientTest
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-starting:normal-run:UTF-8:"
                 + encodedSourceName
                 + ":"
+                + "-"
+                + ":"
                 + encodedName
                 + ":"
+                + "-"
+                + ":"
                 + encodedGroup
                 + ":"
                 + encodedMessage
@@ -1307,8 +1339,12 @@ public class ForkClientTest
                 .hasSize( 1 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getName() )
                 .isEqualTo( "my test" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getNameText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getElapsed() )
                 .isEqualTo( 102 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getMessage() )
@@ -1396,7 +1432,7 @@ public class ForkClientTest
 
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-starting:normal-run:UTF-8:"
                 + encodedSourceName
-                + ":-:-:-:-:-:-:-" );
+                + ":-:-:-:-:-:-:-:-:-" );
 
         assertThat( client.testsInProgress() )
                 .hasSize( 1 )
@@ -1405,8 +1441,12 @@ public class ForkClientTest
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-succeeded:normal-run:UTF-8:"
                 + encodedSourceName
                 + ":"
+                + "-"
+                + ":"
                 + encodedName
                 + ":"
+                + "-"
+                + ":"
                 + encodedGroup
                 + ":"
                 + encodedMessage
@@ -1433,8 +1473,12 @@ public class ForkClientTest
                 .hasSize( 2 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getName() )
                 .isNull();
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getNameText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getName() )
@@ -1530,7 +1574,7 @@ public class ForkClientTest
 
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-starting:normal-run:UTF-8:"
                 + encodedSourceName
-                + ":-:-:-:-:-:-:-" );
+                + ":-:-:-:-:-:-:-:-:-" );
 
         assertThat( client.testsInProgress() )
                 .hasSize( 1 )
@@ -1539,8 +1583,12 @@ public class ForkClientTest
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-failed:normal-run:UTF-8:"
                 + encodedSourceName
                 + ":"
+                + "-"
+                + ":"
                 + encodedName
                 + ":"
+                + "-"
+                + ":"
                 + encodedGroup
                 + ":"
                 + encodedMessage
@@ -1567,12 +1615,20 @@ public class ForkClientTest
                 .hasSize( 2 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getName() )
                 .isNull();
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getNameText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getSourceText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getName() )
                 .isEqualTo( "my test" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getNameText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getElapsed() )
                 .isEqualTo( 102 );
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getMessage() )
@@ -1664,7 +1720,7 @@ public class ForkClientTest
 
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-starting:normal-run:UTF-8:"
                 + encodedSourceName
-                + ":-:-:-:-:-:-:-" );
+                + ":-:-:-:-:-:-:-:-:-" );
 
         assertThat( client.testsInProgress() )
                 .hasSize( 1 )
@@ -1673,8 +1729,12 @@ public class ForkClientTest
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-skipped:normal-run:UTF-8:"
                 + encodedSourceName
                 + ":"
+                + "-"
+                + ":"
                 + encodedName
                 + ":"
+                + "-"
+                + ":"
                 + encodedGroup
                 + ":"
                 + encodedMessage
@@ -1701,12 +1761,20 @@ public class ForkClientTest
                 .hasSize( 2 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getName() )
                 .isNull();
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getNameText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getSourceText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getName() )
                 .isEqualTo( "my test" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getNameText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getElapsed() )
                 .isEqualTo( 102 );
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getMessage() )
@@ -1787,9 +1855,11 @@ public class ForkClientTest
         when( reportEntry.getName() ).thenReturn( "my test" );
         when( reportEntry.getNameWithGroup() ).thenReturn( "name with group" );
         when( reportEntry.getSourceName() ).thenReturn( "pkg.MyTest" );
+        when( reportEntry.getSourceText() ).thenReturn( "display name" );
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         String encodedSourceName = encodeBase64String( toArray( UTF_8.encode( reportEntry.getSourceName() ) ) );
+        String encodedSourceText = encodeBase64String( toArray( UTF_8.encode( reportEntry.getSourceText() ) ) );
         String encodedName = encodeBase64String( toArray( UTF_8.encode( reportEntry.getName() ) ) );
         String encodedGroup = encodeBase64String( toArray( UTF_8.encode( reportEntry.getGroup() ) ) );
         String encodedMessage = encodeBase64String( toArray( UTF_8.encode( reportEntry.getMessage() ) ) );
@@ -1798,7 +1868,9 @@ public class ForkClientTest
 
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-starting:normal-run:UTF-8:"
                 + encodedSourceName
-                + ":-:-:-:-:-:-:-" );
+                + ":"
+                + encodedSourceText
+                + ":-:':-:-:-:-:-:-" );
 
         assertThat( client.testsInProgress() )
                 .hasSize( 1 )
@@ -1807,8 +1879,12 @@ public class ForkClientTest
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-error:normal-run:UTF-8:"
                 + encodedSourceName
                 + ":"
+                + encodedSourceText
+                + ":"
                 + encodedName
                 + ":"
+                + "-"
+                + ":"
                 + encodedGroup
                 + ":"
                 + encodedMessage
@@ -1835,10 +1911,14 @@ public class ForkClientTest
                 .hasSize( 2 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceText() )
+                .isEqualTo( "display name" );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getName() )
                 .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getSourceText() )
+                .isEqualTo( "display name" );
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getName() )
                 .isEqualTo( "my test" );
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getElapsed() )
@@ -1919,12 +1999,14 @@ public class ForkClientTest
         when( reportEntry.getGroup() ).thenReturn( "this group" );
         when( reportEntry.getMessage() ).thenReturn( "some test" );
         when( reportEntry.getName() ).thenReturn( "my test" );
+        when( reportEntry.getNameText() ).thenReturn("display name");
         when( reportEntry.getNameWithGroup() ).thenReturn( "name with group" );
         when( reportEntry.getSourceName() ).thenReturn( "pkg.MyTest" );
         when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
         String encodedSourceName = encodeBase64String( toArray( UTF_8.encode( reportEntry.getSourceName() ) ) );
         String encodedName = encodeBase64String( toArray( UTF_8.encode( reportEntry.getName() ) ) );
+        String encodedText = encodeBase64String( toArray( UTF_8.encode( reportEntry.getNameText() ) ) );
         String encodedGroup = encodeBase64String( toArray( UTF_8.encode( reportEntry.getGroup() ) ) );
         String encodedMessage = encodeBase64String( toArray( UTF_8.encode( reportEntry.getMessage() ) ) );
 
@@ -1932,7 +2014,7 @@ public class ForkClientTest
 
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-starting:normal-run:UTF-8:"
                 + encodedSourceName
-                + ":-:-:-:-:-:-:-" );
+                + ":-:-:-:-:-:-:-:-:-" );
 
         assertThat( client.testsInProgress() )
                 .hasSize( 1 )
@@ -1941,8 +2023,12 @@ public class ForkClientTest
         client.consumeMultiLineContent( ":maven:surefire:std:out:test-assumption-failure:normal-run:UTF-8:"
                 + encodedSourceName
                 + ":"
+                + "-"
+                + ":"
                 + encodedName
                 + ":"
+                + encodedText
+                + ":"
                 + encodedGroup
                 + ":"
                 + encodedMessage
@@ -1969,12 +2055,16 @@ public class ForkClientTest
                 .hasSize( 2 );
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getSourceText() )
+                .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 0 ) ).getName() )
                 .isNull();
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getSourceName() )
                 .isEqualTo( "pkg.MyTest" );
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getName() )
                 .isEqualTo( "my test" );
+        assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getNameText() )
+                .isEqualTo( "display name" );
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getElapsed() )
                 .isEqualTo( 102 );
         assertThat( ( (ReportEntry) receiver.getData().get( 1 ) ).getMessage() )
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoderTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoderTest.java
index 894ec2d..53c3562 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoderTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoderTest.java
@@ -107,22 +107,24 @@ public class ForkedChannelDecoderTest
         @Test
         public void shouldRecognizeEmptyStream4ReportEntry()
         {
-            ReportEntry reportEntry = toReportEntry( null, null, "", null, null, "",
+            ReportEntry reportEntry = toReportEntry( null, null, null, "", "", null, null, "",
                     "", "", null );
             assertThat( reportEntry ).isNull();
 
-            reportEntry = toReportEntry( UTF_8, "", "", "", "", "-", "", "", "" );
+            reportEntry = toReportEntry( UTF_8, "", "", "", "", "", "", "-", "", "", "" );
             assertThat( reportEntry ).isNotNull();
             assertThat( reportEntry.getStackTraceWriter() ).isNull();
             assertThat( reportEntry.getSourceName() ).isEmpty();
+            assertThat( reportEntry.getSourceText() ).isEmpty();
             assertThat( reportEntry.getName() ).isEmpty();
+            assertThat( reportEntry.getNameText() ).isEmpty();
             assertThat( reportEntry.getGroup() ).isEmpty();
             assertThat( reportEntry.getNameWithGroup() ).isEmpty();
             assertThat( reportEntry.getMessage() ).isEmpty();
             assertThat( reportEntry.getElapsed() ).isNull();
 
             rule.expect( NumberFormatException.class );
-            toReportEntry( UTF_8, "", "", "", "", "", "", "", "" );
+            toReportEntry( UTF_8, "", "", "", "", "", "", "", "", "", "" );
             fail();
         }
 
@@ -153,58 +155,66 @@ public class ForkedChannelDecoderTest
             when( reportEntry.getGroup() ).thenReturn( "this group" );
             when( reportEntry.getMessage() ).thenReturn( "skipped test" );
             when( reportEntry.getName() ).thenReturn( "my test" );
+            when( reportEntry.getNameText() ).thenReturn( "my display name" );
             when( reportEntry.getNameWithGroup() ).thenReturn( "name with group" );
             when( reportEntry.getSourceName() ).thenReturn( "pkg.MyTest" );
+            when( reportEntry.getSourceText() ).thenReturn( "test class display name" );
             when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
             String encodedSourceName = encodeBase64String( toArray( UTF_8.encode( reportEntry.getSourceName() ) ) );
+            String encodedSourceText = encodeBase64String( toArray( UTF_8.encode( reportEntry.getSourceText() ) ) );
             String encodedName = encodeBase64String( toArray( UTF_8.encode( reportEntry.getName() ) ) );
+            String encodedText = encodeBase64String( toArray( UTF_8.encode( reportEntry.getNameText() ) ) );
             String encodedGroup = encodeBase64String( toArray( UTF_8.encode( reportEntry.getGroup() ) ) );
             String encodedMessage = encodeBase64String( toArray( UTF_8.encode( reportEntry.getMessage() ) ) );
 
-            ReportEntry decodedReportEntry = toReportEntry( UTF_8, encodedSourceName, encodedName, encodedGroup,
-                                                                  encodedMessage, "-", null, null, null
-            );
+            ReportEntry decodedReportEntry = toReportEntry( UTF_8, encodedSourceName, encodedSourceText,
+                    encodedName, encodedText, encodedGroup, encodedMessage, "-", null, null, null );
 
             assertThat( decodedReportEntry ).isNotNull();
             assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
+            assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
             assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
+            assertThat( decodedReportEntry.getNameText() ).isEqualTo(reportEntry.getNameText());
             assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
             assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
             assertThat( decodedReportEntry.getStackTraceWriter() ).isNull();
 
-            decodedReportEntry = toReportEntry( UTF_8, encodedSourceName, encodedName, encodedGroup, encodedMessage,
-                    "-", encodedExceptionMsg, encodedSmartStackTrace, null
-            );
+            decodedReportEntry = toReportEntry( UTF_8, encodedSourceName, encodedSourceText, encodedName, encodedText,
+                    encodedGroup, encodedMessage, "-", encodedExceptionMsg, encodedSmartStackTrace, null );
 
             assertThat( decodedReportEntry ).isNotNull();
             assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
+            assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
             assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
+            assertThat( decodedReportEntry.getNameText() ).isEqualTo(reportEntry.getNameText());
             assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
             assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
             assertThat( decodedReportEntry.getElapsed() ).isNull();
             assertThat( decodedReportEntry.getStackTraceWriter() ).isNull();
 
-            decodedReportEntry = toReportEntry( UTF_8, encodedSourceName, encodedName, encodedGroup, encodedMessage,
-                                                      "1003", encodedExceptionMsg, encodedSmartStackTrace, null
-            );
+            decodedReportEntry = toReportEntry( UTF_8, encodedSourceName, encodedSourceText, encodedName, encodedText,
+                    encodedGroup, encodedMessage, "1003", encodedExceptionMsg, encodedSmartStackTrace, null );
 
             assertThat( decodedReportEntry ).isNotNull();
             assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
+            assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
             assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
+            assertThat( decodedReportEntry.getNameText() ).isEqualTo(reportEntry.getNameText());
             assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
             assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
             assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
             assertThat( decodedReportEntry.getStackTraceWriter() ).isNull();
 
-            decodedReportEntry = toReportEntry( UTF_8, encodedSourceName, encodedName, encodedGroup, encodedMessage,
-                                                      "1003", encodedExceptionMsg, encodedSmartStackTrace,
-                                                      encodedStackTrace
-            );
+            decodedReportEntry = toReportEntry( UTF_8, encodedSourceName, encodedSourceText, encodedName, encodedText,
+                    encodedGroup, encodedMessage, "1003", encodedExceptionMsg, encodedSmartStackTrace,
+                    encodedStackTrace );
 
             assertThat( decodedReportEntry ).isNotNull();
             assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
+            assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
             assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
+            assertThat( decodedReportEntry.getNameText() ).isEqualTo(reportEntry.getNameText());
             assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
             assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
             assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
@@ -217,14 +227,15 @@ public class ForkedChannelDecoderTest
             assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() ).isEqualTo( stackTrace );
             assertThat( decodedReportEntry.getStackTraceWriter().writeTrimmedTraceToString() ).isEqualTo( stackTrace );
 
-            decodedReportEntry = toReportEntry( UTF_8, encodedSourceName, encodedName, encodedGroup, encodedMessage,
-                                                      "1003", encodedExceptionMsg, encodedSmartStackTrace,
-                                                      encodedTrimmedStackTrace
-            );
+            decodedReportEntry = toReportEntry( UTF_8, encodedSourceName, encodedSourceText, encodedName, encodedText,
+                    encodedGroup, encodedMessage, "1003", encodedExceptionMsg, encodedSmartStackTrace,
+                    encodedTrimmedStackTrace );
 
             assertThat( decodedReportEntry ).isNotNull();
             assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
+            assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
             assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
+            assertThat( decodedReportEntry.getNameText() ).isEqualTo(reportEntry.getNameText());
             assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
             assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
             assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
@@ -643,8 +654,10 @@ public class ForkedChannelDecoderTest
             when( reportEntry.getGroup() ).thenReturn( "this group" );
             when( reportEntry.getMessage() ).thenReturn( reportedMessage );
             when( reportEntry.getName() ).thenReturn( "my test" );
+            when( reportEntry.getName() ).thenReturn( "display name of test" );
             when( reportEntry.getNameWithGroup() ).thenReturn( "name with group" );
             when( reportEntry.getSourceName() ).thenReturn( "pkg.MyTest" );
+            when( reportEntry.getSourceText() ).thenReturn("test class display name");
             when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
 
             Stream out = Stream.newStream();
@@ -777,7 +790,9 @@ public class ForkedChannelDecoderTest
         public void handle( RunMode runMode, ReportEntry reportEntry )
         {
             assertThat( reportEntry.getSourceName() ).isEqualTo( this.reportEntry.getSourceName() );
+            assertThat( reportEntry.getSourceText() ).isEqualTo( this.reportEntry.getSourceText() );
             assertThat( reportEntry.getName() ).isEqualTo( this.reportEntry.getName() );
+            assertThat( reportEntry.getNameText() ).isEqualTo( this.reportEntry.getNameText() );
             assertThat( reportEntry.getGroup() ).isEqualTo( this.reportEntry.getGroup() );
             assertThat( reportEntry.getMessage() ).isEqualTo( this.reportEntry.getMessage() );
             assertThat( reportEntry.getElapsed() ).isEqualTo( this.reportEntry.getElapsed() );
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 55c3a64..4df9355 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
@@ -22,19 +22,26 @@ package org.apache.maven.plugin.surefire.report;
 import java.io.File;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Queue;
 
 import junit.framework.TestCase;
 
 import org.apache.maven.plugin.surefire.StartupReportConfiguration;
+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.shared.utils.logging.MessageUtils;
 import org.apache.maven.surefire.report.RunStatistics;
 import org.apache.maven.surefire.report.SafeThrowable;
 import org.apache.maven.surefire.report.StackTraceWriter;
+import org.apache.maven.surefire.suite.RunResult;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.error;
 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.failure;
 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.flake;
@@ -44,6 +51,8 @@ import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.Tes
 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.getTestResultType;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.powermock.reflect.Whitebox.getInternalState;
+import static org.powermock.reflect.Whitebox.invokeMethod;
 
 public class DefaultReporterFactoryTest
     extends TestCase
@@ -62,13 +71,16 @@ public class DefaultReporterFactoryTest
 
     private final static String ERROR = "error";
 
-    public void testMergeTestHistoryResult()
+    public void testMergeTestHistoryResult() throws Exception
     {
         MessageUtils.setColorEnabled( false );
-        File reportsDirectory = new File("target");
+        File target = new File( System.getProperty( "user.dir" ), "target" );
+        File reportsDirectory = new File( target, "tmp5" );
         StartupReportConfiguration reportConfig =
-                new StartupReportConfiguration( true, true, "PLAIN", false, false, reportsDirectory, false, null,
-                        new File( reportsDirectory, "TESTHASH" ), false, 1, null, null, false );
+                new StartupReportConfiguration( true, true, "PLAIN", false, reportsDirectory, false, null,
+                        new File( reportsDirectory, "TESTHASH" ), false, 1, null, null, false,
+                        new SurefireStatelessReporter(), new SurefireConsoleOutputReporter(),
+                        new SurefireStatelessTestsetInfoReporter() );
 
         DummyTestReporter reporter = new DummyTestReporter();
 
@@ -111,7 +123,7 @@ public class DefaultReporterFactoryTest
         factory.addListener( secondRunListener );
         factory.addListener( thirdRunListener );
 
-        factory.mergeTestHistoryResult();
+        invokeMethod( factory, "mergeTestHistoryResult" );
         RunStatistics mergedStatistics = factory.getGlobalRunStatistics();
 
         // Only TEST_THREE is a failing test, other three are flaky tests
@@ -132,14 +144,12 @@ public class DefaultReporterFactoryTest
         reporter.reset();
         factory.printTestFailures( error );
         String[] expectedFailureOutput =
-            { "Errors: ", TEST_THREE, "  Run 1: " + ASSERTION_FAIL, "  Run 2: " + ERROR, "  Run 3: " + ERROR, ""
-            };
+            { "Errors: ", TEST_THREE, "  Run 1: " + ASSERTION_FAIL, "  Run 2: " + ERROR, "  Run 3: " + ERROR, "" };
         assertEquals( asList( expectedFailureOutput ), reporter.getMessages() );
 
         reporter.reset();
         factory.printTestFailures( failure );
-        String[] expectedErrorOutput = { };
-        assertEquals( asList( expectedErrorOutput ), reporter.getMessages() );
+        assertEquals( emptyList(), reporter.getMessages() );
     }
 
     static final class DummyTestReporter implements ConsoleLogger
@@ -197,12 +207,13 @@ public class DefaultReporterFactoryTest
         @Override
         public void error( String message, Throwable t )
         {
-            messages.add( message );
+            messages.add( message + " " + t.getLocalizedMessage() );
         }
 
         @Override
         public void error( Throwable t )
         {
+            messages.add( t.getLocalizedMessage() );
         }
 
         List<String> getMessages()
@@ -256,6 +267,127 @@ public class DefaultReporterFactoryTest
         assertEquals( skipped, getTestResultType( skippedList, 1 ) );
     }
 
+    public void testLogger()
+    {
+        MessageUtils.setColorEnabled( false );
+        File target = new File( System.getProperty( "user.dir" ), "target" );
+        File reportsDirectory = new File( target, "tmp6" );
+        StartupReportConfiguration reportConfig =
+                new StartupReportConfiguration( true, true, "PLAIN", false, reportsDirectory, false, null,
+                        new File( reportsDirectory, "TESTHASH" ), false, 1, null, null, false,
+                        new SurefireStatelessReporter(), new SurefireConsoleOutputReporter(),
+                        new SurefireStatelessTestsetInfoReporter() );
+
+        DummyTestReporter reporter = new DummyTestReporter();
+
+        DefaultReporterFactory factory = new DefaultReporterFactory( reportConfig, reporter );
+
+        TestSetRunListener runListener = (TestSetRunListener) factory.createReporter();
+
+        assertTrue( runListener.isDebugEnabled() );
+        assertTrue( runListener.isInfoEnabled() );
+        assertTrue( runListener.isWarnEnabled() );
+        assertTrue( runListener.isErrorEnabled() );
+
+        runListener.debug( "msg" );
+        assertEquals( 1, reporter.getMessages().size() );
+        assertEquals( "msg", reporter.getMessages().get( 0 ) );
+        reporter.reset();
+
+        runListener.info( "msg\n" );
+        assertEquals( 1, reporter.getMessages().size() );
+        assertEquals( "msg", reporter.getMessages().get( 0 ) );
+        reporter.reset();
+
+        runListener.warning( "msg\r\n" );
+        assertEquals( 1, reporter.getMessages().size() );
+        assertEquals( "msg", reporter.getMessages().get( 0 ) );
+        reporter.reset();
+
+        runListener.error( "msg" );
+        assertEquals( 1, reporter.getMessages().size() );
+        assertEquals( "msg", reporter.getMessages().get( 0 ) );
+        reporter.reset();
+
+        runListener.error( "msg\n", new Exception( "e" ) );
+        assertEquals( 1, reporter.getMessages().size() );
+        assertEquals( "msg e", reporter.getMessages().get( 0 ) );
+        reporter.reset();
+
+        runListener.error( new Exception( "e" ) );
+        assertEquals( 1, reporter.getMessages().size() );
+        assertEquals( "e", reporter.getMessages().get( 0 ) );
+        reporter.reset();
+    }
+
+    public void testCreateReporterWithZeroStatistics()
+    {
+        MessageUtils.setColorEnabled( false );
+        File target = new File( System.getProperty( "user.dir" ), "target" );
+        File reportsDirectory = new File( target, "tmp7" );
+        StartupReportConfiguration reportConfig =
+                new StartupReportConfiguration( true, true, "PLAIN", false, reportsDirectory, false, null,
+                        new File( reportsDirectory, "TESTHASH" ), false, 0, null, null, false,
+                        new SurefireStatelessReporter(), new SurefireConsoleOutputReporter(),
+                        new SurefireStatelessTestsetInfoReporter() );
+
+        assertTrue( reportConfig.isUseFile() );
+        assertTrue( reportConfig.isPrintSummary() );
+        assertEquals( "PLAIN", reportConfig.getReportFormat() );
+        assertFalse( reportConfig.isRedirectTestOutputToFile() );
+        assertEquals( reportsDirectory, reportConfig.getReportsDirectory() );
+        assertFalse( reportConfig.isTrimStackTrace() );
+        assertNull( reportConfig.getReportNameSuffix() );
+        assertEquals( new File( reportsDirectory, "TESTHASH" ), reportConfig.getStatisticsFile() );
+        assertFalse( reportConfig.isRequiresRunHistory() );
+        assertEquals( 0, reportConfig.getRerunFailingTestsCount() );
+        assertNull( reportConfig.getXsdSchemaLocation() );
+        assertEquals( UTF_8, reportConfig.getEncoding() );
+        assertFalse( reportConfig.isForkMode() );
+        assertNotNull( reportConfig.getXmlReporter() );
+        assertNotNull( reportConfig.getConsoleOutputReporter() );
+        assertNotNull( reportConfig.getTestsetReporter() );
+        assertNull( reportConfig.getStatisticsReporter() );
+
+        DummyTestReporter reporter = new DummyTestReporter();
+
+        DefaultReporterFactory factory = new DefaultReporterFactory( reportConfig, reporter );
+        assertEquals( reportsDirectory, factory.getReportsDirectory() );
+
+        TestSetRunListener runListener = (TestSetRunListener) factory.createReporter();
+        Collection listeners = getInternalState( factory, "listeners" );
+        assertEquals( 1, listeners.size() );
+        assertTrue( listeners.contains( runListener ) );
+
+        assertNotNull( runListener.getTestMethodStats() );
+
+        factory.runStarting();
+
+        factory.close();
+
+        RunStatistics statistics = factory.getGlobalRunStatistics();
+        assertEquals( 0, statistics.getCompletedCount() );
+        assertEquals( new RunResult( 0, 0, 0, 0 ), statistics.getRunResult() );
+        assertEquals( 0, statistics.getFailures() );
+        assertEquals( 0, statistics.getErrors() );
+        assertEquals( 0, statistics.getSkipped() );
+        assertEquals( 0, statistics.getFlakes() );
+        assertEquals( "Tests run: 0, Failures: 0, Errors: 0, Skipped: 0", statistics.getSummary() );
+        assertEquals( 0, statistics.getCompletedCount() );
+
+        List<String> messages = reporter.getMessages();
+        assertEquals( "", messages.get( 0 ) );
+        assertEquals( "-------------------------------------------------------", messages.get( 1 ) );
+        assertEquals( " T E S T S", messages.get( 2 ) );
+        assertEquals( "-------------------------------------------------------", messages.get( 3 ) );
+        assertEquals( "", messages.get( 4 ) );
+        assertEquals( "Results:", messages.get( 5 ) );
+        assertEquals( "", messages.get( 6 ) );
+        assertEquals( "Tests run: 0, Failures: 0, Errors: 0, Skipped: 0", messages.get( 7 ) );
+        assertEquals( "", messages.get( 8 ) );
+        assertEquals( 9, messages.size() );
+    }
+
     static class DummyStackTraceWriter
         implements StackTraceWriter
     {
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 bebaed3..5faf8e3 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
@@ -84,10 +84,11 @@ public class StatelessXmlReporterTest
     {
         StatelessXmlReporter reporter =
                 new StatelessXmlReporter( reportDir, null, false, 0,
-                        new ConcurrentHashMap<String, Deque<WrappedReportEntry>>(), XSD );
+                        new ConcurrentHashMap<String, Deque<WrappedReportEntry>>(), XSD, "3.0",
+                        false, false, false, false );
         reporter.cleanTestHistoryMap();
 
-        ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), getClass().getName(), 12 );
+        ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), null, getClass().getName(), null, 12 );
         WrappedReportEntry testSetReportEntry = new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS,
                 12, null, null, systemProps() );
         stats.testSucceeded( testSetReportEntry );
@@ -102,7 +103,7 @@ public class StatelessXmlReporterTest
     public void testAllFieldsSerialized()
             throws IOException
     {
-        ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), TEST_ONE, 12 );
+        ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), null, TEST_ONE, null, 12 );
         WrappedReportEntry testSetReportEntry =
                 new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null, systemProps() );
         expectedReportFile = new File( reportDir, "TEST-" + getClass().getName() + ".xml" );
@@ -129,12 +130,12 @@ public class StatelessXmlReporterTest
 
         stdErr.write( stdErrPrefix + "?&-&amp;&#163;\u0020\u0000\u001F", false );
         WrappedReportEntry t2 =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), TEST_TWO, stackTraceWriter, 13 ),
-                        ReportEntryType.ERROR, 13, stdOut, stdErr );
+                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_TWO, null,
+                        stackTraceWriter, 13 ), ReportEntryType.ERROR, 13, stdOut, stdErr );
 
         stats.testSucceeded( t2 );
         StatelessXmlReporter reporter = new StatelessXmlReporter( reportDir, null, false, 0,
-                new ConcurrentHashMap<String, Deque<WrappedReportEntry>>(), XSD );
+                new ConcurrentHashMap<String, Deque<WrappedReportEntry>>(), XSD, "3.0", false, false, false, false );
         reporter.testSetCompleted( testSetReportEntry, stats );
 
         FileInputStream fileInputStream = new FileInputStream( expectedReportFile );
@@ -171,7 +172,7 @@ public class StatelessXmlReporterTest
             throws IOException
     {
         WrappedReportEntry testSetReportEntry =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), TEST_ONE, 12 ),
+                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_ONE, null, 12 ),
                         ReportEntryType.SUCCESS, 12, null, null, systemProps() );
         expectedReportFile = new File( reportDir, "TEST-" + getClass().getName() + ".xml" );
 
@@ -187,23 +188,23 @@ public class StatelessXmlReporterTest
         String secondRunErr = "second run err";
 
         WrappedReportEntry testTwoFirstError =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), TEST_TWO, stackTraceWriterOne, 5 ),
-                        ReportEntryType.ERROR, 5, createStdOutput( firstRunOut ),
+                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_TWO, null,
+                        stackTraceWriterOne, 5 ), ReportEntryType.ERROR, 5, createStdOutput( firstRunOut ),
                         createStdOutput( firstRunErr ) );
 
         WrappedReportEntry testTwoSecondError =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), TEST_TWO, stackTraceWriterTwo, 13 ),
-                        ReportEntryType.ERROR, 13, createStdOutput( secondRunOut ),
+                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_TWO, null,
+                        stackTraceWriterTwo, 13 ), ReportEntryType.ERROR, 13, createStdOutput( secondRunOut ),
                         createStdOutput( secondRunErr ) );
 
         WrappedReportEntry testThreeFirstRun =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), TEST_THREE, stackTraceWriterOne, 13 ),
-                        ReportEntryType.FAILURE, 13, createStdOutput( firstRunOut ),
+                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_THREE, null,
+                        stackTraceWriterOne, 13 ), ReportEntryType.FAILURE, 13, createStdOutput( firstRunOut ),
                         createStdOutput( firstRunErr ) );
 
         WrappedReportEntry testThreeSecondRun =
-                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), TEST_THREE, stackTraceWriterTwo, 2 ),
-                        ReportEntryType.SUCCESS, 2, createStdOutput( secondRunOut ),
+                new WrappedReportEntry( new SimpleReportEntry( getClass().getName(), null, TEST_THREE, null,
+                        stackTraceWriterTwo, 2 ), ReportEntryType.SUCCESS, 2, createStdOutput( secondRunOut ),
                         createStdOutput( secondRunErr ) );
 
         stats.testSucceeded( testTwoFirstError );
@@ -213,7 +214,7 @@ public class StatelessXmlReporterTest
 
         StatelessXmlReporter reporter =
                 new StatelessXmlReporter( reportDir, null, false, 1,
-                        new HashMap<String, Deque<WrappedReportEntry>>(), XSD );
+                        new HashMap<String, Deque<WrappedReportEntry>>(), XSD, "3.0", false, false, false, false );
 
         reporter.testSetCompleted( testSetReportEntry, stats );
         reporter.testSetCompleted( testSetReportEntry, rerunStats );
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/TestSetStatsTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/TestSetStatsTest.java
new file mode 100644
index 0000000..7aac381
--- /dev/null
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/TestSetStatsTest.java
@@ -0,0 +1,71 @@
+package org.apache.maven.plugin.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.report.ReportEntry;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * tests for {@link TestSetStats}.
+ */
+@RunWith( PowerMockRunner.class )
+@PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
+public class TestSetStatsTest
+{
+    @Mock
+    private ReportEntry reportEntry;
+
+    @Test
+    public void shouldConcatenateWithTestGroup()
+    {
+        when( reportEntry.getNameWithGroup() )
+                .thenReturn( "pkg.MyTest (my group)" );
+        String actual = TestSetStats.concatenateWithTestGroup( buffer(), reportEntry, false );
+        verify( reportEntry, times( 1 ) ).getNameWithGroup();
+        verifyNoMoreInteractions( reportEntry );
+        String expected = buffer().a( "pkg." ).strong( "MyTest (my group)" ).toString();
+        assertThat( actual )
+                .isEqualTo( expected );
+    }
+
+    @Test
+    public void shouldConcatenateWithJUnit5TestGroup()
+    {
+        when( reportEntry.getReportNameWithGroup() )
+                .thenReturn( "pkg.MyTest (my group)" );
+        String actual = TestSetStats.concatenateWithTestGroup( buffer(), reportEntry, true );
+        verify( reportEntry, times( 1 ) ).getReportNameWithGroup();
+        verifyNoMoreInteractions( reportEntry );
+        String expected = buffer().strong( "pkg.MyTest (my group)" ).toString();
+        assertThat( actual )
+                .isEqualTo( expected );
+    }
+}
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 61080a1..abbc093 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
@@ -23,6 +23,8 @@ import org.apache.maven.surefire.report.SimpleReportEntry;
 
 import junit.framework.TestCase;
 
+import static org.apache.maven.plugin.surefire.report.ReportEntryType.*;
+
 /**
  * @author Kristian Rosenvold
  */
@@ -33,32 +35,84 @@ public class WrappedReportEntryTest
     {
         String className = "surefire.testcase.JunitParamsTest";
         WrappedReportEntry wr =
-            new WrappedReportEntry( new SimpleReportEntry( className, null ), null, 12, null, null );
-        final String reportName = wr.getReportName();
+            new WrappedReportEntry( new SimpleReportEntry( 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 );
+        assertTrue( wr.isSucceeded() );
+        assertFalse( wr.isErrorOrFailure() );
+        assertFalse( wr.isSkipped() );
     }
 
     public void testRegular()
     {
-        ReportEntry reportEntry = new SimpleReportEntry( "surefire.testcase.JunitParamsTest", "testSum" );
+        ReportEntry reportEntry = new SimpleReportEntry( "surefire.testcase.JunitParamsTest", null, "testSum", null );
         WrappedReportEntry wr = new WrappedReportEntry( reportEntry, null, 12, null, null );
-        final String reportName = wr.getReportName();
-        assertEquals( "surefire.testcase.JunitParamsTest", reportName );
+        assertEquals( "surefire.testcase.JunitParamsTest.testSum", wr.getClassMethodName() );
+        assertEquals( "surefire.testcase.JunitParamsTest", wr.getReportSourceName() );
+        assertEquals( "surefire.testcase.JunitParamsTest", wr.getReportSourceName( "" ) );
+        assertEquals( "surefire.testcase.JunitParamsTest(BDD)", wr.getReportSourceName( "BDD" ) );
+        assertEquals( "surefire.testcase.JunitParamsTest", wr.getSourceName( "" ) );
+        assertEquals( "surefire.testcase.JunitParamsTest(BDD)", wr.getSourceName( "BDD" ) );
+        assertEquals( "testSum", wr.getReportName() );
+        assertFalse(wr.isSucceeded());
+        assertFalse( wr.isErrorOrFailure() );
+        assertFalse( wr.isSkipped() );
+        assertTrue( wr.getSystemProperties().isEmpty() );
+        assertNull( wr.getGroup() );
+        assertEquals( "surefire.testcase.JunitParamsTest", wr.getNameWithGroup() );
+    }
+
+    public void testDisplayNames()
+    {
+        ReportEntry reportEntry =
+                new SimpleReportEntry( "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() );
+        assertEquals( "dn1(BDD)", wr.getReportSourceName( "BDD" ) );
+        assertEquals( "surefire.testcase.JunitParamsTest(BDD)", wr.getSourceName( "BDD" ) );
+        assertEquals( "dn2", wr.getReportName() );
+        assertFalse(wr.isSucceeded());
+        assertTrue(wr.isErrorOrFailure());
+        assertFalse( wr.isSkipped() );
+        assertNull( wr.getStackTraceWriter() );
+        assertEquals( "surefire.testcase.JunitParamsTest.testSum  Time elapsed: 0.012 s",
+                wr.getElapsedTimeSummary() );
+        assertEquals( "surefire.testcase.JunitParamsTest.testSum  Time elapsed: 0.012 s  <<< ERROR!",
+                wr.getOutput( false ) );
+        assertEquals( "exception", wr.getMessage() );
+    }
+
+    public void testEqualDisplayNames()
+    {
+        ReportEntry reportEntry = new SimpleReportEntry( "surefire.testcase.JunitParamsTest",
+                "surefire.testcase.JunitParamsTest", "testSum", "testSum" );
+        WrappedReportEntry wr = new WrappedReportEntry( reportEntry, FAILURE, 12, null, null );
+        assertEquals( "surefire.testcase.JunitParamsTest", wr.getReportSourceName() );
+        assertEquals( "surefire.testcase.JunitParamsTest(BDD)", wr.getReportSourceName( "BDD" ) );
+        assertEquals( "testSum", wr.getReportName() );
+        assertFalse(wr.isSucceeded());
+        assertTrue( wr.isErrorOrFailure() );
+        assertFalse( wr.isSkipped() );
     }
 
     public void testGetReportNameWithParams()
     {
         String className = "[0] 1\u002C 2\u002C 3 (testSum)";
-        ReportEntry reportEntry = new SimpleReportEntry( className, null );
-        WrappedReportEntry wr = new WrappedReportEntry( reportEntry, null, 12, null, null );
-        final String reportName = wr.getReportName();
+        ReportEntry reportEntry = new SimpleReportEntry( 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 );
+        assertFalse( wr.isSucceeded() );
+        assertFalse (wr.isErrorOrFailure() );
+        assertTrue( wr.isSkipped() );
     }
 
     public void testElapsed()
     {
         String className = "[0] 1\u002C 2\u002C 3 (testSum)";
-        ReportEntry reportEntry = new SimpleReportEntry( className, null );
+        ReportEntry reportEntry = new SimpleReportEntry( 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 2970356..b38d036 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
@@ -81,9 +81,9 @@ public class RunEntryStatisticsMapTest
         RunEntryStatisticsMap existingEntries = RunEntryStatisticsMap.fromFile( data );
         RunEntryStatisticsMap newResults = new RunEntryStatisticsMap();
 
-        ReportEntry reportEntry1 = new SimpleReportEntry( "abc", "method1", 42 );
-        ReportEntry reportEntry2 = new SimpleReportEntry( "abc", "willFail", 17 );
-        ReportEntry reportEntry3 = new SimpleReportEntry( "abc", "method3", 100 );
+        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 );
 
         newResults.add( existingEntries.createNextGeneration( reportEntry1 ) );
         newResults.add( existingEntries.createNextGeneration( reportEntry2 ) );
@@ -104,9 +104,9 @@ public class RunEntryStatisticsMapTest
         RunEntryStatisticsMap nextRun = RunEntryStatisticsMap.fromFile( data );
         newResults = new RunEntryStatisticsMap();
 
-        ReportEntry newRunReportEntry1 = new SimpleReportEntry( "abc", "method1", 52 );
-        ReportEntry newRunReportEntry2 = new SimpleReportEntry( "abc", "willFail", 27 );
-        ReportEntry newRunReportEntry3 = new SimpleReportEntry( "abc", "method3", 110 );
+        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 );
 
         newResults.add( nextRun.createNextGeneration( newRunReportEntry1 ) );
         newResults.add( nextRun.createNextGenerationFailure( newRunReportEntry2 ) );
@@ -129,7 +129,7 @@ public class RunEntryStatisticsMapTest
     {
         File data = File.createTempFile( "surefire-unit", "test" );
         RunEntryStatisticsMap reportEntries = RunEntryStatisticsMap.fromFile( data );
-        ReportEntry reportEntry = new SimpleReportEntry( "abc", "line1\nline2" + NL + " line3", 42 );
+        ReportEntry reportEntry = new SimpleReportEntry( "abc", null, "line1\nline2" + NL + " line3", null, 42 );
         reportEntries.add( reportEntries.createNextGeneration( reportEntry ) );
 
         reportEntries.serialize( data );
@@ -163,8 +163,10 @@ public class RunEntryStatisticsMapTest
     {
         File data = File.createTempFile( "surefire-unit", "test" );
         RunEntryStatisticsMap reportEntries = RunEntryStatisticsMap.fromFile( data );
-        reportEntries.add( reportEntries.createNextGeneration( new SimpleReportEntry( "abc", "line1\nline2", 42 ) ) );
-        reportEntries.add( reportEntries.createNextGeneration( new SimpleReportEntry( "abc", "test", 10 ) ) );
+        reportEntries.add(
+                reportEntries.createNextGeneration( new SimpleReportEntry( "abc", null, "line1\nline2", null, 42 ) ) );
+        reportEntries.add(
+                reportEntries.createNextGeneration( new SimpleReportEntry( "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/JUnit4SuiteTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
index c6177cd..d28a89c 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
@@ -25,6 +25,7 @@ import junit.framework.TestCase;
 import junit.framework.TestSuite;
 import org.apache.maven.plugin.surefire.AbstractSurefireMojoJava7PlusTest;
 import org.apache.maven.plugin.surefire.AbstractSurefireMojoTest;
+import org.apache.maven.plugin.surefire.CommonReflectorTest;
 import org.apache.maven.plugin.surefire.MojoMocklessTest;
 import org.apache.maven.plugin.surefire.SurefireHelperTest;
 import org.apache.maven.plugin.surefire.SurefireReflectorTest;
@@ -42,13 +43,16 @@ import org.apache.maven.plugin.surefire.booterclient.output.ForkClientTest;
 import org.apache.maven.plugin.surefire.booterclient.output.ForkedChannelDecoderTest;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactoryTest;
 import org.apache.maven.plugin.surefire.report.StatelessXmlReporterTest;
+import org.apache.maven.plugin.surefire.report.TestSetStatsTest;
 import org.apache.maven.plugin.surefire.report.WrappedReportEntryTest;
 import org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest;
 import org.apache.maven.plugin.surefire.util.DependenciesScannerTest;
 import org.apache.maven.plugin.surefire.util.DirectoryScannerTest;
 import org.apache.maven.plugin.surefire.util.ScannerUtilTest;
 import org.apache.maven.plugin.surefire.util.SpecificFileFilterTest;
-import org.apache.maven.surefire.report.ConsoleOutputFileReporterTest;
+import org.apache.maven.surefire.extensions.ConsoleOutputReporterTest;
+import org.apache.maven.surefire.extensions.StatelessReporterTest;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoReporterTest;
 import org.apache.maven.surefire.report.FileReporterTest;
 import org.apache.maven.surefire.report.RunStatisticsTest;
 import org.apache.maven.surefire.spi.SPITest;
@@ -68,7 +72,7 @@ public class JUnit4SuiteTest extends TestCase
         suite.addTestSuite( RelocatorTest.class );
         suite.addTestSuite( RunStatisticsTest.class );
         suite.addTestSuite( FileReporterTest.class );
-        suite.addTestSuite( ConsoleOutputFileReporterTest.class );
+        suite.addTestSuite( org.apache.maven.surefire.report.ConsoleOutputFileReporterTest.class );
         suite.addTestSuite( SurefirePropertiesTest.class );
         suite.addTestSuite( SpecificFileFilterTest.class );
         suite.addTest( new JUnit4TestAdapter( DirectoryScannerTest.class ) );
@@ -95,6 +99,11 @@ public class JUnit4SuiteTest extends TestCase
         suite.addTest( new JUnit4TestAdapter( MojoMocklessTest.class ) );
         suite.addTest( new JUnit4TestAdapter( ForkClientTest.class ) );
         suite.addTest( new JUnit4TestAdapter( ForkedChannelDecoderTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( ConsoleOutputReporterTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( StatelessReporterTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( TestSetStatsTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( StatelessTestsetInfoReporterTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( CommonReflectorTest.class ) );
         return suite;
     }
 }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ConsoleOutputReporterTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ConsoleOutputReporterTest.java
new file mode 100644
index 0000000..546e554
--- /dev/null
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ConsoleOutputReporterTest.java
@@ -0,0 +1,173 @@
+package org.apache.maven.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
+import org.apache.maven.plugin.surefire.extensions.junit5.JUnit5ConsoleOutputReporter;
+import org.apache.maven.plugin.surefire.report.ConsoleOutputFileReporter;
+import org.apache.maven.plugin.surefire.report.DirectConsoleOutput;
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.powermock.reflect.Whitebox.getInternalState;
+
+/**
+ * tests for {@link SurefireConsoleOutputReporter} and {@link JUnit5ConsoleOutputReporter}.
+ */
+public class ConsoleOutputReporterTest
+{
+    @Test
+    public void shouldCloneConsoleReporter()
+    {
+        SurefireConsoleOutputReporter extension = new SurefireConsoleOutputReporter();
+        extension.setDisable( true );
+        extension.setEncoding( "ISO-8859-1" );
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        Object clone = extension.clone( classLoader );
+        assertThat( clone )
+                .isNotSameAs( extension );
+        assertThat( clone )
+                .isInstanceOf( SurefireConsoleOutputReporter.class );
+        assertThat( clone.toString() )
+                .isEqualTo( "SurefireConsoleOutputReporter{disable=true, encoding=ISO-8859-1}" );
+        assertThat( ( (SurefireConsoleOutputReporter) clone ).isDisable() )
+                .isTrue();
+        assertThat( ( (SurefireConsoleOutputReporter) clone ).getEncoding() )
+                .isEqualTo( "ISO-8859-1" );
+    }
+
+    @Test
+    public void shouldAssertToStringConsoleReporter()
+    {
+        SurefireConsoleOutputReporter extension = new SurefireConsoleOutputReporter();
+        assertThat( extension.toString() )
+                .isEqualTo( "SurefireConsoleOutputReporter{disable=false, encoding=UTF-8}" );
+    }
+
+    @Test
+    public void shouldCreateConsoleListener()
+    {
+        ConsoleOutputReporter extension = new SurefireConsoleOutputReporter();
+
+        ConsoleOutputReportEventListener listener1 = extension.createListener( System.out, System.err );
+        assertThat( listener1 )
+                .isInstanceOf( DirectConsoleOutput.class );
+        assertThat( getInternalState( listener1, "out" ) )
+                .isSameAs( System.out );
+        assertThat( getInternalState( listener1, "err" ) )
+                .isSameAs( System.err );
+
+        File target = new File( System.getProperty( "user.dir" ), "target" );
+        File reportsDirectory = new File( target, "surefire-reports" );
+        String reportNameSuffix = "suffix";
+        boolean usePhrasedFileName = false;
+        Integer forkNumber = 1;
+        String encoding = "ISO-8859-2";
+        extension.setEncoding( encoding );
+        ConsoleOutputReportEventListener listener2 =
+                extension.createListener( reportsDirectory, reportNameSuffix, forkNumber );
+        assertThat( listener2 )
+                .isInstanceOf( ConsoleOutputFileReporter.class );
+        assertThat( getInternalState( listener2, "reportsDirectory" ) )
+                .isSameAs( reportsDirectory );
+        assertThat( getInternalState( listener2, "reportNameSuffix" ) )
+                .isSameAs( reportNameSuffix );
+        assertThat( getInternalState( listener2, "usePhrasedFileName" ) )
+                .isEqualTo( usePhrasedFileName );
+        assertThat( getInternalState( listener2, "forkNumber" ) )
+                .isSameAs( forkNumber );
+        assertThat( getInternalState( listener2, "encoding" ) )
+                .isSameAs( encoding );
+        assertThat( getInternalState( listener2, "reportEntryName" ) )
+                .isNull();
+    }
+
+    @Test
+    public void shouldCloneJUnit5ConsoleReporter()
+    {
+        JUnit5ConsoleOutputReporter extension = new JUnit5ConsoleOutputReporter();
+        extension.setDisable( true );
+        extension.setEncoding( "ISO-8859-1" );
+        extension.setUsePhrasedFileName( true );
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        Object clone = extension.clone( classLoader );
+        assertThat( clone )
+                .isNotSameAs( extension );
+        assertThat( clone )
+                .isInstanceOf( JUnit5ConsoleOutputReporter.class );
+        assertThat( clone.toString() ).isEqualTo(
+                "JUnit5ConsoleOutputReporter{disable=true, encoding=ISO-8859-1, usePhrasedFileName=true}" );
+        assertThat( ( (JUnit5ConsoleOutputReporter) clone ).isDisable() )
+                .isTrue();
+        assertThat( ( (JUnit5ConsoleOutputReporter) clone ).getEncoding() )
+                .isEqualTo( "ISO-8859-1" );
+        assertThat( ( (JUnit5ConsoleOutputReporter) clone ).isUsePhrasedFileName() )
+                .isTrue();
+    }
+
+    @Test
+    public void shouldAssertToStringJUnit5ConsoleReporter()
+    {
+        JUnit5ConsoleOutputReporter extension = new JUnit5ConsoleOutputReporter();
+        assertThat( extension.toString() )
+                .isEqualTo( "JUnit5ConsoleOutputReporter{disable=false, encoding=UTF-8, usePhrasedFileName=false}" );
+    }
+
+    @Test
+    public void shouldCreateJUnit5ConsoleListener()
+    {
+        JUnit5ConsoleOutputReporter extension = new JUnit5ConsoleOutputReporter();
+
+        ConsoleOutputReportEventListener listener1 = extension.createListener( System.out, System.err );
+        assertThat( listener1 )
+                .isInstanceOf( DirectConsoleOutput.class );
+        assertThat( getInternalState( listener1, "out" ) )
+                .isSameAs( System.out );
+        assertThat( getInternalState( listener1, "err" ) )
+                .isSameAs( System.err );
+
+        File target = new File( System.getProperty( "user.dir" ), "target" );
+        File reportsDirectory = new File( target, "surefire-reports" );
+        String reportNameSuffix = "suffix";
+        boolean usePhrasedFileName = true;
+        Integer forkNumber = 1;
+        String encoding = "ISO-8859-1";
+        extension.setEncoding( encoding );
+        extension.setUsePhrasedFileName( usePhrasedFileName );
+        ConsoleOutputReportEventListener listener2 =
+                extension.createListener( reportsDirectory, reportNameSuffix, forkNumber );
+        assertThat( listener2 )
+                .isInstanceOf( ConsoleOutputFileReporter.class );
+        assertThat( getInternalState( listener2, "reportsDirectory" ) )
+                .isSameAs( reportsDirectory );
+        assertThat( getInternalState( listener2, "reportNameSuffix" ) )
+                .isSameAs( reportNameSuffix );
+        assertThat( getInternalState( listener2, "usePhrasedFileName" ) )
+                .isEqualTo( usePhrasedFileName );
+        assertThat( getInternalState( listener2, "forkNumber" ) )
+                .isSameAs( forkNumber );
+        assertThat( getInternalState( listener2, "encoding" ) )
+                .isSameAs( encoding );
+        assertThat( getInternalState( listener2, "reportEntryName" ) )
+                .isNull();
+    }
+}
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/StatelessReporterTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/StatelessReporterTest.java
new file mode 100644
index 0000000..bb06eff
--- /dev/null
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/StatelessReporterTest.java
@@ -0,0 +1,244 @@
+package org.apache.maven.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.extensions.DefaultStatelessReportMojoConfiguration;
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
+import org.apache.maven.plugin.surefire.extensions.junit5.JUnit5Xml30StatelessReporter;
+import org.apache.maven.plugin.surefire.report.StatelessXmlReporter;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.powermock.reflect.Whitebox.getInternalState;
+
+/**
+ * tests for {@link SurefireStatelessReporter} and {@link JUnit5Xml30StatelessReporter}.
+ */
+public class StatelessReporterTest
+{
+    @Test
+    public void shouldCloneXmlReporter()
+    {
+        SurefireStatelessReporter extension = new SurefireStatelessReporter();
+        extension.setDisable( true );
+        extension.setVersion( "V1" );
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+
+        Object clone = extension.clone( classLoader );
+        assertThat( clone )
+                .isNotSameAs( extension );
+        assertThat( clone )
+                .isInstanceOf( SurefireStatelessReporter.class );
+        assertThat( clone.toString() )
+                .isEqualTo( "SurefireStatelessReporter{version=V1, disable=true}" );
+        assertThat( ( (SurefireStatelessReporter) clone ).isDisable() )
+                .isTrue();
+        assertThat( ( (SurefireStatelessReporter) clone ).getVersion() )
+                .isEqualTo( "V1" );
+    }
+
+    @Test
+    public void shouldAssertToStringXmlReporter()
+    {
+        SurefireStatelessReporter extension = new SurefireStatelessReporter();
+        assertThat( extension.toString() )
+                .isEqualTo( "SurefireStatelessReporter{version=3.0, disable=false}" );
+    }
+
+    @Test
+    public void shouldCreateConsoleListener()
+    {
+        File target = new File( System.getProperty( "user.dir" ), "target" );
+        File reportsDirectory = new File( target, "surefire-reports" );
+        String reportNameSuffix = "suffix";
+        String schema = "https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report-3.0.xsd";
+        Map<String, Deque<WrappedReportEntry>> testClassMethodRunHistory = new HashMap<>();
+        DefaultStatelessReportMojoConfiguration config =
+                new DefaultStatelessReportMojoConfiguration( reportsDirectory, reportNameSuffix, true, 5, schema,
+                        testClassMethodRunHistory );
+        SurefireStatelessReporter extension = new SurefireStatelessReporter();
+
+        assertThat( extension.getVersion() )
+                .isEqualTo( "3.0" );
+        extension.setVersion( "V3" );
+        assertThat( extension.getVersion() )
+                .isEqualTo( "V3" );
+
+        assertThat( extension.isDisable() )
+                .isFalse();
+        extension.setDisable( true );
+        assertThat( extension.isDisable() )
+                .isTrue();
+
+        StatelessReportEventListener<WrappedReportEntry, TestSetStats> listener = extension.createListener( config );
+        assertThat( listener )
+                .isInstanceOf( StatelessXmlReporter.class );
+        assertThat( getInternalState( listener, "reportsDirectory" ) )
+                .isSameAs( reportsDirectory );
+        assertThat( getInternalState( listener, "reportNameSuffix" ) )
+                .isSameAs( reportNameSuffix );
+        assertThat( getInternalState( listener, "trimStackTrace" ) )
+                .isEqualTo( true );
+        assertThat( getInternalState( listener, "rerunFailingTestsCount" ) )
+                .isEqualTo( 5 );
+        assertThat( getInternalState( listener, "xsdSchemaLocation" ) )
+                .isSameAs( schema );
+        assertThat( getInternalState( listener, "xsdVersion" ) )
+                .isEqualTo( "V3" );
+        assertThat( getInternalState( listener, "testClassMethodRunHistoryMap" ) )
+                .isSameAs( testClassMethodRunHistory );
+        assertThat( getInternalState( listener, "phrasedFileName" ) )
+                .isEqualTo( false );
+        assertThat( getInternalState( listener, "phrasedSuiteName" ) )
+                .isEqualTo( false );
+        assertThat( getInternalState( listener, "phrasedClassName" ) )
+                .isEqualTo( false );
+        assertThat( getInternalState( listener, "phrasedMethodName" ) )
+                .isEqualTo( false );
+    }
+
+    @Test
+    public void shouldCloneJUnit5XmlReporter()
+    {
+        JUnit5Xml30StatelessReporter extension = new JUnit5Xml30StatelessReporter();
+        extension.setDisable( true );
+        extension.setVersion( "V1" );
+        extension.setUsePhrasedFileName( true );
+        extension.setUsePhrasedTestSuiteClassName( true );
+        extension.setUsePhrasedTestCaseClassName( true );
+        extension.setUsePhrasedTestCaseMethodName( true );
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+
+        Object clone = extension.clone( classLoader );
+        assertThat( clone )
+                .isNotSameAs( extension );
+        assertThat( clone )
+                .isInstanceOf( JUnit5Xml30StatelessReporter.class );
+        assertThat( clone.toString() )
+                .isEqualTo( "JUnit5Xml30StatelessReporter{version=V1, disable=true, usePhrasedFileName=true, "
+                        + "usePhrasedTestSuiteClassName=true, usePhrasedTestCaseClassName=true, "
+                        + "usePhrasedTestCaseMethodName=true}" );
+        assertThat( ( (JUnit5Xml30StatelessReporter) clone ).isDisable() )
+                .isTrue();
+        assertThat( ( (JUnit5Xml30StatelessReporter) clone ).getVersion() )
+                .isEqualTo( "V1" );
+        assertThat( ( (JUnit5Xml30StatelessReporter) clone ).getUsePhrasedFileName() )
+                .isTrue();
+        assertThat( ( (JUnit5Xml30StatelessReporter) clone ).getUsePhrasedTestCaseClassName() )
+                .isTrue();
+        assertThat( ( (JUnit5Xml30StatelessReporter) clone ).getUsePhrasedTestCaseMethodName() )
+                .isTrue();
+        assertThat( ( (JUnit5Xml30StatelessReporter) clone ).getUsePhrasedTestSuiteClassName() )
+                .isTrue();
+    }
+
+    @Test
+    public void shouldAssertToStringJUnit5ConsoleReporter()
+    {
+        JUnit5Xml30StatelessReporter extension = new JUnit5Xml30StatelessReporter();
+        assertThat( extension.toString() )
+                .isEqualTo( "JUnit5Xml30StatelessReporter{version=3.0, disable=false, "
+                        + "usePhrasedFileName=false, usePhrasedTestSuiteClassName=false, "
+                        + "usePhrasedTestCaseClassName=false, usePhrasedTestCaseMethodName=false}" );
+    }
+
+    @Test
+    public void shouldCreateJUnit5ConsoleListener()
+    {
+        File target = new File( System.getProperty( "user.dir" ), "target" );
+        File reportsDirectory = new File( target, "surefire-reports" );
+        String reportNameSuffix = "suffix";
+        String schema = "https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report-3.0.xsd";
+        Map<String, Deque<WrappedReportEntry>> testClassMethodRunHistory = new HashMap<>();
+        DefaultStatelessReportMojoConfiguration config =
+                new DefaultStatelessReportMojoConfiguration( reportsDirectory, reportNameSuffix, true, 5, schema,
+                        testClassMethodRunHistory );
+        JUnit5Xml30StatelessReporter extension = new JUnit5Xml30StatelessReporter();
+
+        assertThat( extension.getVersion() )
+                .isEqualTo( "3.0" );
+        extension.setVersion( "V3" );
+        assertThat( extension.getVersion() )
+                .isEqualTo( "V3" );
+
+        assertThat( extension.isDisable() )
+                .isFalse();
+        extension.setDisable( true );
+        assertThat( extension.isDisable() )
+                .isTrue();
+
+        assertThat( extension.getUsePhrasedFileName() )
+                .isFalse();
+        extension.setUsePhrasedFileName( true );
+        assertThat( extension.getUsePhrasedFileName() )
+                .isTrue();
+
+        assertThat( extension.getUsePhrasedTestSuiteClassName() )
+                .isFalse();
+        extension.setUsePhrasedTestSuiteClassName( true );
+        assertThat( extension.getUsePhrasedTestSuiteClassName() )
+                .isTrue();
+
+        assertThat( extension.getUsePhrasedTestCaseClassName() )
+                .isFalse();
+        extension.setUsePhrasedTestCaseClassName( true );
+        assertThat( extension.getUsePhrasedTestSuiteClassName() )
+                .isTrue();
+
+        assertThat( extension.getUsePhrasedTestCaseMethodName() )
+                .isFalse();
+        extension.setUsePhrasedTestCaseMethodName( true );
+        assertThat( extension.getUsePhrasedTestCaseMethodName() )
+                .isTrue();
+
+        StatelessReportEventListener<WrappedReportEntry, TestSetStats> listener = extension.createListener( config );
+        assertThat( listener )
+                .isInstanceOf( StatelessXmlReporter.class );
+        assertThat( getInternalState( listener, "reportsDirectory" ) )
+                .isSameAs( reportsDirectory );
+        assertThat( getInternalState( listener, "reportNameSuffix" ) )
+                .isSameAs( reportNameSuffix );
+        assertThat( getInternalState( listener, "trimStackTrace" ) )
+                .isEqualTo( true );
+        assertThat( getInternalState( listener, "rerunFailingTestsCount" ) )
+                .isEqualTo( 5 );
+        assertThat( getInternalState( listener, "xsdSchemaLocation" ) )
+                .isSameAs( schema );
+        assertThat( getInternalState( listener, "xsdVersion" ) )
+                .isEqualTo( "V3" );
+        assertThat( getInternalState( listener, "testClassMethodRunHistoryMap" ) )
+                .isSameAs( testClassMethodRunHistory );
+        assertThat( getInternalState( listener, "phrasedFileName" ) )
+                .isEqualTo( true );
+        assertThat( getInternalState( listener, "phrasedSuiteName" ) )
+                .isEqualTo( true );
+        assertThat( getInternalState( listener, "phrasedClassName" ) )
+                .isEqualTo( true );
+        assertThat( getInternalState( listener, "phrasedMethodName" ) )
+                .isEqualTo( true );
+    }
+}
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoReporterTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoReporterTest.java
new file mode 100644
index 0000000..aa10111
--- /dev/null
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoReporterTest.java
@@ -0,0 +1,229 @@
+package org.apache.maven.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
+import org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoReporter;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.report.ConsoleReporter;
+import org.apache.maven.plugin.surefire.report.FileReporter;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.shared.utils.logging.MessageUtils;
+import org.apache.maven.surefire.report.TestSetReportEntry;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import static java.util.Collections.singletonList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import static org.powermock.reflect.Whitebox.getInternalState;
+
+/**
+ * tests for {@link SurefireStatelessTestsetInfoReporter} and {@link JUnit5StatelessTestsetInfoReporter}.
+ */
+@RunWith( PowerMockRunner.class )
+@PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
+public class StatelessTestsetInfoReporterTest
+{
+    @Mock
+    private ConsoleLogger consoleLogger;
+
+    @Mock
+    private TestSetReportEntry eventTestsetStarting;
+
+    @Mock
+    private WrappedReportEntry eventTestsetFinished;
+
+    @Test
+    public void shouldCloneReporter()
+    {
+        SurefireStatelessTestsetInfoReporter extension = new SurefireStatelessTestsetInfoReporter();
+        extension.setDisable( true );
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+
+        Object clone = extension.clone( classLoader );
+        assertThat( clone )
+                .isNotSameAs( extension );
+        assertThat( clone )
+                .isInstanceOf( SurefireStatelessTestsetInfoReporter.class );
+        assertThat( clone.toString() )
+                .isEqualTo( "SurefireStatelessTestsetInfoReporter{disable=true}" );
+        assertThat( ( (SurefireStatelessTestsetInfoReporter) clone ).isDisable() )
+                .isTrue();
+    }
+
+    @Test
+    public void shouldAssertToStringReporter()
+    {
+        SurefireStatelessTestsetInfoReporter extension = new SurefireStatelessTestsetInfoReporter();
+        assertThat( extension.toString() )
+                .isEqualTo( "SurefireStatelessTestsetInfoReporter{disable=false}" );
+    }
+
+    @Test
+    public void shouldCreateFileReporterListener()
+    {
+        File target = new File( System.getProperty( "user.dir" ), "target" );
+        File reportsDirectory = new File( target, "surefire-reports" );
+        String reportNameSuffix = "suffix";
+        Charset encoding = StandardCharsets.UTF_8;
+        SurefireStatelessTestsetInfoReporter extension = new SurefireStatelessTestsetInfoReporter();
+
+        assertThat( extension.isDisable() )
+                .isFalse();
+        extension.setDisable( true );
+        assertThat( extension.isDisable() )
+                .isTrue();
+
+        StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> listener =
+                extension.createListener( reportsDirectory, reportNameSuffix, encoding );
+        assertThat( listener )
+                .isInstanceOf( FileReporter.class );
+        assertThat( listener.getReportsDirectory() )
+                .isSameAs( reportsDirectory );
+        assertThat( listener.getReportNameSuffix() )
+                .isSameAs( reportNameSuffix );
+        assertThat( listener.getEncoding() )
+                .isSameAs( encoding );
+        assertThat( getInternalState( listener, "usePhrasedFileName" ) )
+                .isEqualTo( false );
+        assertThat( getInternalState( listener, "usePhrasedClassNameInRunning" ) )
+                .isEqualTo( false );
+        assertThat( getInternalState( listener, "usePhrasedClassNameInTestCaseSummary" ) )
+                .isEqualTo( false );
+    }
+
+    @Test
+    public void shouldCreateConsoleReporterListener()
+    {
+        SurefireStatelessTestsetInfoReporter extension = new SurefireStatelessTestsetInfoReporter();
+
+        assertThat( extension.isDisable() )
+                .isFalse();
+        extension.setDisable( true );
+        assertThat( extension.isDisable() )
+                .isTrue();
+
+        StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> listener =
+                extension.createListener( consoleLogger );
+        assertThat( listener )
+                .isInstanceOf( ConsoleReporter.class );
+        assertThat( listener.getConsoleLogger() )
+                .isSameAs( consoleLogger );
+        assertThat( getInternalState( listener, "usePhrasedClassNameInRunning" ) )
+                .isEqualTo( false );
+        assertThat( getInternalState( listener, "usePhrasedClassNameInTestCaseSummary" ) )
+                .isEqualTo( false );
+    }
+
+    @Test
+    public void shouldCloneJUnit5Reporter()
+    {
+        JUnit5StatelessTestsetInfoReporter extension = new JUnit5StatelessTestsetInfoReporter();
+        extension.setDisable( true );
+        extension.setUsePhrasedFileName( true );
+        extension.setUsePhrasedClassNameInTestCaseSummary( true );
+        extension.setUsePhrasedClassNameInRunning( true );
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+
+        Object clone = extension.clone( classLoader );
+        assertThat( clone )
+                .isNotSameAs( extension );
+        assertThat( clone )
+                .isInstanceOf( JUnit5StatelessTestsetInfoReporter.class );
+        assertThat( clone.toString() )
+                .isEqualTo( "JUnit5StatelessTestsetInfoReporter{disable=true, usePhrasedFileName=true, "
+                        + "usePhrasedClassNameInRunning=true, usePhrasedClassNameInTestCaseSummary=true}" );
+        assertThat( ( (JUnit5StatelessTestsetInfoReporter) clone ).isDisable() )
+                .isTrue();
+        assertThat( ( (JUnit5StatelessTestsetInfoReporter) clone ).isUsePhrasedFileName() )
+                .isTrue();
+        assertThat( ( (JUnit5StatelessTestsetInfoReporter) clone ).isUsePhrasedClassNameInTestCaseSummary() )
+                .isTrue();
+        assertThat( ( (JUnit5StatelessTestsetInfoReporter) clone ).isUsePhrasedClassNameInRunning() )
+                .isTrue();
+    }
+
+    @Test
+    public void shouldAssertToStringJUnit5Reporter()
+    {
+        JUnit5StatelessTestsetInfoReporter extension = new JUnit5StatelessTestsetInfoReporter();
+        assertThat( extension.toString() )
+                .isEqualTo( "JUnit5StatelessTestsetInfoReporter{disable=false, usePhrasedFileName=false, "
+                                + "usePhrasedClassNameInRunning=false, usePhrasedClassNameInTestCaseSummary=false}" );
+    }
+
+    @Test
+    public void shouldReportTestsetLifecycle()
+    {
+        ConsoleReporter consoleReporter = new ConsoleReporter( consoleLogger, false, false );
+        MessageUtils.setColorEnabled( false );
+
+        when( eventTestsetStarting.getNameWithGroup() ).thenReturn( "pkg.MyTest" );
+        when( eventTestsetFinished.getNameWithGroup() ).thenReturn( "pkg.MyTest" );
+        when( eventTestsetFinished.getElapsedTimeVerbose() ).thenReturn( "Time elapsed: 1.03 s" );
+
+        consoleReporter.testSetStarting( eventTestsetStarting );
+        ArgumentCaptor<String> logs = ArgumentCaptor.forClass( String.class );
+        verify( consoleLogger, times( 1 ) ).info( logs.capture() );
+        verifyNoMoreInteractions( consoleLogger );
+        assertThat( logs.getAllValues() )
+                .hasSize( 1 )
+                .contains( "Running pkg.MyTest" );
+
+        TestSetStats testSetStats = new TestSetStats( false, true );
+        testSetStats.testStart();
+        testSetStats.testFailure( eventTestsetFinished );
+        assertThat( testSetStats.getCompletedCount() ).isEqualTo( 1 );
+        assertThat( testSetStats.getFailures() ).isEqualTo( 1 );
+        assertThat( testSetStats.getErrors() ).isEqualTo( 0 );
+        assertThat( testSetStats.getSkipped() ).isEqualTo( 0 );
+        reset( consoleLogger );
+        consoleReporter.testSetCompleted( eventTestsetFinished, testSetStats, singletonList( "pkg.MyTest failed" ) );
+        consoleReporter.reset();
+        verify( consoleLogger, never() ).info( anyString() );
+        verify( consoleLogger, never() ).warning( anyString() );
+        logs = ArgumentCaptor.forClass( String.class );
+        verify( consoleLogger, times( 2 ) ).error( logs.capture() );
+        List<String> messages = logs.getAllValues();
+        assertThat( messages )
+                .hasSize( 2 )
+                .containsSequence(
+                        "Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.03 s "
+                        + "<<< FAILURE! - in pkg.MyTest", "pkg.MyTest failed" );
+        verifyNoMoreInteractions( consoleLogger );
+    }
+}
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 ee086f5..c74e5b7 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
@@ -45,8 +45,9 @@ public class ConsoleOutputFileReporterTest
         File reportDir = new File( new File( System.getProperty( "user.dir" ), "target" ), "tmp1" );
         //noinspection ResultOfMethodCallIgnored
         reportDir.mkdirs();
-        ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), getClass().getName() );
-        ConsoleOutputFileReporter reporter = new ConsoleOutputFileReporter( reportDir, null, null );
+        TestSetReportEntry reportEntry =
+                new SimpleReportEntry( getClass().getName(), null, getClass().getName(), null );
+        ConsoleOutputFileReporter reporter = new ConsoleOutputFileReporter( reportDir, null, false, null, "UTF-8" );
         reporter.testSetStarting( reportEntry );
         reporter.writeTestOutput( "some ", false, true );
         reporter.testSetCompleted( reportEntry );
@@ -70,13 +71,14 @@ public class ConsoleOutputFileReporterTest
     public void testFileNameWithSuffix() throws IOException
     {
         File reportDir = new File( new File( System.getProperty( "user.dir" ), "target" ), "tmp2" );
-        //noinspection ResultOfMethodCallIgnored
-        reportDir.mkdirs();
         String suffixText = "sampleSuffixText";
-        ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), getClass().getName() );
-        ConsoleOutputFileReporter reporter = new ConsoleOutputFileReporter( reportDir, suffixText, null );
+        TestSetReportEntry reportEntry =
+                new SimpleReportEntry( getClass().getName(), null, getClass().getName(), null );
+        ConsoleOutputFileReporter reporter =
+                new ConsoleOutputFileReporter( reportDir, suffixText, false, null, "UTF-8" );
         reporter.testSetStarting( reportEntry );
-        reporter.writeTestOutput( "some ", false, true );
+        reporter.writeTestOutput( null, true, true );
+        reporter.writeTestOutput( "some ", true, true );
         reporter.testSetCompleted( reportEntry );
         reporter.close();
 
@@ -88,6 +90,9 @@ public class ConsoleOutputFileReporterTest
         assertThat( FileUtils.fileRead( expectedReportFile, US_ASCII.name() ) )
                 .contains( "some " );
 
+        assertThat( expectedReportFile )
+                .hasSize( 9 + 2 * System.lineSeparator().length() );
+
         //noinspection ResultOfMethodCallIgnored
         expectedReportFile.delete();
     }
@@ -95,11 +100,9 @@ public class ConsoleOutputFileReporterTest
     public void testNullReportFile() throws IOException
     {
         File reportDir = new File( new File( System.getProperty( "user.dir" ), "target" ), "tmp3" );
-        //noinspection ResultOfMethodCallIgnored
-        reportDir.mkdirs();
-        ConsoleOutputFileReporter reporter = new ConsoleOutputFileReporter( reportDir, null, null );
+        ConsoleOutputFileReporter reporter = new ConsoleOutputFileReporter( reportDir, null, false, null, "UTF-8" );
         reporter.writeTestOutput( "some text", false, true );
-        reporter.testSetCompleted( new SimpleReportEntry( getClass().getName(), getClass().getName() ) );
+        reporter.testSetCompleted( new SimpleReportEntry( getClass().getName(), null, getClass().getName(), null ) );
         reporter.close();
 
         File expectedReportFile = new File( reportDir, "null-output.txt" );
@@ -117,10 +120,9 @@ public class ConsoleOutputFileReporterTest
     public void testConcurrentAccessReportFile() throws Exception
     {
         File reportDir = new File( new File( System.getProperty( "user.dir" ), "target" ), "tmp4" );
-        //noinspection ResultOfMethodCallIgnored
-        reportDir.mkdirs();
-        final ConsoleOutputFileReporter reporter = new ConsoleOutputFileReporter( reportDir, null, null );
-        reporter.testSetStarting( new SimpleReportEntry( getClass().getName(), getClass().getName() ) );
+        final ConsoleOutputFileReporter reporter =
+                new ConsoleOutputFileReporter( reportDir, null, false, null, "UTF-8" );
+        reporter.testSetStarting( new SimpleReportEntry( 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++ )
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 b733e2a..0b9d84b 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
@@ -42,10 +42,10 @@ public class FileReporterTest
     public void testFileNameWithoutSuffix()
     {
         File reportDir = new File( "target" );
-        reportEntry = new SimpleReportEntry( this.getClass().getName(), testName );
+        reportEntry = new SimpleReportEntry( getClass().getName(), null, testName, null );
         WrappedReportEntry wrappedReportEntry =
             new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null );
-        reporter = new FileReporter( reportDir, null, Charset.defaultCharset() );
+        reporter = new FileReporter( reportDir, null, Charset.defaultCharset(), false, false, false );
         reporter.testSetCompleted( wrappedReportEntry, createTestSetStats(), new ArrayList<String>() );
 
         File expectedReportFile = new File( reportDir, testName + ".txt" );
@@ -64,10 +64,10 @@ public class FileReporterTest
     {
         File reportDir = new File( "target" );
         String suffixText = "sampleSuffixText";
-        reportEntry = new SimpleReportEntry( this.getClass().getName(), testName );
+        reportEntry = new SimpleReportEntry( getClass().getName(), null, testName, null );
         WrappedReportEntry wrappedReportEntry =
             new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null );
-        reporter = new FileReporter( reportDir, suffixText, Charset.defaultCharset() );
+        reporter = new FileReporter( reportDir, suffixText, Charset.defaultCharset(), false, false, false );
         reporter.testSetCompleted( wrappedReportEntry, createTestSetStats(), new ArrayList<String>() );
 
         File expectedReportFile = new File( reportDir, testName + "-" + suffixText + ".txt" );
diff --git a/maven-surefire-plugin/pom.xml b/maven-surefire-plugin/pom.xml
index ac34a2b..5a1261f 100644
--- a/maven-surefire-plugin/pom.xml
+++ b/maven-surefire-plugin/pom.xml
@@ -86,7 +86,33 @@
                 </executions>
             </plugin>
             <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>jacoco-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <!--todo remove-->
+                        <id>jacoco-report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <propertyName>jacoco.agent</propertyName>
+                </configuration>
+            </plugin>
+            <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
+                </configuration>
                 <dependencies>
                     <dependency>
                         <groupId>org.apache.maven.surefire</groupId>
diff --git a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
index 21fb2f6..e23f05e 100644
--- a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
+++ b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
@@ -237,7 +237,7 @@ public class SurefirePlugin
     /**
      * The character encoding scheme to be applied while generating test report
      * files (see target/surefire-reports/yourTestName.txt).
-     * The report output files (*-out.txt) are still encoded with JVM's encoding used in standard out/err pipes.
+     * The report output files (*-out.txt) are encoded in UTF-8 if not set otherwise.
      *
      * @since 3.0.0-M1
      */
diff --git a/maven-surefire-plugin/src/site/apt/examples/junit-platform.apt.vm b/maven-surefire-plugin/src/site/apt/examples/junit-platform.apt.vm
index 1a11987..2a86f2a 100644
--- a/maven-surefire-plugin/src/site/apt/examples/junit-platform.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/examples/junit-platform.apt.vm
@@ -309,3 +309,56 @@ else
 </build>
 ...
 +---+
+
+
+* Surefire Extensions and Reports Configuration for @DisplayName
+
+   Since plugin version 3.0.0-M4 you can use fine grained configuration of reports and enable phrased names together
+   with <<<...@DisplayName>>> in you tests.
+   This is the complete list of attributes of particular objects. You do not have to specify e.g. <<<disable>>>,
+   <<<version>>> and <<<encoding>>>. The boolean values reach default values <<<false>>> if not specified otherwise.
+
++---+
+...
+<build>
+    <plugins>
+        ...
+        <plugin>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>${project.artifactId}</artifactId>
+            <version>${project.version}</version>
+            <configuration>
+                <statelessTestsetReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5Xml30StatelessReporter">
+                    <disable>false</disable>
+                    <version>3.0</version>
+                    <usePhrasedFileName>false</usePhrasedFileName>
+                    <usePhrasedTestSuiteClassName>true</usePhrasedTestSuiteClassName>
+                    <usePhrasedTestCaseClassName>true</usePhrasedTestCaseClassName>
+                    <usePhrasedTestCaseMethodName>true</usePhrasedTestCaseMethodName>
+                </statelessTestsetReporter>
+                <consoleOutputReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5ConsoleOutputReporter">
+                    <disable>false</disable>
+                    <encoding>UTF-8</encoding>
+                    <usePhrasedFileName>false</usePhrasedFileName>
+                </consoleOutputReporter>
+                <statelessTestsetInfoReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoReporter">
+                    <disable>false</disable>
+                    <usePhrasedFileName>false</usePhrasedFileName>
+                    <usePhrasedClassNameInRunning>true</usePhrasedClassNameInRunning>
+                    <usePhrasedClassNameInTestCaseSummary>true</usePhrasedClassNameInTestCaseSummary>
+                </statelessTestsetInfoReporter>
+            </configuration>
+        </plugin>
+    </plugins>
+</build>
+...
++---+
+
+   Default implementations of these extensions are
+   <<<org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter>>>,
+   <<<org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter>>>, and
+   <<<org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter>>>.
+
+   The aim of extensions is to let the users customizing the default behavior. We are keen on listing useful
+   extensions on Apache Maven Surefire site if you propagate your extensions on GitHub.
+
diff --git a/maven-surefire-plugin/src/site/apt/featurematrix.apt.vm b/maven-surefire-plugin/src/site/apt/featurematrix.apt.vm
index d4024a2..521895a 100644
--- a/maven-surefire-plugin/src/site/apt/featurematrix.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/featurematrix.apt.vm
@@ -40,6 +40,14 @@ Feature Matrix
 *---------------------------------------------+------------+----------+------------+-----------+----------+----------------------+
 | parallel support                            |     N      |    N     |      Y     |    Y      |  N       |  N                   |
 *---------------------------------------------+------------+----------+------------+-----------+----------+----------------------+
+| custom run-listener                         |     N      |    Y     |      Y     |    Y      |  -       |  N                   |
+*---------------------------------------------+------------+----------+------------+-----------+----------+----------------------+
+| re-run count                                |     N      |    Y     |      Y     |    N      |  N       |  N                   |
+*---------------------------------------------+------------+----------+------------+-----------+----------+----------------------+
+| skip after failure count                    |     N      |    Y     |      Y     |    Y      |  N       |  N                   |
+*---------------------------------------------+------------+----------+------------+-----------+----------+----------------------+
+| Surefire Extensions                         |     Y      |    Y     |      Y     |    Y      |  Y       |  Y(*2)               |
+*---------------------------------------------+------------+----------+------------+-----------+----------+----------------------+
 
 
     Legend: "Y" means supported, "N" means not supported. "?" means not tested.
@@ -50,4 +58,7 @@ Feature Matrix
 
    (*1) The JUnit 5 Platform supports running multiple individual test methods in a single class, but there are some
    corner cases that are not supported, yet: {{{https://github.com/junit-team/junit5/issues/1343}junit-team/junit5#1343}}
-   and {{{https://github.com/junit-team/junit5/issues/1406}junit-team/junit5#1406)}}.
\ No newline at end of file
+   and {{{https://github.com/junit-team/junit5/issues/1406}junit-team/junit5#1406)}}.
+
+   (*2) 3 extensions related to JUnit5 annotation <<<DisplayName>>>.
+
diff --git a/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java b/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
index 4cbbf00..6d7b4a3 100644
--- a/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
+++ b/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
@@ -18,99 +18,70 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-
-import java.lang.reflect.Field;
-import org.apache.maven.toolchain.Toolchain;
-
 import junit.framework.TestCase;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.surefire.suite.RunResult;
 
-public class SurefirePluginTest
-    extends TestCase
-{
-
-    public void testForkMode()
-        throws NoSuchFieldException, IllegalAccessException
-    {
-        SurefirePlugin surefirePlugin = new SurefirePlugin();
-        setFieldValue( surefirePlugin, "toolchain", new MyToolChain() );
-        setFieldValue( surefirePlugin, "forkMode", "never" );
-        assertEquals( "once", surefirePlugin.getEffectiveForkMode() );
-    }
+import static org.fest.assertions.Assertions.assertThat;
 
-    public void testForkCountComputation()
+public class SurefirePluginTest extends TestCase
+{
+    public void testDefaultIncludes()
     {
-        SurefirePlugin surefirePlugin = new SurefirePlugin();
-        assertConversionFails( surefirePlugin, "nothing" );
-
-        assertConversionFails( surefirePlugin, "5,0" );
-        assertConversionFails( surefirePlugin, "5.0" );
-        assertConversionFails( surefirePlugin, "5,0C" );
-        assertConversionFails( surefirePlugin, "5.0CC" );
-
-        assertForkCount( surefirePlugin, 5, "5" );
-
-        int availableProcessors = Runtime.getRuntime().availableProcessors();
-        assertForkCount( surefirePlugin, 3*availableProcessors, "3C" );
-        assertForkCount( surefirePlugin, (int) ( 2.5*availableProcessors ), "2.5C" );
-        assertForkCount( surefirePlugin, availableProcessors, "1.0001 C" );
-        assertForkCount( surefirePlugin, 1, 1d / ( (double) availableProcessors + 1 ) + "C" );
-        assertForkCount( surefirePlugin, 0, "0 C" );
+        assertThat( new SurefirePlugin().getDefaultIncludes() )
+                .containsOnly( "**/Test*.java", "**/*Test.java", "**/*Tests.java", "**/*TestCase.java" );
     }
 
-    private void assertForkCount( SurefirePlugin surefirePlugin, int expected, String value )
+    public void testReportSchemaLocation()
     {
-        assertEquals( expected, surefirePlugin.convertWithCoreCount( value ));
+        assertThat( new SurefirePlugin().getReportSchemaLocation() )
+            .isEqualTo( "https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report-3.0.xsd" );
     }
 
-    private void assertConversionFails( SurefirePlugin surefirePlugin, String value )
+    public void testFailIfNoTests() throws Exception
     {
-        try {
-            surefirePlugin.convertWithCoreCount( value );
-        } catch (NumberFormatException nfe)
+        RunResult runResult = new RunResult( 0, 0, 0, 0 );
+        try
         {
-            return;
+            SurefirePlugin plugin = new SurefirePlugin();
+            plugin.setFailIfNoTests( true );
+            plugin.handleSummary( runResult, null );
         }
-        fail( "Expected NumberFormatException when converting " + value );
-    }
-
-    private void setFieldValue( SurefirePlugin plugin, String fieldName, Object value )
-        throws NoSuchFieldException, IllegalAccessException
-    {
-        Field field = findField( plugin.getClass(), fieldName );
-        field.setAccessible( true );
-        field.set( plugin, value );
-
-    }
-
-    private Field findField( Class clazz, String fieldName )
-    {
-        while ( clazz != null )
+        catch ( MojoFailureException e )
         {
-            try
-            {
-                return clazz.getDeclaredField( fieldName );
-            }
-            catch ( NoSuchFieldException e )
-            {
-                clazz = clazz.getSuperclass();
-            }
+            assertThat( e.getLocalizedMessage() )
+                    .isEqualTo( "No tests were executed!  (Set -DfailIfNoTests=false to ignore this error.)" );
+            return;
         }
-        throw new IllegalArgumentException( "Field not found" );
+        fail( "Expected MojoFailureException with message "
+                + "'No tests were executed!  (Set -DfailIfNoTests=false to ignore this error.)'" );
     }
 
-    private class MyToolChain
-        implements Toolchain
+    public void testTestFailure() throws Exception
     {
-        @Override
-        public String getType()
+        RunResult runResult = new RunResult( 1, 0, 1, 0 );
+        try
         {
-            return null;
+            SurefirePlugin plugin = new SurefirePlugin();
+            plugin.handleSummary( runResult, null );
         }
-
-        @Override
-        public String findTool( String s )
+        catch ( MojoFailureException e )
         {
-            return null;
+            assertThat( e.getLocalizedMessage() )
+                    .isEqualTo( "There are test failures.\n\nPlease refer to null "
+                            + "for the individual test results.\nPlease refer to dump files (if any exist) "
+                            + "[date].dump, [date]-jvmRun[N].dump and [date].dumpstream." );
+            return;
         }
+        fail( "Expected MojoFailureException with message "
+                + "'There are test failures.\n\nPlease refer to null "
+                + "for the individual test results.\nPlease refer to dump files (if any exist) "
+                + "[date].dump, [date]-jvmRun[N].dump and [date].dumpstream.'");
+    }
+
+    public void testPluginName()
+    {
+        assertThat( new SurefirePlugin().getPluginName() )
+                .isEqualTo( "surefire" );
     }
 }
diff --git a/maven-surefire-report-plugin/pom.xml b/maven-surefire-report-plugin/pom.xml
index cf941ce..5f855d0 100644
--- a/maven-surefire-report-plugin/pom.xml
+++ b/maven-surefire-report-plugin/pom.xml
@@ -179,8 +179,35 @@
                 </executions>
             </plugin>
             <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>jacoco-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <!--todo remove-->
+                        <id>jacoco-report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <propertyName>jacoco.agent</propertyName>
+                </configuration>
+            </plugin>
+            <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
                 <configuration>
+                    <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
+                    <includes>
+                        <include>**/JUnit4SuiteTest.java</include>
+                    </includes>
                     <classpathDependencyExcludes>
                         <classpathDependencyExclude>org.fusesource.jansi:jansi</classpathDependencyExclude>
                     </classpathDependencyExcludes>
diff --git a/surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/JUnit4SuiteTest.java b/maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/JUnit4SuiteTest.java
similarity index 70%
copy from surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/JUnit4SuiteTest.java
copy to maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/JUnit4SuiteTest.java
index 142ca9d..5ab3ee1 100644
--- a/surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/JUnit4SuiteTest.java
+++ b/maven-surefire-report-plugin/src/test/java/org/apache/maven/plugins/surefire/report/JUnit4SuiteTest.java
@@ -21,26 +21,24 @@ package org.apache.maven.plugins.surefire.report;
 
 import junit.framework.JUnit4TestAdapter;
 import junit.framework.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
 
 /**
  * Adapt the JUnit4 tests which use only annotations to the JUnit3 test suite.
  *
  * @author Tibor Digana (tibor17)
- * @since 2.21.0
+ * @since 3.0.0-M4
  */
-@Suite.SuiteClasses( {
-        ReportTestCaseTest.class,
-        ReportTestSuiteTest.class,
-        SurefireReportParserTest.class,
-        TestSuiteXmlParserTest.class
-} )
-@RunWith( Suite.class )
-public class JUnit4SuiteTest
+public class JUnit4SuiteTest extends TestCase
 {
     public static Test suite()
     {
-        return new JUnit4TestAdapter( JUnit4SuiteTest.class );
+        TestSuite suite = new TestSuite();
+        suite.addTest( new JUnit4TestAdapter( Surefire597Test.class ) );
+        suite.addTest( new JUnit4TestAdapter( SurefireSchemaValidationTest.class ) );
+        suite.addTestSuite( Surefire1183Test.class );
+        suite.addTestSuite(  SurefireReportMojoTest.class);
+        return suite;
     }
 }
diff --git a/pom.xml b/pom.xml
index e30a333..8469f31 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,6 +50,7 @@
   <modules>
     <module>surefire-logger-api</module>
     <module>surefire-api</module>
+    <module>surefire-extensions-api</module>
     <module>surefire-booter</module>
     <module>surefire-grouper</module>
     <module>surefire-providers</module>
@@ -93,7 +94,8 @@
     <doxiaSitetoolsVersion>1.8.1</doxiaSitetoolsVersion>
     <!-- maven-shared-utils:3.2.0+ another behavior - broke Surefire performance - end of subprocess notification not arrived in ForkStarter -->
     <mavenSharedUtilsVersion>3.1.0</mavenSharedUtilsVersion>
-    <powermockVersion>2.0.0-RC.1</powermockVersion>
+    <powermockVersion>2.0.2</powermockVersion>
+    <jacocoVersion>0.8.4</jacocoVersion>
     <maven.surefire.scm.devConnection>scm:git:https://gitbox.apache.org/repos/asf/maven-surefire.git</maven.surefire.scm.devConnection>
     <maven.site.path>surefire-archives/surefire-LATEST</maven.site.path>
     <!-- Override with Jigsaw JRE 9 -->
@@ -330,7 +332,7 @@
       <dependency>
         <groupId>org.mockito</groupId>
         <artifactId>mockito-core</artifactId>
-        <version>2.21.0</version>
+        <version>2.27.0</version>
         <exclusions>
           <exclusion>
             <groupId>org.hamcrest</groupId>
@@ -357,6 +359,17 @@
         <artifactId>powermock-api-mockito2</artifactId>
         <version>${powermockVersion}</version>
       </dependency>
+      <dependency>
+        <groupId>org.powermock</groupId>
+        <artifactId>powermock-reflect</artifactId>
+        <version>${powermockVersion}</version>
+        <scope>compile</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.javassist</groupId>
+        <artifactId>javassist</artifactId>
+        <version>3.22.0-GA</version>
+      </dependency>
       <!-- END: PowerMock@Java9 -->
       <dependency>
         <groupId>junit</groupId>
@@ -377,12 +390,19 @@
         <groupId>org.assertj</groupId>
         <artifactId>assertj-core</artifactId>
         <version>3.9.1</version>
+<!--        <version>3.12.2</version>-->
       </dependency>
       <dependency>
         <groupId>com.google.code.findbugs</groupId>
         <artifactId>jsr305</artifactId>
         <version>2.0.3</version>
       </dependency>
+      <dependency>
+        <groupId>org.jacoco</groupId>
+        <artifactId>org.jacoco.agent</artifactId>
+        <classifier>runtime</classifier>
+        <version>${jacocoVersion}</version>
+      </dependency>
     </dependencies>
   </dependencyManagement>
   <dependencies>
@@ -464,6 +484,7 @@
             <compilerArgs>
               <arg>-Xdoclint:all</arg>
             </compilerArgs>
+            <encoding>UTF-8</encoding>
           </configuration>
         </plugin>
         <!-- NOTE: animal sniffer does not check test classes: https://jira.codehaus.org/browse/MANIMALSNIFFER-40 -->
@@ -471,21 +492,6 @@
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>animal-sniffer-maven-plugin</artifactId>
           <version>1.17</version>
-          <executions>
-            <execution>
-              <id>signature-check</id>
-              <goals>
-                <goal>check</goal>
-              </goals>
-              <configuration>
-                <signature>
-                  <groupId>org.codehaus.mojo.signature</groupId>
-                  <artifactId>java17</artifactId>
-                  <version>1.0</version>
-                </signature>
-              </configuration>
-            </execution>
-          </executions>
         </plugin>
         <plugin>
           <artifactId>maven-surefire-plugin</artifactId>
@@ -493,7 +499,7 @@
           <configuration>
             <!-- NOTE: Be sure to isolate the Surefire version under test from the version running the tests! -->
             <useSystemClassLoader>false</useSystemClassLoader>
-            <argLine>${jvm.args.tests} ${jacoco.agent} -Dnet.bytebuddy.experimental=true</argLine>
+            <argLine>${jvm.args.tests}</argLine><!-- -Dnet.bytebuddy.experimental=true ${jacoco.agent}-->
             <useFile>false</useFile>
             <redirectTestOutputToFile>false</redirectTestOutputToFile>
             <jvm>${jdk.home}/bin/java</jvm>
@@ -519,35 +525,65 @@
         <plugin>
           <groupId>org.jacoco</groupId>
           <artifactId>jacoco-maven-plugin</artifactId>
-          <version>0.8.3</version>
+          <version>${jacocoVersion}</version>
+          <configuration>
+            <!--<append>true</append>
+            <inclNoLocationClasses>true</inclNoLocationClasses>
+            <haltOnFailure>false</haltOnFailure>
+            <jmx>false</jmx>-->
+            <includes>
+              <include>**/failsafe/*</include>
+              <include>**/failsafe/**/*</include>
+              <include>**/surefire/*</include>
+              <include>**/surefire/**/*</include>
+            </includes>
+            <excludes>
+              <exclude>**/HelpMojo.class</exclude>
+              <exclude>**/shadefire/**/*</exclude>
+              <exclude>org/jacoco/**/*</exclude>
+              <exclude>com/vladium/emma/rt/*</exclude>
+            </excludes>
+          </configuration>
         </plugin>
       </plugins>
     </pluginManagement>
     <plugins>
       <plugin>
-        <groupId>org.jacoco</groupId>
-        <artifactId>jacoco-maven-plugin</artifactId>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
         <executions>
           <execution>
-            <id>jacoco-agent</id>
+            <id>rat-check</id>
             <goals>
-              <goal>prepare-agent</goal>
+              <goal>check</goal>
             </goals>
+            <configuration>
+              <excludes combine.children="append">
+                <exclude>Jenkinsfile</exclude>
+                <exclude>README.md</exclude>
+                <exclude>.gitignore</exclude>
+                <exclude>.git/**/*</exclude>
+                <exclude>**/.idea</exclude>
+                <exclude>**/.svn/**/*</exclude>
+                <exclude>**/*.iml</exclude>
+                <exclude>**/*.ipr</exclude>
+                <exclude>**/*.iws</exclude>
+                <exclude>**/*.versionsBackup</exclude>
+                <exclude>**/dependency-reduced-pom.xml</exclude>
+                <exclude>.repository/**</exclude> <!-- jenkins with local maven repository -->
+                <exclude>src/test/resources/**/*</exclude>
+                <exclude>src/test/resources/**/*.css</exclude>
+                <exclude>**/*.jj</exclude>
+                <exclude>src/main/resources/META-INF/services/org.apache.maven.surefire.providerapi.SurefireProvider
+                </exclude>
+                <exclude>DEPENDENCIES</exclude>
+                <exclude>.m2/**</exclude>
+                <exclude>.m2</exclude>
+                <exclude>.travis.yml</exclude>
+              </excludes>
+            </configuration>
           </execution>
         </executions>
-        <configuration>
-          <propertyName>jacoco.agent</propertyName>
-          <append>true</append>
-          <inclNoLocationClasses>true</inclNoLocationClasses>
-          <haltOnFailure>false</haltOnFailure>
-          <jmx>false</jmx>
-          <includes>
-            <include>**/failsafe/*</include>
-            <include>**/failsafe/**/*</include>
-            <include>**/surefire/*</include>
-            <include>**/surefire/**/*</include>
-          </includes>
-        </configuration>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
@@ -606,45 +642,27 @@
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>animal-sniffer-maven-plugin</artifactId>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.rat</groupId>
-        <artifactId>apache-rat-plugin</artifactId>
         <executions>
           <execution>
-            <id>rat-check</id>
+            <id>signature-check</id>
             <goals>
               <goal>check</goal>
             </goals>
             <configuration>
-              <excludes combine.children="append">
-                <exclude>Jenkinsfile</exclude>
-                <exclude>README.md</exclude>
-                <exclude>.gitignore</exclude>
-                <exclude>.git/**/*</exclude>
-                <exclude>**/.idea</exclude>
-                <exclude>**/.svn/**/*</exclude>
-                <exclude>**/*.iml</exclude>
-                <exclude>**/*.ipr</exclude>
-                <exclude>**/*.iws</exclude>
-                <exclude>**/*.versionsBackup</exclude>
-                <exclude>**/dependency-reduced-pom.xml</exclude>
-                <exclude>.repository/**</exclude> <!-- jenkins with local maven repository -->
-                <exclude>src/test/resources/**/*</exclude>
-                <exclude>src/test/resources/**/*.css</exclude>
-                <exclude>**/*.jj</exclude>
-                <exclude>src/main/resources/META-INF/services/org.apache.maven.surefire.providerapi.SurefireProvider
-                </exclude>
-                <exclude>DEPENDENCIES</exclude>
-                <exclude>.m2/**</exclude>
-                <exclude>.m2</exclude>
-                <exclude>.travis.yml</exclude>
-              </excludes>
+              <signature>
+                <groupId>org.codehaus.mojo.signature</groupId>
+                <artifactId>java17</artifactId>
+                <version>1.0</version>
+              </signature>
             </configuration>
           </execution>
         </executions>
       </plugin>
       <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+      <plugin>
         <artifactId>maven-deploy-plugin</artifactId>
         <configuration>
           <deployAtEnd>true</deployAtEnd>
diff --git a/surefire-api/pom.xml b/surefire-api/pom.xml
index c42041b..e791c9d 100644
--- a/surefire-api/pom.xml
+++ b/surefire-api/pom.xml
@@ -60,8 +60,32 @@
   <build>
     <plugins>
       <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>jacoco-agent</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <!--todo remove-->
+            <id>jacoco-report</id>
+            <phase>prepare-package</phase>
+            <goals>
+              <goal>report</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <propertyName>jacoco.agent</propertyName>
+        </configuration>
+      </plugin>
+      <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
+          <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
           <includes>
             <include>**/JUnit4SuiteTest.java</include>
           </includes>
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkedChannelEncoder.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkedChannelEncoder.java
index 9a46fca..f66e137 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkedChannelEncoder.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkedChannelEncoder.java
@@ -328,8 +328,12 @@ public final class ForkedChannelEncoder
                 .append( ':' )
                 .append( toBase64( reportEntry.getSourceName() ) )
                 .append( ':' )
+                .append( toBase64( reportEntry.getSourceText() ) )
+                .append( ':' )
                 .append( toBase64( reportEntry.getName() ) )
                 .append( ':' )
+                .append( toBase64( reportEntry.getNameText() ) )
+                .append( ':' )
                 .append( toBase64( reportEntry.getGroup() ) )
                 .append( ':' )
                 .append( toBase64( reportEntry.getMessage() ) )
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
index 360c802..5cc1415 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
@@ -34,10 +34,8 @@ import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.RunOrder;
 import org.apache.maven.surefire.util.SurefireReflectionException;
 
-import javax.annotation.Nonnull;
 import java.io.File;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -147,32 +145,9 @@ public class SurefireReflector
         int getSkipped = (Integer) invokeGetter( result, "getSkipped" );
         int getFailures = (Integer) invokeGetter( result, "getFailures" );
         return new RunResult( getCompletedCount1, getErrors, getFailures, getSkipped );
-
-    }
-
-    class ClassLoaderProxy
-        implements InvocationHandler
-    {
-        private final Object target;
-
-        /**
-         * @param delegate a target
-         */
-        ClassLoaderProxy( Object delegate )
-        {
-            this.target = delegate;
-        }
-
-        @Override
-        public Object invoke( Object proxy, Method method, Object[] args )
-            throws Throwable
-        {
-            Method delegateMethod = target.getClass().getMethod( method.getName(), method.getParameterTypes() );
-            return delegateMethod.invoke( target, args );
-        }
     }
 
-    Object createTestRequest( TestRequest suiteDefinition )
+    private Object createTestRequest( TestRequest suiteDefinition )
     {
         if ( suiteDefinition == null )
         {
@@ -191,7 +166,7 @@ public class SurefireReflector
         }
     }
 
-    Object createTestListResolver( TestListResolver resolver )
+    private Object createTestListResolver( TestListResolver resolver )
     {
         if ( resolver == null )
         {
@@ -204,7 +179,7 @@ public class SurefireReflector
         }
     }
 
-    Object createDirectoryScannerParameters( DirectoryScannerParameters directoryScannerParameters )
+    private Object createDirectoryScannerParameters( DirectoryScannerParameters directoryScannerParameters )
     {
         if ( directoryScannerParameters == null )
         {
@@ -222,8 +197,7 @@ public class SurefireReflector
                             RunOrder.asString( directoryScannerParameters.getRunOrder() ) );
     }
 
-
-    Object createRunOrderParameters( RunOrderParameters runOrderParameters )
+    private Object createRunOrderParameters( RunOrderParameters runOrderParameters )
     {
         if ( runOrderParameters == null )
         {
@@ -236,7 +210,7 @@ public class SurefireReflector
         return newInstance( constructor, RunOrder.asString( runOrderParameters.getRunOrder() ), runStatisticsFile );
     }
 
-    Object createTestArtifactInfo( TestArtifactInfo testArtifactInfo )
+    private Object createTestArtifactInfo( TestArtifactInfo testArtifactInfo )
     {
         if ( testArtifactInfo == null )
         {
@@ -247,7 +221,7 @@ public class SurefireReflector
         return newInstance( constructor, testArtifactInfo.getVersion(), testArtifactInfo.getClassifier() );
     }
 
-    Object createReporterConfiguration( ReporterConfiguration reporterConfig )
+    private Object createReporterConfiguration( ReporterConfiguration reporterConfig )
     {
         Constructor constructor = getConstructor( reporterConfiguration, File.class, boolean.class );
         return newInstance( constructor, reporterConfig.getReportsDirectory(), reporterConfig.isTrimStackTrace() );
@@ -315,7 +289,7 @@ public class SurefireReflector
         invokeSetter( o, "setSystemExitTimeout", Integer.class, systemExitTimeout );
     }
 
-    public void setDirectoryScannerParameters( Object o, DirectoryScannerParameters dirScannerParams )
+    void setDirectoryScannerParameters( Object o, DirectoryScannerParameters dirScannerParams )
     {
         Object param = createDirectoryScannerParameters( dirScannerParams );
         invokeSetter( o, "setDirectoryScannerParameters", directoryScannerParameters, param );
@@ -362,8 +336,7 @@ public class SurefireReflector
         }
     }
 
-
-    void setReporterConfiguration( Object o, ReporterConfiguration reporterConfiguration )
+    private void setReporterConfiguration( Object o, ReporterConfiguration reporterConfiguration )
     {
         Object param = createReporterConfiguration( reporterConfiguration );
         invokeSetter( o, "setReporterConfiguration", this.reporterConfiguration, param );
@@ -402,11 +375,6 @@ public class SurefireReflector
         return runResult.isAssignableFrom( o.getClass() );
     }
 
-    public Object createConsoleLogger( @Nonnull ConsoleLogger consoleLogger )
-    {
-        return createConsoleLogger( consoleLogger, surefireClassLoader );
-    }
-
     private static Collection<Integer> toOrdinals( Collection<? extends Enum> enums )
     {
         Collection<Integer> ordinals = new ArrayList<>( enums.size() );
@@ -429,5 +397,4 @@ public class SurefireReflector
             throw new SurefireReflectionException( e );
         }
     }
-
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/report/CategorizedReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/report/CategorizedReportEntry.java
index 226999e..c35d6d5 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/report/CategorizedReportEntry.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/report/CategorizedReportEntry.java
@@ -21,6 +21,7 @@ package org.apache.maven.surefire.report;
 
 import java.util.Collections;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * @author Kristian Rosenvold
@@ -28,7 +29,7 @@ import java.util.Map;
 public class CategorizedReportEntry
     extends SimpleReportEntry
 {
-    private static final String GROUP_PREFIX = " (of ";
+    public static final String GROUP_PREFIX = " (of ";
 
     private static final String GROUP_SUFIX = ")";
 
@@ -42,30 +43,35 @@ public class CategorizedReportEntry
     public CategorizedReportEntry( String source, String name, String group, StackTraceWriter stackTraceWriter,
                                    Integer elapsed )
     {
-        super( source, name, stackTraceWriter, elapsed );
+        super( source, null, name, null, stackTraceWriter, elapsed );
         this.group = group;
     }
 
     public CategorizedReportEntry( String source, String name, String group, StackTraceWriter stackTraceWriter,
                                    Integer elapsed, String message )
     {
-        this( source, name, group, stackTraceWriter, elapsed, message, Collections.<String, String>emptyMap() );
+        this( source, null, name, null,
+                group, stackTraceWriter, elapsed, message, Collections.<String, String>emptyMap() );
     }
 
-    public CategorizedReportEntry( String source, String name, String group, StackTraceWriter stackTraceWriter,
+    public CategorizedReportEntry( String source, String sourceText, String name, String nameText,
+                                   String group, StackTraceWriter stackTraceWriter,
                                    Integer elapsed, String message, Map<String, String> systemProperties )
     {
-        super( source, name, stackTraceWriter, elapsed, message, systemProperties );
+        super( source, sourceText, name, nameText, stackTraceWriter, elapsed, message, systemProperties );
         this.group = group;
     }
 
-    public static TestSetReportEntry reportEntry( String source, String name, String group,
+    public static TestSetReportEntry reportEntry( 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, name, group, stackTraceWriter, elapsed, message, systemProperties )
-            : new SimpleReportEntry( source, name, stackTraceWriter, elapsed, message, systemProperties );
+            ? new CategorizedReportEntry( source, sourceText, name, nameText,
+                group, stackTraceWriter, elapsed, message, systemProperties )
+            : new SimpleReportEntry( source, sourceText, name, nameText,
+                stackTraceWriter, elapsed, message, systemProperties );
     }
 
     @Override
@@ -81,6 +87,12 @@ public class CategorizedReportEntry
     }
 
     @Override
+    public String getReportNameWithGroup()
+    {
+        return isNameWithGroup() ? getSourceText() + GROUP_PREFIX + getGroup() + GROUP_SUFIX : getSourceText();
+    }
+
+    @Override
     public boolean equals( Object o )
     {
         if ( this == o )
@@ -98,8 +110,7 @@ public class CategorizedReportEntry
 
         CategorizedReportEntry that = (CategorizedReportEntry) o;
 
-        return !( group != null ? !group.equals( that.group ) : that.group != null );
-
+        return Objects.equals( group, that.group );
     }
 
     @Override
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/report/ReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/report/ReportEntry.java
index 6e4a04a..6bc9618 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/report/ReportEntry.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/report/ReportEntry.java
@@ -33,6 +33,13 @@ public interface ReportEntry
     String getSourceName();
 
     /**
+     * Human readable {@link #getSourceName() test class}.
+     *
+     * @return source text
+     */
+    String getSourceText();
+
+    /**
      * The name of the test case
      *
      * @return A string describing the test case
@@ -40,6 +47,13 @@ public interface ReportEntry
     String getName();
 
     /**
+     * Human readable {@link #getName() test case}.
+     *
+     * @return name text
+     */
+    String getNameText();
+
+    /**
      * The group/category of the testcase
      *
      * @return A string
@@ -84,4 +98,11 @@ public interface ReportEntry
      * @return A string with the test case name and group/category, or just the name.
      */
     String getNameWithGroup();
+
+    /**
+     * A source text of the test case together with the group or category (if any exists).
+     *
+     * @return A string with the test case text and group/category, or just the source text.
+     */
+    String getReportNameWithGroup();
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
index 73e5f3b..df7b4c4 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
@@ -35,79 +35,88 @@ public class SimpleReportEntry
 
     private final String source;
 
+    private final String sourceText;
+
     private final String name;
 
+    private final String nameText;
+
     private final StackTraceWriter stackTraceWriter;
 
     private final Integer elapsed;
 
     private final String message;
 
-    public SimpleReportEntry()
-    {
-        this( null, null );
-    }
-
-    public SimpleReportEntry( String source, String name )
+    public SimpleReportEntry( String source, String sourceText, String name, String nameText )
     {
-        this( source, name, null, null );
+        this( source, sourceText, name, nameText, null, null );
     }
 
-    public SimpleReportEntry( String source, String name, Map<String, String> systemProperties )
+    public SimpleReportEntry( String source, String sourceText, String name, String nameText,
+                              Map<String, String> systemProperties )
     {
-        this( source, name, null, null, systemProperties );
+        this( source, sourceText, name, nameText, null, null, systemProperties );
     }
 
-    private SimpleReportEntry( String source, String name, StackTraceWriter stackTraceWriter )
+    private SimpleReportEntry( String source, String sourceText, String name, String nameText,
+                               StackTraceWriter stackTraceWriter )
     {
-        this( source, name, stackTraceWriter, null );
+        this( source, sourceText, name, nameText, stackTraceWriter, null );
     }
 
-    public SimpleReportEntry( String source, String name, Integer elapsed )
+    public SimpleReportEntry( String source, String sourceText, String name, String nameText, Integer elapsed )
     {
-        this( source, name, null, elapsed );
+        this( source, sourceText, name, nameText, null, elapsed );
     }
 
-    public SimpleReportEntry( String source, String name, String message )
+    public SimpleReportEntry( String source, String sourceText, String name, String nameText, String message )
     {
-        this( source, name, null, null, message, Collections.<String, String>emptyMap() );
+        this( source, sourceText, name, nameText, null, null, message, Collections.<String, String>emptyMap() );
     }
 
-    protected SimpleReportEntry( String source, String name, StackTraceWriter stackTraceWriter, Integer elapsed,
-                                 String message, Map<String, String> systemProperties )
+    public SimpleReportEntry( String source, String sourceText, String name, String nameText,
+                                 StackTraceWriter stackTraceWriter, Integer elapsed, String message,
+                                 Map<String, String> systemProperties )
     {
         this.source = source;
+        this.sourceText = sourceText;
         this.name = name;
+        this.nameText = nameText;
         this.stackTraceWriter = stackTraceWriter;
         this.message = message;
         this.elapsed = elapsed;
         this.systemProperties = new ImmutableMap<>( systemProperties );
     }
 
-    public SimpleReportEntry( String source, String name, StackTraceWriter stackTraceWriter, Integer elapsed )
+    public SimpleReportEntry( String source, String sourceText, String name, String nameText,
+                              StackTraceWriter stackTraceWriter, Integer elapsed )
     {
-        this( source, name, stackTraceWriter, elapsed, Collections.<String, String>emptyMap() );
+        this( source, sourceText, name, nameText, stackTraceWriter, elapsed, Collections.<String, String>emptyMap() );
     }
 
-    public SimpleReportEntry( String source, String name, StackTraceWriter stackTraceWriter, Integer elapsed,
-                              Map<String, String> systemProperties )
+    public SimpleReportEntry( String source, String sourceText, String name, String nameText,
+                              StackTraceWriter stackTraceWriter, Integer elapsed, Map<String, String> systemProperties )
     {
-        this( source, name, stackTraceWriter, elapsed, safeGetMessage( stackTraceWriter ), systemProperties );
+        this( source, sourceText, name, nameText,
+                stackTraceWriter, elapsed, safeGetMessage( stackTraceWriter ), systemProperties );
     }
 
-    public static SimpleReportEntry assumption( String source, String name, String message )
+    public static SimpleReportEntry assumption( String source, String sourceText, String name, String nameText,
+                                                String message )
     {
-        return new SimpleReportEntry( source, name, message );
+        return new SimpleReportEntry( source, sourceText, name, nameText, message );
     }
 
-    public static SimpleReportEntry ignored( String source, String name, String message )
+    public static SimpleReportEntry ignored( String source, String sourceText, String name, String nameText,
+                                             String message )
     {
-        return new SimpleReportEntry( source, name, message );
+        return new SimpleReportEntry( source, sourceText, name, nameText, message );
     }
 
-    public static SimpleReportEntry withException( String source, String name, StackTraceWriter stackTraceWriter )
+    public static SimpleReportEntry withException( String source, String sourceText, String name, String nameText,
+                                                   StackTraceWriter stackTraceWriter )
     {
-        return new SimpleReportEntry( source, name, stackTraceWriter );
+        return new SimpleReportEntry( source, sourceText, name, nameText, stackTraceWriter );
     }
 
     private static String safeGetMessage( StackTraceWriter stackTraceWriter )
@@ -130,12 +139,24 @@ public class SimpleReportEntry
     }
 
     @Override
+    public String getSourceText()
+    {
+        return sourceText;
+    }
+
+    @Override
     public String getName()
     {
         return name;
     }
 
     @Override
+    public String getNameText()
+    {
+        return nameText;
+    }
+
+    @Override
     public String getGroup()
     {
         return null;
@@ -162,8 +183,9 @@ public class SimpleReportEntry
     @Override
     public String toString()
     {
-        return "ReportEntry{" + "source='" + source + '\'' + ", name='" + name + '\'' + ", stackTraceWriter="
-            + stackTraceWriter + ", elapsed=" + elapsed + ", message=" + message + '}';
+        return "ReportEntry{" + "source='" + source + "', sourceText='" + sourceText
+                + "', name='" + name + "', nameText='" + nameText + "', stackTraceWriter='"
+                + stackTraceWriter + "', elapsed='" + elapsed + "', message='" + message + "'}";
     }
 
     @Override
@@ -185,16 +207,25 @@ public class SimpleReportEntry
         }
 
         SimpleReportEntry that = (SimpleReportEntry) o;
-        return isElapsedTimeEqual( that ) && isNameEqual( that ) && isSourceEqual( that ) && isStackEqual( that );
+        return isSourceEqual( that ) && isSourceTextEqual( that )
+                && isNameEqual( that ) && isNameTextEqual( that )
+                && isStackEqual( that )
+                && isElapsedTimeEqual( that )
+                && isSystemPropertiesEqual( that )
+                && isMessageEqual( that );
     }
 
     @Override
     public int hashCode()
     {
-        int result = Objects.hashCode( source );
-        result = 31 * result + Objects.hashCode( name );
-        result = 31 * result + Objects.hashCode( stackTraceWriter );
-        result = 31 * result + Objects.hashCode( elapsed );
+        int result = Objects.hashCode( getSourceName() );
+        result = 31 * result + Objects.hashCode( getSourceText() );
+        result = 31 * result + Objects.hashCode( getName() );
+        result = 31 * result + Objects.hashCode( getNameText() );
+        result = 31 * result + Objects.hashCode( getStackTraceWriter() );
+        result = 31 * result + Objects.hashCode( getElapsed() );
+        result = 31 * result + Objects.hashCode( getSystemProperties() );
+        result = 31 * result + Objects.hashCode( getMessage() );
         return result;
     }
 
@@ -205,6 +236,12 @@ public class SimpleReportEntry
     }
 
     @Override
+    public String getReportNameWithGroup()
+    {
+        return getSourceText();
+    }
+
+    @Override
     public Map<String, String> getSystemProperties()
     {
         return systemProperties;
@@ -212,21 +249,41 @@ public class SimpleReportEntry
 
     private boolean isElapsedTimeEqual( SimpleReportEntry en )
     {
-        return Objects.equals( elapsed, en.elapsed );
+        return Objects.equals( getElapsed(), en.getElapsed() );
+    }
+
+    private boolean isNameTextEqual( SimpleReportEntry en )
+    {
+        return Objects.equals( getNameText(), en.getNameText() );
     }
 
     private boolean isNameEqual( SimpleReportEntry en )
     {
-        return Objects.equals( name, en.name );
+        return Objects.equals( getName(), en.getName() );
     }
 
     private boolean isSourceEqual( SimpleReportEntry en )
     {
-        return Objects.equals( source, en.source );
+        return Objects.equals( getSourceName(), en.getSourceName() );
+    }
+
+    private boolean isSourceTextEqual( SimpleReportEntry en )
+    {
+        return Objects.equals( getSourceText(), en.getSourceText() );
     }
 
     private boolean isStackEqual( SimpleReportEntry en )
     {
-        return Objects.equals( stackTraceWriter, en.stackTraceWriter );
+        return Objects.equals( getStackTraceWriter(), en.getStackTraceWriter() );
+    }
+
+    private boolean isSystemPropertiesEqual( SimpleReportEntry en )
+    {
+        return Objects.equals( getSystemProperties(), en.getSystemProperties() );
+    }
+
+    private boolean isMessageEqual( SimpleReportEntry en )
+    {
+        return Objects.equals( getMessage(), en.getMessage() );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java
index 63b30ff..57e9ea7 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java
@@ -223,6 +223,12 @@ public final class ReflectionUtils
         }
     }
 
+    public static Class<?> reloadClass( ClassLoader classLoader, Object source )
+            throws ReflectiveOperationException
+    {
+        return classLoader.loadClass( source.getClass().getName() );
+    }
+
     /**
      * Invoker of public static no-argument method.
      *
diff --git a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
index 63c4490..38f0c48 100644
--- a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
@@ -23,6 +23,7 @@ import junit.framework.JUnit4TestAdapter;
 import junit.framework.Test;
 import org.apache.maven.plugin.surefire.runorder.ThreadedExecutionSchedulerTest;
 import org.apache.maven.surefire.SpecificTestClassFilterTest;
+import org.apache.maven.surefire.booter.CommandReaderTest;
 import org.apache.maven.surefire.booter.ForkedChannelEncoderTest;
 import org.apache.maven.surefire.booter.ForkingRunListenerTest;
 import org.apache.maven.surefire.booter.MasterProcessCommandTest;
@@ -50,6 +51,7 @@ import org.junit.runners.Suite;
  * @since 2.19
  */
 @Suite.SuiteClasses( {
+    CommandReaderTest.class,
     ThreadedExecutionSchedulerTest.class,
     ForkingRunListenerTest.class,
     MasterProcessCommandTest.class,
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
similarity index 98%
rename from surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
rename to surefire-api/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
index 00d50b4..5168d2b 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
@@ -193,14 +193,14 @@ public class CommandReaderTest
                 Iterator<String> it = reader.getIterableClasses( new ForkedChannelEncoder( nul() ) ).iterator();
                 assertThat( it.next(), is( CommandReaderTest.class.getName() ) );
                 counter.countDown();
-                assertThat( it.next(), is( PropertiesWrapperTest.class.getName() ) );
+                assertThat( it.next(), is( Foo.class.getName() ) );
             }
         };
         FutureTask<Object> futureTask = new FutureTask<>( runnable, null );
         Thread t = new Thread( futureTask );
         t.start();
         counter.await();
-        addTestToPipeline( PropertiesWrapperTest.class.getName() );
+        addTestToPipeline( Foo.class.getName() );
         try
         {
             futureTask.get();
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/Foo.java b/surefire-api/src/test/java/org/apache/maven/surefire/booter/Foo.java
similarity index 100%
rename from surefire-booter/src/test/java/org/apache/maven/surefire/booter/Foo.java
rename to surefire-api/src/test/java/org/apache/maven/surefire/booter/Foo.java
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/booter/ForkedChannelEncoderTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/booter/ForkedChannelEncoderTest.java
index b9708b0..4429f79 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/booter/ForkedChannelEncoderTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/booter/ForkedChannelEncoderTest.java
@@ -179,43 +179,51 @@ public class ForkedChannelEncoderTest
         StringBuilder encode = encode( "X", "normal-run", reportEntry, false );
         assertThat( encode.toString() )
                 .isEqualTo( ":maven:surefire:std:out:X:normal-run:UTF-8:"
-                                    + encodedSourceName
-                                    + ":"
-                                    + encodedName
-                                    + ":"
-                                    + encodedGroup
-                                    + ":"
-                                    + encodedMessage
-                                    + ":"
-                                    + 102
-                                    + ":"
-
-                                    + encodedExceptionMsg
-                                    + ":"
-                                    + encodedSmartStackTrace
-                                    + ":"
-                                    + encodedStackTrace
+                                + encodedSourceName
+                                + ":"
+                                + "-"
+                                + ":"
+                                + encodedName
+                                + ":"
+                                + "-"
+                                + ":"
+                                + encodedGroup
+                                + ":"
+                                + encodedMessage
+                                + ":"
+                                + 102
+                                + ":"
+
+                                + encodedExceptionMsg
+                                + ":"
+                                + encodedSmartStackTrace
+                                + ":"
+                                + encodedStackTrace
                 );
 
         encode = encode( "X", "normal-run", reportEntry, true );
         assertThat( encode.toString() )
                 .isEqualTo( ":maven:surefire:std:out:X:normal-run:UTF-8:"
-                                    + encodedSourceName
-                                    + ":"
-                                    + encodedName
-                                    + ":"
-                                    + encodedGroup
-                                    + ":"
-                                    + encodedMessage
-                                    + ":"
-                                    + 102
-                                    + ":"
-
-                                    + encodedExceptionMsg
-                                    + ":"
-                                    + encodedSmartStackTrace
-                                    + ":"
-                                    + encodedTrimmedStackTrace
+                                + encodedSourceName
+                                + ":"
+                                + "-"
+                                + ":"
+                                + encodedName
+                                + ":"
+                                + "-"
+                                + ":"
+                                + encodedGroup
+                                + ":"
+                                + encodedMessage
+                                + ":"
+                                + 102
+                                + ":"
+
+                                + encodedExceptionMsg
+                                + ":"
+                                + encodedSmartStackTrace
+                                + ":"
+                                + encodedTrimmedStackTrace
                 );
 
         Stream out = Stream.newStream();
@@ -227,8 +235,12 @@ public class ForkedChannelEncoderTest
                 .isEqualTo( ":maven:surefire:std:out:testset-starting:normal-run:UTF-8:"
                                     + encodedSourceName
                                     + ":"
+                                    + "-"
+                                    + ":"
                                     + encodedName
                                     + ":"
+                                    + "-"
+                                    + ":"
                                     + encodedGroup
                                     + ":"
                                     + encodedMessage
@@ -253,8 +265,12 @@ public class ForkedChannelEncoderTest
                 .isEqualTo( ":maven:surefire:std:out:testset-starting:normal-run:UTF-8:"
                                     + encodedSourceName
                                     + ":"
+                                    + "-"
+                                    + ":"
                                     + encodedName
                                     + ":"
+                                    + "-"
+                                    + ":"
                                     + encodedGroup
                                     + ":"
                                     + encodedMessage
@@ -316,8 +332,12 @@ public class ForkedChannelEncoderTest
                 .isEqualTo( ":maven:surefire:std:out:testset-completed:normal-run:UTF-8:"
                         + encodedSourceName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedGroup
                         + ":"
                         + encodedMessage
@@ -379,8 +399,12 @@ public class ForkedChannelEncoderTest
                 .isEqualTo( ":maven:surefire:std:out:test-starting:normal-run:UTF-8:"
                         + encodedSourceName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedGroup
                         + ":"
                         + encodedMessage
@@ -442,8 +466,12 @@ public class ForkedChannelEncoderTest
                 .isEqualTo( ":maven:surefire:std:out:test-succeeded:normal-run:UTF-8:"
                         + encodedSourceName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedGroup
                         + ":"
                         + encodedMessage
@@ -505,8 +533,12 @@ public class ForkedChannelEncoderTest
                 .isEqualTo( ":maven:surefire:std:out:test-failed:normal-run:UTF-8:"
                         + encodedSourceName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedGroup
                         + ":"
                         + encodedMessage
@@ -567,8 +599,12 @@ public class ForkedChannelEncoderTest
                 .isEqualTo( ":maven:surefire:std:out:test-skipped:normal-run:UTF-8:"
                         + encodedSourceName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedGroup
                         + ":"
                         + encodedMessage
@@ -628,8 +664,12 @@ public class ForkedChannelEncoderTest
                 .isEqualTo( ":maven:surefire:std:out:test-error:normal-run:UTF-8:"
                         + encodedSourceName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedGroup
                         + ":"
                         + encodedMessage
@@ -687,8 +727,12 @@ public class ForkedChannelEncoderTest
                 .isEqualTo( ":maven:surefire:std:out:test-assumption-failure:normal-run:UTF-8:"
                         + encodedSourceName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedName
                         + ":"
+                        + "-"
+                        + ":"
                         + encodedGroup
                         + ":"
                         + encodedMessage
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/NewClassLoaderRunner.java b/surefire-api/src/test/java/org/apache/maven/surefire/booter/NewClassLoaderRunner.java
similarity index 97%
rename from surefire-booter/src/test/java/org/apache/maven/surefire/booter/NewClassLoaderRunner.java
rename to surefire-api/src/test/java/org/apache/maven/surefire/booter/NewClassLoaderRunner.java
index 2e71dc7..f137598 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/NewClassLoaderRunner.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/booter/NewClassLoaderRunner.java
@@ -19,6 +19,7 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
+import org.apache.maven.shared.utils.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -45,8 +46,6 @@ import java.util.HashSet;
 import java.util.List;
 
 import static java.io.File.pathSeparator;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.commons.io.FileUtils.readFileToString;
 
 /**
  * JUnit runner testing methods in a separate class loader.
@@ -193,7 +192,7 @@ public class NewClassLoaderRunner
     public static class TestClassLoader
         extends URLClassLoader
     {
-        public TestClassLoader()
+        TestClassLoader()
         {
             super( toClassPath(), null );
         }
@@ -236,7 +235,7 @@ public class NewClassLoaderRunner
             Collection<URL> classPath = new HashSet<>();
             try
             {
-                String[] files = readFileToString( new File( "target/test-classpath/cp.txt" ), UTF_8 )
+                String[] files = FileUtils.fileRead( new File( "target/test-classpath/cp.txt" ), "UTF-8" )
                         .split( pathSeparator );
                 for ( String file : files )
                 {
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
index 8394e00..cbd2d16 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
@@ -19,23 +19,67 @@ package org.apache.maven.surefire.booter;
 */
 
 import junit.framework.TestCase;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.report.ReporterFactory;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.suite.RunResult;
+import org.apache.maven.surefire.testset.DirectoryScannerParameters;
+import org.apache.maven.surefire.testset.RunOrderParameters;
+import org.apache.maven.surefire.testset.TestArtifactInfo;
+import org.apache.maven.surefire.testset.TestListResolver;
+import org.apache.maven.surefire.testset.TestRequest;
+import org.apache.maven.surefire.util.RunOrder;
+import org.mockito.ArgumentCaptor;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 public class SurefireReflectorTest
         extends TestCase
 {
+    public void testCreateConsoleLogger()
+    {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        ConsoleLogger consoleLogger = mock( ConsoleLogger.class );
+        ConsoleLogger decorator = (ConsoleLogger) SurefireReflector.createConsoleLogger( consoleLogger, cl );
+        assertThat( decorator )
+        .isNotSameAs( consoleLogger );
+
+        assertThat( decorator.isDebugEnabled() ).isFalse();
+        when( consoleLogger.isDebugEnabled() ).thenReturn( true );
+        assertThat( decorator.isDebugEnabled() ).isTrue();
+        verify( consoleLogger, times( 2 ) ).isDebugEnabled();
+
+        decorator.info( "msg" );
+        ArgumentCaptor<String> argumentMsg = ArgumentCaptor.forClass( String.class );
+        verify( consoleLogger, times( 1 ) ).info( argumentMsg.capture() );
+        assertThat( argumentMsg.getAllValues() ).hasSize( 1 );
+        assertThat( argumentMsg.getAllValues().get( 0 ) ).isEqualTo( "msg" );
+    }
+
     public void testShouldCreateFactoryWithoutException()
     {
-        ReporterFactory factory = new ReporterFactory() {
+        ReporterFactory factory = new ReporterFactory()
+        {
             @Override
-            public RunListener createReporter() {
+            public RunListener createReporter()
+            {
                 return null;
             }
 
             @Override
-            public RunResult close() {
+            public RunResult close()
+            {
                 return null;
             }
         };
@@ -46,4 +90,105 @@ public class SurefireReflectorTest
         assertNotNull( baseProviderFactory.getReporterFactory() );
         assertSame( factory, baseProviderFactory.getReporterFactory() );
     }
+
+    public void testSetDirectoryScannerParameters()
+    {
+        SurefireReflector surefireReflector = getReflector();
+        Object foo = getFoo();
+
+        DirectoryScannerParameters directoryScannerParameters =
+                new DirectoryScannerParameters( new File( "ABC" ), new ArrayList<String>(), new ArrayList<String>(),
+                        new ArrayList<String>(), false, "hourly" );
+        surefireReflector.setDirectoryScannerParameters( foo, directoryScannerParameters );
+        assertTrue( isCalled( foo ) );
+    }
+
+    public void testRunOrderParameters()
+    {
+        SurefireReflector surefireReflector = getReflector();
+        Object foo = getFoo();
+
+        RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, new File( "." ) );
+        surefireReflector.setRunOrderParameters( foo, runOrderParameters );
+        assertTrue( isCalled( foo ) );
+    }
+
+    public void testTestSuiteDefinition()
+    {
+        SurefireReflector surefireReflector = getReflector();
+        Object foo = getFoo();
+
+        TestRequest testSuiteDefinition =
+                new TestRequest( Arrays.asList( new File( "file1" ), new File( "file2" ) ),
+                        new File( "TestSOurce" ), new TestListResolver( "aUserRequestedTest#aMethodRequested" ) );
+        surefireReflector.setTestSuiteDefinition( foo, testSuiteDefinition );
+        assertTrue( isCalled( foo ) );
+    }
+
+    public void testProviderProperties()
+    {
+        SurefireReflector surefireReflector = getReflector();
+        Object foo = getFoo();
+
+        surefireReflector.setProviderProperties( foo, new HashMap<String, String>() );
+        assertTrue( isCalled( foo ) );
+    }
+
+    public void testReporterConfiguration()
+    {
+        SurefireReflector surefireReflector = getReflector();
+        Object foo = getFoo();
+
+        ReporterConfiguration reporterConfiguration = getReporterConfiguration();
+        surefireReflector.setReporterConfigurationAware( foo, reporterConfiguration );
+        assertTrue( isCalled( foo ) );
+    }
+
+    private ReporterConfiguration getReporterConfiguration()
+    {
+        return new ReporterConfiguration( new File( "CDE" ), true );
+    }
+
+    public void testTestClassLoaderAware()
+    {
+        SurefireReflector surefireReflector = getReflector();
+        Object foo = getFoo();
+
+        surefireReflector.setTestClassLoader( foo, getClass().getClassLoader() );
+        assertTrue( isCalled( foo ) );
+    }
+
+    public void testArtifactInfoAware()
+    {
+        SurefireReflector surefireReflector = getReflector();
+        Object foo = getFoo();
+
+        TestArtifactInfo testArtifactInfo = new TestArtifactInfo( "12.3", "test" );
+        surefireReflector.setTestArtifactInfo( foo, testArtifactInfo );
+        assertTrue( isCalled( foo ) );
+    }
+
+    private SurefireReflector getReflector()
+    {
+        return new SurefireReflector( this.getClass().getClassLoader() );
+    }
+
+    private Object getFoo()
+    { // Todo: Setup a different classloader so we can really test crossing
+        return new Foo();
+    }
+
+    private Boolean isCalled( Object foo )
+    {
+        final Method isCalled;
+        try
+        {
+            isCalled = foo.getClass().getMethod( "isCalled" );
+            return (Boolean) isCalled.invoke( foo );
+        }
+        catch ( ReflectiveOperationException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
 }
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/ReflectionUtilsTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/ReflectionUtilsTest.java
index 5440d6e..34eafe3 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/util/ReflectionUtilsTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/ReflectionUtilsTest.java
@@ -31,6 +31,14 @@ import static org.fest.assertions.Assertions.assertThat;
  */
 public class ReflectionUtilsTest
 {
+    @Test
+    public void shouldReloadClass() throws Exception
+    {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        assertThat( ReflectionUtils.reloadClass( cl, new B() ) )
+                .isEqualTo( B.class );
+    }
+
     @Test(expected = RuntimeException.class)
     public void shouldNotInvokeStaticMethod()
     {
diff --git a/surefire-booter/pom.xml b/surefire-booter/pom.xml
index 74cd93c..1fe33e7 100644
--- a/surefire-booter/pom.xml
+++ b/surefire-booter/pom.xml
@@ -86,23 +86,39 @@
       <artifactId>powermock-api-mockito2</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.jacoco</groupId>
+      <artifactId>org.jacoco.agent</artifactId>
+      <classifier>runtime</classifier>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
     <plugins>
       <plugin>
-        <artifactId>maven-dependency-plugin</artifactId>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
         <executions>
           <execution>
-            <id>build-test-classpath</id>
-            <phase>generate-sources</phase>
+            <id>jacoco-instrumentation</id>
             <goals>
-              <goal>build-classpath</goal>
+              <goal>instrument</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>restore-classes</id>
+            <goals>
+              <goal>restore-instrumented-classes</goal>
+            </goals>
+          </execution>
+          <execution>
+            <!--todo remove-->
+            <id>jacoco-report</id>
+            <phase>prepare-package</phase>
+            <goals>
+              <goal>report</goal>
             </goals>
-            <configuration>
-              <includeScope>test</includeScope>
-              <outputFile>target/test-classpath/cp.txt</outputFile>
-            </configuration>
           </execution>
         </executions>
       </plugin>
@@ -116,9 +132,13 @@
           </dependency>
         </dependencies>
         <configuration>
+          <argLine>${jvm.args.tests}</argLine>
           <includes>
             <include>**/JUnit4SuiteTest.java</include>
           </includes>
+          <systemPropertyVariables>
+            <jacoco-agent.destfile>${project.build.directory}/jacoco.exec</jacoco-agent.destfile>
+          </systemPropertyVariables>
         </configuration>
       </plugin>
       <plugin>
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
index f073a8b..ff731c4 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
@@ -21,8 +21,8 @@ package org.apache.maven.surefire.booter;
 
 import junit.framework.JUnit4TestAdapter;
 import junit.framework.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
 
 /**
  * Adapt the JUnit4 tests which use only annotations to the JUnit3 test suite.
@@ -30,19 +30,15 @@ import org.junit.runners.Suite;
  * @author Tibor Digana (tibor17)
  * @since 2.19
  */
-@Suite.SuiteClasses( {
-    ClasspathTest.class,
-    CommandReaderTest.class,
-    PropertiesWrapperTest.class,
-    SurefireReflectorTest.class,
-    PpidCheckerTest.class,
-    SystemUtilsTest.class
-} )
-@RunWith( Suite.class )
-public class JUnit4SuiteTest
+public class JUnit4SuiteTest extends TestCase
 {
     public static Test suite()
     {
-        return new JUnit4TestAdapter( JUnit4SuiteTest.class );
+        TestSuite suite = new TestSuite();
+        suite.addTest( new JUnit4TestAdapter( PpidCheckerTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( SystemUtilsTest.class ) );
+        suite.addTestSuite( ClasspathTest.class );
+        suite.addTestSuite( PropertiesWrapperTest.class );
+        return suite;
     }
 }
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
deleted file mode 100644
index 0f5188c..0000000
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import java.io.File;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import org.apache.maven.surefire.report.ReporterConfiguration;
-import org.apache.maven.surefire.testset.DirectoryScannerParameters;
-import org.apache.maven.surefire.testset.RunOrderParameters;
-import org.apache.maven.surefire.testset.TestArtifactInfo;
-import org.apache.maven.surefire.testset.TestListResolver;
-import org.apache.maven.surefire.testset.TestRequest;
-import org.apache.maven.surefire.util.RunOrder;
-
-import junit.framework.TestCase;
-
-/**
- * @author Kristian Rosenvold
- */
-public class SurefireReflectorTest
-    extends TestCase
-{
-    public void testSetDirectoryScannerParameters()
-        throws Exception
-    {
-        SurefireReflector surefireReflector = getReflector();
-        Object foo = getFoo();
-
-        DirectoryScannerParameters directoryScannerParameters =
-            new DirectoryScannerParameters( new File( "ABC" ), new ArrayList<String>(), new ArrayList<String>(),
-                                            new ArrayList<String>(), false, "hourly" );
-        surefireReflector.setDirectoryScannerParameters( foo, directoryScannerParameters );
-        assertTrue( isCalled( foo ) );
-
-    }
-
-    public void testRunOrderParameters()
-        throws Exception
-    {
-        SurefireReflector surefireReflector = getReflector();
-        Object foo = getFoo();
-
-        RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, new File( "." ) );
-        surefireReflector.setRunOrderParameters( foo, runOrderParameters );
-        assertTrue( isCalled( foo ) );
-
-    }
-
-    public void testTestSuiteDefinition()
-        throws Exception
-    {
-        SurefireReflector surefireReflector = getReflector();
-        Object foo = getFoo();
-
-        TestRequest testSuiteDefinition =
-            new TestRequest( Arrays.asList( new File( "file1" ), new File( "file2" ) ),
-                             new File( "TestSOurce" ), new TestListResolver( "aUserRequestedTest#aMethodRequested" ) );
-        surefireReflector.setTestSuiteDefinition( foo, testSuiteDefinition );
-        assertTrue( isCalled( foo ) );
-    }
-
-    public void testProviderProperties()
-        throws Exception
-    {
-        SurefireReflector surefireReflector = getReflector();
-        Object foo = getFoo();
-
-        surefireReflector.setProviderProperties( foo, new HashMap<String, String>() );
-        assertTrue( isCalled( foo ) );
-    }
-
-    public void testReporterConfiguration()
-        throws Exception
-    {
-        SurefireReflector surefireReflector = getReflector();
-        Object foo = getFoo();
-
-        ReporterConfiguration reporterConfiguration = getReporterConfiguration();
-        surefireReflector.setReporterConfigurationAware( foo, reporterConfiguration );
-        assertTrue( isCalled( foo ) );
-    }
-
-    private ReporterConfiguration getReporterConfiguration()
-    {
-        return new ReporterConfiguration( new File( "CDE" ), true );
-    }
-
-    public void testTestClassLoaderAware()
-        throws Exception
-    {
-        SurefireReflector surefireReflector = getReflector();
-        Object foo = getFoo();
-
-        surefireReflector.setTestClassLoader( foo, getClass().getClassLoader() );
-        assertTrue( isCalled( foo ) );
-    }
-
-    public void testArtifactInfoAware()
-        throws Exception
-    {
-        SurefireReflector surefireReflector = getReflector();
-        Object foo = getFoo();
-
-        TestArtifactInfo testArtifactInfo = new TestArtifactInfo( "12.3", "test" );
-        surefireReflector.setTestArtifactInfo( foo, testArtifactInfo );
-        assertTrue( isCalled( foo ) );
-    }
-
-    private SurefireReflector getReflector()
-    {
-        return new SurefireReflector( this.getClass().getClassLoader() );
-    }
-
-    public Object getFoo()
-    { // Todo: Setup a different classloader so we can really test crossing
-        return new Foo();
-    }
-
-
-    private Boolean isCalled( Object foo )
-    {
-        final Method isCalled;
-        try
-        {
-            isCalled = foo.getClass().getMethod( "isCalled" );
-            return (Boolean) isCalled.invoke( foo );
-        }
-        catch ( ReflectiveOperationException e )
-        {
-            throw new RuntimeException( e );
-        }
-    }
-}
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SystemUtilsTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SystemUtilsTest.java
index a957d06..438cb00 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SystemUtilsTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SystemUtilsTest.java
@@ -22,6 +22,7 @@ package org.apache.maven.surefire.booter;
 import org.junit.Test;
 import org.junit.experimental.runners.Enclosed;
 import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
@@ -63,7 +64,8 @@ public class SystemUtilsTest
         public void shouldMatchJavaSpecVersion() throws Exception
         {
             BigDecimal actual = invokeMethod( SystemUtils.class, "getJavaSpecificationVersion" );
-            BigDecimal expected = new BigDecimal( System.getProperty( "java.specification.version" ) ).stripTrailingZeros();
+            BigDecimal expected =
+                    new BigDecimal( System.getProperty( "java.specification.version" ) ).stripTrailingZeros();
             assertThat( actual ).isEqualTo( expected );
             assertThat( SystemUtils.JAVA_SPECIFICATION_VERSION ).isEqualTo( expected );
         }
@@ -295,6 +297,7 @@ public class SystemUtilsTest
 
     @RunWith( PowerMockRunner.class )
     @PrepareForTest( SystemUtils.class )
+    @PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
     public static class MockTest
     {
 
diff --git a/surefire-extensions-api/pom.xml b/surefire-extensions-api/pom.xml
new file mode 100644
index 0000000..db0aafa
--- /dev/null
+++ b/surefire-extensions-api/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.maven.surefire</groupId>
+        <artifactId>surefire</artifactId>
+        <version>3.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>surefire-extensions-api</artifactId>
+    <name>Surefire Extensions API</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.maven.surefire</groupId>
+            <artifactId>surefire-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-component-annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jacoco</groupId>
+            <artifactId>org.jacoco.agent</artifactId>
+            <classifier>runtime</classifier>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>jacoco-instrumentation</id>
+                        <!--
+                        phase: the order of the plugins matters and maven-plugin-plugin must be before jacoco plugin
+                        -->
+                        <goals>
+                            <goal>instrument</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>restore-classes</id>
+                        <goals>
+                            <goal>restore-instrumented-classes</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <!--todo remove-->
+                        <id>jacoco-report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.surefire</groupId>
+                        <artifactId>surefire-shadefire</artifactId>
+                        <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
+                    </dependency>
+                </dependencies>
+                <configuration>
+                    <argLine>${jvm.args.tests}</argLine>
+                    <systemPropertyVariables>
+                        <jacoco-agent.destfile>${project.build.directory}/jacoco.exec</jacoco-agent.destfile>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ConsoleOutputReportEventListener.java
similarity index 60%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java
copy to surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ConsoleOutputReportEventListener.java
index f1b5b5a..2b1e090 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ConsoleOutputReportEventListener.java
@@ -1,4 +1,4 @@
-package org.apache.maven.plugin.surefire.report;
+package org.apache.maven.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,18 +19,19 @@ package org.apache.maven.plugin.surefire.report;
  * under the License.
  */
 
-import org.apache.maven.surefire.report.ConsoleOutputReceiver;
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 
 /**
- * @author Kristian Rosenvold
+ * Extension listener for logger.
+ * The signature can be changed between major, minor versions or milestones.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
  */
-public interface TestcycleConsoleOutputReceiver
-    extends ConsoleOutputReceiver
+public interface ConsoleOutputReportEventListener
 {
-    void testSetStarting( ReportEntry reportEntry );
-
-    void testSetCompleted( ReportEntry report );
-
+    void testSetStarting( TestSetReportEntry report );
+    void testSetCompleted( TestSetReportEntry report );
     void close();
+    void writeTestOutput( String output, boolean newLine, boolean stdout );
 }
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ConsoleOutputReporter.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ConsoleOutputReporter.java
new file mode 100644
index 0000000..382eaf3
--- /dev/null
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ConsoleOutputReporter.java
@@ -0,0 +1,72 @@
+package org.apache.maven.surefire.extensions;
+
+/*
+ * 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 java.io.File;
+import java.io.PrintStream;
+
+import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
+
+/**
+ * Extension for logger.
+ * The signature can be changed between major, minor versions or milestones.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ */
+public abstract class ConsoleOutputReporter
+{
+    /**
+     * {@code false} by default
+     */
+    private boolean disable;
+
+    /**
+     * The content is encoded <em>UTF-8</em> by default.
+     */
+    private String encoding;
+
+    public abstract ConsoleOutputReportEventListener createListener( File reportsDirectory, String reportNameSuffix,
+                                                                     Integer forkNumber );
+
+    public abstract ConsoleOutputReportEventListener createListener( PrintStream out, PrintStream err );
+
+    public abstract Object clone( ClassLoader target );
+
+    public boolean isDisable()
+    {
+        return disable;
+    }
+
+    public void setDisable( boolean disable )
+    {
+        this.disable = disable;
+    }
+
+    public String getEncoding()
+    {
+        return isBlank( encoding ) ? "UTF-8" : encoding;
+    }
+
+    public void setEncoding( String encoding )
+    {
+        this.encoding = encoding;
+    }
+}
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessReportEventListener.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessReportEventListener.java
new file mode 100644
index 0000000..3187802
--- /dev/null
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessReportEventListener.java
@@ -0,0 +1,43 @@
+package org.apache.maven.surefire.extensions;
+
+/*
+ * 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.report.TestSetReportEntry;
+
+/**
+ * Creates a report upon handled event "<em>testSetCompleted</em>".
+ * <br>
+ * Defaults to <em>org.apache.maven.plugin.surefire.report.StatelessXmlReporter</em>.
+ *
+ * author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ * @param <R> report entry type, see <em>WrappedReportEntry</em> from module the <em>maven-surefire-common</em>
+ * @param <S> test-set statistics, see <em>TestSetStats</em> from module the <em>maven-surefire-common</em>
+ */
+public interface StatelessReportEventListener<R extends TestSetReportEntry, S>
+{
+    /**
+     * The callback is called after the test class has been completed and the state of report is final.
+     *
+     * @param report <em>WrappedReportEntry</em>
+     * @param testSetStats <em>TestSetStats</em>
+     */
+     void testSetCompleted( R report, S testSetStats );
+}
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessReportMojoConfiguration.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessReportMojoConfiguration.java
new file mode 100644
index 0000000..f176fce
--- /dev/null
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessReportMojoConfiguration.java
@@ -0,0 +1,75 @@
+package org.apache.maven.surefire.extensions;
+
+/*
+ * 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 java.io.File;
+
+/**
+ * Configuration passed to the constructor of default reporter
+ * <em>org.apache.maven.plugin.surefire.report.StatelessXmlReporter</em>.
+ * Signatures can be changed between major, minor versions or milestones.
+ */
+public class StatelessReportMojoConfiguration
+{
+    private final File reportsDirectory;
+
+    private final String reportNameSuffix;
+
+    private final boolean trimStackTrace;
+
+    private final int rerunFailingTestsCount;
+
+    private final String xsdSchemaLocation;
+
+    public StatelessReportMojoConfiguration( File reportsDirectory, String reportNameSuffix, boolean trimStackTrace,
+                                             int rerunFailingTestsCount, String xsdSchemaLocation )
+    {
+        this.reportsDirectory = reportsDirectory;
+        this.reportNameSuffix = reportNameSuffix;
+        this.trimStackTrace = trimStackTrace;
+        this.rerunFailingTestsCount = rerunFailingTestsCount;
+        this.xsdSchemaLocation = xsdSchemaLocation;
+    }
+
+    public File getReportsDirectory()
+    {
+        return reportsDirectory;
+    }
+
+    public String getReportNameSuffix()
+    {
+        return reportNameSuffix;
+    }
+
+    public boolean isTrimStackTrace()
+    {
+        return trimStackTrace;
+    }
+
+    public int getRerunFailingTestsCount()
+    {
+        return rerunFailingTestsCount;
+    }
+
+    public String getXsdSchemaLocation()
+    {
+        return xsdSchemaLocation;
+    }
+}
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessReporter.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessReporter.java
new file mode 100644
index 0000000..60cee59
--- /dev/null
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessReporter.java
@@ -0,0 +1,87 @@
+package org.apache.maven.surefire.extensions;
+
+/*
+ * 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.report.TestSetReportEntry;
+
+import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
+
+/**
+ * Extension for stateless reporter.
+ * Signatures can be changed between major, minor versions or milestones.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ * @param <R> report entry type, see <em>WrappedReportEntry</em> from module the <em>maven-surefire-common</em>
+ * @param <S> test-set statistics, see <em>TestSetStats</em> from module the <em>maven-surefire-common</em>
+ * @param <C> mojo config, see <em>DefaultStatelessReportMojoConfiguration</em> from <em>maven-surefire-common</em>
+ */
+public abstract class StatelessReporter<R extends TestSetReportEntry, S, C extends StatelessReportMojoConfiguration>
+{
+    /**
+     * {@code false} by default
+     */
+    //todo remove isDisableXmlReport() in AbstractSurefireMojo and use this param instead
+    private boolean disable;
+
+    /**
+     * Version of reporter. It is version <em>3.0</em> used by default in XML reporter.
+     */
+    private String version;
+
+    /**
+     * Creates reporter.
+     *
+     * @return reporter object
+     */
+    public abstract StatelessReportEventListener<R, S> createListener( C configuration );
+
+    public abstract Object clone( ClassLoader target );
+
+    public boolean isDisable()
+    {
+        return disable;
+    }
+
+    public void setDisable( boolean disable )
+    {
+        this.disable = disable;
+    }
+
+    public String getVersion()
+    {
+        return isBlank( version ) ? "3.0" : version;
+    }
+
+    public void setVersion( String version )
+    {
+        this.version = version;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getClass().getSimpleName()
+                + "{"
+                + "version=" + getVersion()
+                + ", disable=" + isDisable()
+                + '}';
+    }
+}
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoConsoleReportEventListener.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoConsoleReportEventListener.java
new file mode 100644
index 0000000..f01acff
--- /dev/null
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoConsoleReportEventListener.java
@@ -0,0 +1,53 @@
+package org.apache.maven.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.report.TestSetReportEntry;
+
+import java.util.List;
+
+/**
+ * Extension listener for stateless console reporter of test-set.
+ * Signatures can be changed between major, minor versions or milestones.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ * @param <R> report entry type, see <em>WrappedReportEntry</em> from module the <em>maven-surefire-common</em>
+ * @param <S> test-set statistics, see <em>TestSetStats</em> from module the <em>maven-surefire-common</em>
+ */
+public abstract class StatelessTestsetInfoConsoleReportEventListener<R extends TestSetReportEntry, S>
+{
+    private final ConsoleLogger logger;
+
+    public StatelessTestsetInfoConsoleReportEventListener( ConsoleLogger logger )
+    {
+        this.logger = logger;
+    }
+
+    public abstract void testSetStarting( TestSetReportEntry report );
+    public abstract void testSetCompleted( R report, S testSetStats, List<String> testResults );
+    public abstract void reset();
+
+    public ConsoleLogger getConsoleLogger()
+    {
+        return logger;
+    }
+}
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoFileReportEventListener.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoFileReportEventListener.java
new file mode 100644
index 0000000..91e460a
--- /dev/null
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoFileReportEventListener.java
@@ -0,0 +1,67 @@
+package org.apache.maven.surefire.extensions;
+
+/*
+ * 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.report.TestSetReportEntry;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.util.List;
+
+/**
+ * Extension listener for stateless file reporter of test-set.
+ * Signatures can be changed between major, minor versions or milestones.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ * @param <R> report entry type, see <em>WrappedReportEntry</em> from module the <em>maven-surefire-common</em>
+ * @param <S> test-set statistics, see <em>TestSetStats</em> from module the <em>maven-surefire-common</em>
+ */
+public abstract class StatelessTestsetInfoFileReportEventListener<R extends TestSetReportEntry, S>
+{
+    private final File reportsDirectory;
+    private final String reportNameSuffix;
+    private final Charset encoding;
+
+    public StatelessTestsetInfoFileReportEventListener( File reportsDirectory, String reportNameSuffix,
+                                                        Charset encoding )
+    {
+        this.reportsDirectory = reportsDirectory;
+        this.reportNameSuffix = reportNameSuffix;
+        this.encoding = encoding;
+    }
+
+    public abstract void testSetCompleted( R report, S testSetStats, List<String> testResults );
+
+    protected File getReportsDirectory()
+    {
+        return reportsDirectory;
+    }
+
+    protected String getReportNameSuffix()
+    {
+        return reportNameSuffix;
+    }
+
+    protected Charset getEncoding()
+    {
+        return encoding;
+    }
+}
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoReporter.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoReporter.java
new file mode 100644
index 0000000..ca10944
--- /dev/null
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StatelessTestsetInfoReporter.java
@@ -0,0 +1,58 @@
+package org.apache.maven.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.report.TestSetReportEntry;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+/**
+ * Extension listener for stateless file and console reporter of test-set.
+ * Signatures can be changed between major, minor versions or milestones.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ * @param <R> report entry type, see <em>WrappedReportEntry</em> from module the <em>maven-surefire-common</em>
+ * @param <S> test-set statistics, see <em>TestSetStats</em> from module the <em>maven-surefire-common</em>
+ */
+public abstract class StatelessTestsetInfoReporter<R extends TestSetReportEntry, S>
+{
+    private boolean disable;
+
+    public abstract StatelessTestsetInfoConsoleReportEventListener<R, S> createListener( ConsoleLogger logger );
+
+    public abstract StatelessTestsetInfoFileReportEventListener<R, S> createListener( File reportsDirectory,
+                                                                                      String reportNameSuffix,
+                                                                                      Charset encoding );
+
+    public abstract Object clone( ClassLoader target );
+
+    public boolean isDisable()
+    {
+        return disable;
+    }
+
+    public void setDisable( boolean disable )
+    {
+        this.disable = disable;
+    }
+}
diff --git a/surefire-grouper/pom.xml b/surefire-grouper/pom.xml
index 9aa676c..d3ae750 100644
--- a/surefire-grouper/pom.xml
+++ b/surefire-grouper/pom.xml
@@ -52,7 +52,33 @@
         </executions>
       </plugin>
       <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>jacoco-agent</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <!--todo remove-->
+            <id>jacoco-report</id>
+            <phase>prepare-package</phase>
+            <goals>
+              <goal>report</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <propertyName>jacoco.agent</propertyName>
+        </configuration>
+      </plugin>
+      <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
+        </configuration>
         <dependencies>
           <dependency>
             <groupId>org.apache.maven.surefire</groupId>
diff --git a/surefire-its/pom.xml b/surefire-its/pom.xml
index 2da331f..c6523cd 100644
--- a/surefire-its/pom.xml
+++ b/surefire-its/pom.xml
@@ -203,13 +203,6 @@
                 </executions>
             </plugin>
             <plugin>
-                <artifactId>maven-jar-plugin</artifactId>
-                <!-- todo dont skip since of failsafe:2.19 internal use if having src/main/java/... -->
-                <configuration>
-                    <skip>true</skip>
-                </configuration>
-            </plugin>
-            <plugin>
                 <groupId>org.jacoco</groupId>
                 <artifactId>jacoco-maven-plugin</artifactId>
                 <executions>
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java
index af8f366..0cfafc4 100644
--- a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java
@@ -64,8 +64,9 @@ public class JUnitPlatformEnginesIT
         args.add( new Object[] { "1.1.1", "5.1.1", "1.0.0", "1.0.0" } );
         args.add( new Object[] { "1.2.0", "5.2.0", "1.1.0", "1.0.0" } );
         args.add( new Object[] { "1.3.2", "5.3.2", "1.1.1", "1.0.0" } );
-        args.add( new Object[] { "1.4.0-SNAPSHOT", "5.4.0-SNAPSHOT", "1.1.1", "1.0.0" } );
-        args.add( new Object[] { "1.4.0-RC2", "5.4.0-RC2", "1.1.1", "1.0.0" } );
+        args.add( new Object[] { "1.4.2", "5.4.2", "1.1.1", "1.0.0" } );
+        args.add( new Object[] { "1.5.0-M1", "5.5.0-M1", "1.1.1", "1.0.0" } );
+        args.add( new Object[] { "1.5.0-SNAPSHOT", "5.5.0-SNAPSHOT", "1.2.0-SNAPSHOT", "1.0.0" } );
         return args;
     }
 
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformIT.java
index 7675843..9522ebe 100644
--- a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformIT.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformIT.java
@@ -22,14 +22,20 @@ package org.apache.maven.surefire.its;
 import org.apache.maven.surefire.its.fixture.OutputValidator;
 import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaVersion;
+import static org.apache.maven.surefire.its.fixture.HelperAssertions.convertUnicodeToUTF8;
 
 public class JUnitPlatformIT
         extends SurefireJUnit4IntegrationTestCase
 {
+    private static final String XML_TESTSUITE_FRAGMENT =
+            "<testsuite xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation="
+                    + "\"https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report-3.0.xsd\" "
+                    + "version=\"3.0\" name=\"&lt;&lt; ✨ &gt;&gt;\"";
+
     @Before
     public void setUp()
     {
@@ -40,27 +46,42 @@ public class JUnitPlatformIT
     public void testJupiterEngine()
     {
         unpack( "/junit-platform-engine-jupiter" )
+                .setTestToRun( "Basic*Test" )
                 .executeTest()
                 .verifyErrorFree( 5 );
     }
 
     @Test
-    @Ignore( "Uncomment while developing SUREFIRE-1222. Rename 'javax' extension of DisplayNameTest.javax." )
     public void testJupiterEngineWithDisplayNames()
     {
         OutputValidator validator = unpack( "/junit-platform-engine-jupiter" )
                 .executeTest()
                 .verifyErrorFree( 7 );
 
-        validator.getSurefireReportsFile( "junitplatformenginejupiter.DisplayNameTest.txt" )
-                 // .assertContainsText( "<< ✨ >>" ) // after @DisplayName is uncommented via SUREFIRE-1222
-                 .assertContainsText( "Test set: junitplatformenginejupiter.DisplayNameTest" );
+        validator.getSurefireReportsFile( "junitplatformenginejupiter.DisplayNameTest.txt", UTF_8 )
+                 .assertContainsText( "<< ✨ >>" );
+
+        validator.getSurefireReportsFile( "junitplatformenginejupiter.DisplayNameTest.txt", UTF_8 )
+                .assertContainsText( "Test set: << ✨ >>" );
+
+        validator.getSurefireReportsFile( "junitplatformenginejupiter.DisplayNameTest.txt", UTF_8 )
+                .assertContainsText( " - in << ✨ >>" );
+
+
+        validator.getSurefireReportsFile( "junitplatformenginejupiter.DisplayNameTest-output.txt", UTF_8 )
+                .assertContainsText( "<< ✨ >>" );
+
+        validator.getSurefireReportsFile( "junitplatformenginejupiter.DisplayNameTest-output.txt", UTF_8 )
+                .assertContainsText( "73$71 ✔" );
+
+        validator.getSurefireReportsFile( "junitplatformenginejupiter.DisplayNameTest-output.txt", UTF_8 )
+                .assertContainsText( "73$72 ✔" );
+
 
-        validator.getSurefireReportsFile( "TEST-junitplatformenginejupiter.DisplayNameTest.xml" )
-                 // At the moment, the testcase with the same is reported twice: test1() and test2() use the same display name
-                 // SUREFIRE-1222 will solve this.
-                 .assertContainsText( "testcase name=\"73$71 ✔\" classname=\"junitplatformenginejupiter.DisplayNameTest\"" )
-                 .assertContainsText( "testcase name=\"73$71 ✔\" classname=\"junitplatformenginejupiter.DisplayNameTest\"" );
+        validator.getSurefireReportsFile( "TEST-junitplatformenginejupiter.DisplayNameTest.xml", UTF_8 )
+                .assertContainsText( "testcase name=\"73$71 ✔\" classname=\"&lt;&lt; ✨ &gt;&gt;\"" )
+                .assertContainsText( "testcase name=\"73$72 ✔\" classname=\"&lt;&lt; ✨ &gt;&gt;\"" )
+                .assertContainsText( XML_TESTSUITE_FRAGMENT );
     }
 
     @Test
diff --git a/surefire-its/src/test/resources/junit-platform-engine-jupiter/pom.xml b/surefire-its/src/test/resources/junit-platform-engine-jupiter/pom.xml
index 192cc8a..4947646 100644
--- a/surefire-its/src/test/resources/junit-platform-engine-jupiter/pom.xml
+++ b/surefire-its/src/test/resources/junit-platform-engine-jupiter/pom.xml
@@ -32,6 +32,7 @@
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
         <junit.jupiter.version>5.2.0</junit.jupiter.version>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     </properties>
 
     <!--
@@ -58,9 +59,39 @@
     <build>
         <plugins>
             <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.0</version>
+                <configuration>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>${surefire.version}</version>
+                <configuration>
+                    <forkCount>1.0C</forkCount>
+                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                    <statelessTestsetReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5Xml30StatelessReporter">
+                        <disable>false</disable>
+                        <version>3.0</version>
+                        <usePhrasedFileName>false</usePhrasedFileName>
+                        <usePhrasedTestSuiteClassName>true</usePhrasedTestSuiteClassName>
+                        <usePhrasedTestCaseClassName>true</usePhrasedTestCaseClassName>
+                        <usePhrasedTestCaseMethodName>true</usePhrasedTestCaseMethodName>
+                    </statelessTestsetReporter>
+                    <consoleOutputReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5ConsoleOutputReporter">
+                        <disable>false</disable>
+                        <encoding>UTF-8</encoding>
+                        <usePhrasedFileName>false</usePhrasedFileName>
+                    </consoleOutputReporter>
+                    <statelessTestsetInfoReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoReporter">
+                        <disable>false</disable>
+                        <usePhrasedFileName>false</usePhrasedFileName>
+                        <usePhrasedClassNameInRunning>true</usePhrasedClassNameInRunning>
+                        <usePhrasedClassNameInTestCaseSummary>true</usePhrasedClassNameInTestCaseSummary>
+                    </statelessTestsetInfoReporter>
+                </configuration>
             </plugin>
         </plugins>
     </build>
diff --git a/surefire-its/src/test/resources/junit-platform-engine-jupiter/src/test/java/junitplatformenginejupiter/DisplayNameTest.javax b/surefire-its/src/test/resources/junit-platform-engine-jupiter/src/test/java/junitplatformenginejupiter/DisplayNameTest.java
similarity index 65%
rename from surefire-its/src/test/resources/junit-platform-engine-jupiter/src/test/java/junitplatformenginejupiter/DisplayNameTest.javax
rename to surefire-its/src/test/resources/junit-platform-engine-jupiter/src/test/java/junitplatformenginejupiter/DisplayNameTest.java
index 8089ad0..a6401e3 100644
--- a/surefire-its/src/test/resources/junit-platform-engine-jupiter/src/test/java/junitplatformenginejupiter/DisplayNameTest.javax
+++ b/surefire-its/src/test/resources/junit-platform-engine-jupiter/src/test/java/junitplatformenginejupiter/DisplayNameTest.java
@@ -19,25 +19,27 @@ package junitplatformenginejupiter;
  * under the License.
  */
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
-// TODO Uncomment after SUREFIRE-1222 is done
-// @DisplayName("<< ✨ >>")
+@DisplayName( "<< ✨ >>" )
 class DisplayNameTest
 {
     @Test
-    @DisplayName("73$71 ✔")
+    @DisplayName( "73$71 ✔" )
     void test1()
+            throws Exception
     {
+        System.out.println( getClass().getDeclaredMethod( "test1" ).getAnnotation( DisplayName.class ).value() );
+        System.out.println( getClass().getAnnotation( DisplayName.class ).value() );
     }
 
     @Test
-    @DisplayName("73$71 ✔")
+    @DisplayName( "73$72 ✔" )
     void test2()
+            throws Exception
     {
+        System.out.println( getClass().getDeclaredMethod( "test2" ).getAnnotation( DisplayName.class ).value() );
+        System.out.println( getClass().getAnnotation( DisplayName.class ).value() );
     }
 }
diff --git a/surefire-logger-api/pom.xml b/surefire-logger-api/pom.xml
index 4a56f3a..429c754 100644
--- a/surefire-logger-api/pom.xml
+++ b/surefire-logger-api/pom.xml
@@ -49,4 +49,56 @@
         </developer>
     </developers>
 
+    <dependencies>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>jacoco-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <!--todo remove-->
+                        <id>jacoco-report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <propertyName>jacoco.agent</propertyName>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
+                    <includes>
+                        <include>**/JUnit4SuiteTest.java</include>
+                    </includes>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.surefire</groupId>
+                        <artifactId>surefire-shadefire</artifactId>
+                        <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>
diff --git a/surefire-logger-api/src/main/java/org/apache/maven/plugin/surefire/log/api/ConsoleLoggerDecorator.java b/surefire-logger-api/src/main/java/org/apache/maven/plugin/surefire/log/api/ConsoleLoggerDecorator.java
index 02105fa..5938fc1 100644
--- a/surefire-logger-api/src/main/java/org/apache/maven/plugin/surefire/log/api/ConsoleLoggerDecorator.java
+++ b/surefire-logger-api/src/main/java/org/apache/maven/plugin/surefire/log/api/ConsoleLoggerDecorator.java
@@ -1 +1 @@
-package org.apache.maven.plugin.surefire.log.api;

/*
 * 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.
 */

/**
 * Decorator around {@link ConsoleLogger}.
 * This class is loaded in the isolated ClassLoader and the child logger in the in-plugi
 n ClassLoader.
 *
 * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
 * @since 2.20
 */
public final class ConsoleLoggerDecorator
        implements ConsoleLogger
{
    private final Object logger;

    public ConsoleLoggerDecorator( Object logger )
    {
        if ( logger == null )
        {
            throw new NullPointerException( "logger argument is null in " + ConsoleLoggerDecorator.class );
        }
        this.logger = logger;
    }

    @Override
    public boolean isDebugEnabled()
    {
        try
        {
            return (Boolean) logger.getClass()
                    .getMethod( "isDebugEnabled" )
                    .invoke( logger );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    @Override
    public void debug( String message )
    {
        try
        {
            logger.getClass()
                    .getMethod( "debug", String.cl
 ass )
                    .invoke( logger, message );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    @Override
    public boolean isInfoEnabled()
    {
        try
        {
            return (Boolean) logger.getClass()
                    .getMethod( "isInfoEnabled" )
                    .invoke( logger );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    @Override
    public void info( String message )
    {
        try
        {
            logger.getClass()
                    .getMethod( "info", String.class )
                    .invoke( logger, message );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    @Override
    public boolean isWarnEnabled()
    {
        try
        {
            r
 eturn (Boolean) logger.getClass()
                    .getMethod( "isWarnEnabled" )
                    .invoke( logger );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    @Override
    public void warning( String message )
    {
        try
        {
            logger.getClass()
                    .getMethod( "warning", String.class )
                    .invoke( logger, message );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    @Override
    public boolean isErrorEnabled()
    {
        try
        {
            return (Boolean) logger.getClass()
                    .getMethod( "isErrorEnabled" )
                    .invoke( logger );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    @Override
 
    public void error( String message )
    {
        try
        {
            logger.getClass()
                    .getMethod( "error", String.class )
                    .invoke( logger, message );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    @Override
    public void error( String message, Throwable t )
    {
        try
        {
            logger.getClass()
                    .getMethod( "error", String.class, Throwable.class )
                    .invoke( logger, message, t );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    @Override
    public void error( Throwable t )
    {
        try
        {
            logger.getClass()
                    .getMethod( "error", Throwable.class )
                    .invoke( logger, t );
        }
        catch ( Exception e )
        {
 
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }
}
\ No newline at end of file
+package org.apache.maven.plugin.surefire.log.api;

/*
 * 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.
 */

/**
 * Decorator around {@link ConsoleLogger}.
 * This class is loaded in the isolated ClassLoader and the child logger in the in-plugi
 n ClassLoader.
 *
 * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
 * @since 2.20
 */
public final class ConsoleLoggerDecorator
        implements ConsoleLogger
{
    private final Object logger;

    public ConsoleLoggerDecorator( Object logger )
    {
        if ( logger == null )
        {
            throw new NullPointerException( "logger argument is null in " + ConsoleLoggerDecorator.class );
        }
        this.logger = logger;
    }

    @Override
    public boolean isDebugEnabled()
    {
        return invokeReturnedBoolean( "isDebugEnabled" );
    }

    @Override
    public void debug( String message )
    {
        invokeWithMessage( message, "debug" );
    }

    @Override
    public boolean isInfoEnabled()
    {
        return invokeReturnedBoolean( "isInfoEnabled" );
    }

    @Override
    public void info( String message )
    {
        invokeWithMessage( message, "info" );
    }

    @Override
    public boolean isWarnEnabled()
    
 {
        return invokeReturnedBoolean( "isWarnEnabled" );
    }

    @Override
    public void warning( String message )
    {
        invokeWithMessage( message, "warning" );
    }

    @Override
    public boolean isErrorEnabled()
    {
        return invokeReturnedBoolean( "isErrorEnabled" );
    }

    @Override
    public void error( String message )
    {
        invokeWithMessage( message, "error" );
    }

    @Override
    public void error( String message, Throwable t )
    {
        try
        {
            logger.getClass()
                    .getMethod( "error", String.class, Throwable.class )
                    .invoke( logger, message, t );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    @Override
    public void error( Throwable t )
    {
        try
        {
            logger.getClass()
                    .getMethod( "error", Throwable.class )
                   
  .invoke( logger, t );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    private boolean invokeReturnedBoolean( String isErrorEnabled )
    {
        try
        {
            return (Boolean) logger.getClass()
                    .getMethod( isErrorEnabled )
                    .invoke( logger );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }

    private void invokeWithMessage( String message, String error )
    {
        try
        {
            logger.getClass()
                    .getMethod( error, String.class )
                    .invoke( logger, message );
        }
        catch ( Exception e )
        {
            throw new IllegalStateException( e.getLocalizedMessage(), e );
        }
    }
}
\ No newline at end of file
diff --git a/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/ConsoleLoggerUtilsTest.java b/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/ConsoleLoggerUtilsTest.java
new file mode 100644
index 0000000..902a17e
--- /dev/null
+++ b/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/ConsoleLoggerUtilsTest.java
@@ -0,0 +1,64 @@
+package org.apache.maven.plugin.surefire.log.api;
+
+/*
+ * 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.junit.Test;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+/**
+ * Tests for {@link ConsoleLoggerUtils}.
+ */
+public class ConsoleLoggerUtilsTest
+{
+    @Test
+    public void shouldPrintStacktraceAsString()
+    {
+        Exception e = new IllegalArgumentException( "wrong param" );
+        String msg = ConsoleLoggerUtils.toString( e );
+
+        StringWriter text = new StringWriter();
+        PrintWriter writer = new PrintWriter( text );
+        e.printStackTrace( writer );
+        String s = text.toString();
+
+        assertThat( msg )
+                .isEqualTo( s );
+    }
+
+    @Test
+    public void shouldPrintStacktracWithMessageAsString()
+    {
+        Exception e = new IllegalArgumentException( "wrong param" );
+        String msg = ConsoleLoggerUtils.toString( "issue", e );
+
+        StringWriter text = new StringWriter();
+        PrintWriter writer = new PrintWriter( text );
+        writer.println( "issue" );
+        e.printStackTrace( writer );
+        String s = text.toString();
+
+        assertThat( msg )
+                .isEqualTo( s );
+    }
+}
diff --git a/surefire-providers/common-junit48/src/test/java/org/apache/maven/surefire/common/junit48/JUnit4SuiteTest.java b/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/JUnit4SuiteTest.java
similarity index 81%
copy from surefire-providers/common-junit48/src/test/java/org/apache/maven/surefire/common/junit48/JUnit4SuiteTest.java
copy to surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/JUnit4SuiteTest.java
index 547c381..69665a3 100644
--- a/surefire-providers/common-junit48/src/test/java/org/apache/maven/surefire/common/junit48/JUnit4SuiteTest.java
+++ b/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/JUnit4SuiteTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.common.junit48;
+package org.apache.maven.plugin.surefire.log.api;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -21,22 +21,20 @@ package org.apache.maven.surefire.common.junit48;
 
 import junit.framework.JUnit4TestAdapter;
 import junit.framework.Test;
+import junit.framework.TestCase;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
 
 /**
  * Adapt the JUnit4 tests which use only annotations to the JUnit3 test suite.
  *
  * @author Tibor Digana (tibor17)
- * @since 2.19
+ * @since 3.0.0-M4
  */
-@Suite.SuiteClasses( {
-    FilterFactoryTest.class,
-    JUnit48ReflectorTest.class,
-    JUnit48TestCheckerTest.class
-} )
+@SuiteClasses( { ConsoleLoggerUtilsTest.class, LevelTest.class, LoggersTest.class } )
 @RunWith( Suite.class )
-public class JUnit4SuiteTest
+public class JUnit4SuiteTest extends TestCase
 {
     public static Test suite()
     {
diff --git a/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/LevelTest.java b/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/LevelTest.java
new file mode 100644
index 0000000..0f48880
--- /dev/null
+++ b/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/LevelTest.java
@@ -0,0 +1,73 @@
+package org.apache.maven.plugin.surefire.log.api;
+
+/*
+ * 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.junit.Test;
+
+import static org.apache.maven.plugin.surefire.log.api.Level.resolveLevel;
+import static org.fest.assertions.Assertions.assertThat;
+
+/**
+ * Tests for {@link Level}.
+ */
+public class LevelTest
+{
+    @Test
+    public void shouldHaveSuccess()
+    {
+        Level level = resolveLevel( true, false, false, false, false );
+        assertThat( level ).isEqualTo( Level.SUCCESS );
+    }
+
+    @Test
+    public void shouldNotHaveSuccess()
+    {
+        Level level = resolveLevel( false, false, false, false, false );
+        assertThat( level ).isEqualTo( Level.NO_COLOR );
+    }
+
+    @Test
+    public void shouldBeFailure()
+    {
+        Level level = resolveLevel( false, true, false, false, false );
+        assertThat( level ).isEqualTo( Level.FAILURE );
+    }
+
+    @Test
+    public void shouldBeError()
+    {
+        Level level = resolveLevel( false, false, true, false, false );
+        assertThat( level ).isEqualTo( Level.FAILURE );
+    }
+
+    @Test
+    public void shouldBeSkipped()
+    {
+        Level level = resolveLevel( false, false, false, true, false );
+        assertThat( level ).isEqualTo( Level.UNSTABLE );
+    }
+
+    @Test
+    public void shouldBeFlake()
+    {
+        Level level = resolveLevel( false, false, false, false, true );
+        assertThat( level ).isEqualTo( Level.UNSTABLE );
+    }
+}
diff --git a/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/LoggersTest.java b/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/LoggersTest.java
new file mode 100644
index 0000000..7de9148
--- /dev/null
+++ b/surefire-logger-api/src/test/java/org/apache/maven/plugin/surefire/log/api/LoggersTest.java
@@ -0,0 +1,139 @@
+package org.apache.maven.plugin.surefire.log.api;
+
+/*
+ * 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.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.internal.matchers.CapturesArguments;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link ConsoleLoggerDecorator}, {@link NullConsoleLogger} and {@link PrintStreamLogger}.
+ */
+public class LoggersTest
+{
+    @Test
+    public void testPrintStreamLogger()
+    {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        PrintStream printStream = new PrintStream( outputStream );
+        PrintStreamLogger logger = new PrintStreamLogger( printStream );
+
+
+        assertThat( logger.isErrorEnabled() ).isTrue();
+        assertThat( logger.isWarnEnabled() ).isTrue();
+        assertThat( logger.isInfoEnabled() ).isTrue();
+        assertThat( logger.isDebugEnabled() ).isTrue();
+
+        logger.error( "error" );
+        logger.debug( "debug" );
+        logger.info( "info" );
+        logger.warning( "warning" );
+
+        String line = System.lineSeparator();
+        assertThat( outputStream.toString() )
+                .isEqualTo( "error" + line + "debug" + line + "info" + line + "warning" + line );
+
+        Exception e = new Exception( "exception" );
+        outputStream.reset();
+        logger.error( e );
+        assertThat( outputStream.toString() )
+                .contains( "java.lang.Exception: exception" )
+                .contains( "at " + getClass().getName() + ".testPrintStreamLogger(LoggersTest.java:63)" );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void shouldThrowNPE()
+    {
+        new ConsoleLoggerDecorator( null );
+    }
+
+    @Test
+    public void testDecorator()
+    {
+        ConsoleLogger logger = mock( ConsoleLogger.class );
+        ConsoleLoggerDecorator decorator = new ConsoleLoggerDecorator( logger );
+
+        assertThat( decorator.isDebugEnabled() ).isFalse();
+        when( logger.isDebugEnabled() ).thenReturn( true );
+        assertThat( decorator.isDebugEnabled() ).isTrue();
+
+        assertThat( decorator.isInfoEnabled() ).isFalse();
+        when( logger.isInfoEnabled() ).thenReturn( true );
+        assertThat( decorator.isInfoEnabled() ).isTrue();
+
+        assertThat( decorator.isWarnEnabled() ).isFalse();
+        when( logger.isWarnEnabled() ).thenReturn( true );
+        assertThat( decorator.isWarnEnabled() ).isTrue();
+
+        assertThat( decorator.isErrorEnabled() ).isFalse();
+        when( logger.isErrorEnabled() ).thenReturn( true );
+        assertThat( decorator.isErrorEnabled() ).isTrue();
+
+        ArgumentCaptor<String> argumentMsg = ArgumentCaptor.forClass( String.class );
+        decorator.debug( "debug" );
+        verify( logger, times( 1 ) ).debug( argumentMsg.capture() );
+        assertThat( argumentMsg.getAllValues() ).hasSize( 1 );
+        assertThat( argumentMsg.getAllValues().get( 0 ) ).isEqualTo( "debug" );
+
+        argumentMsg = ArgumentCaptor.forClass( String.class );
+        decorator.info( "info" );
+        verify( logger, times( 1 ) ).info( argumentMsg.capture() );
+        assertThat( argumentMsg.getAllValues() ).hasSize( 1 );
+        assertThat( argumentMsg.getAllValues().get( 0 ) ).isEqualTo( "info" );
+
+        argumentMsg = ArgumentCaptor.forClass( String.class );
+        decorator.warning( "warning" );
+        verify( logger, times( 1 ) ).warning( argumentMsg.capture() );
+        assertThat( argumentMsg.getAllValues() ).hasSize( 1 );
+        assertThat( argumentMsg.getAllValues().get( 0 ) ).isEqualTo( "warning" );
+
+        argumentMsg = ArgumentCaptor.forClass( String.class );
+        decorator.error( "error" );
+        verify( logger, times( 1 ) ).error( argumentMsg.capture() );
+        assertThat( argumentMsg.getAllValues() ).hasSize( 1 );
+        assertThat( argumentMsg.getAllValues().get( 0 ) ).isEqualTo( "error" );
+
+        ArgumentCaptor<Throwable> argumentThrowable = ArgumentCaptor.forClass( Throwable.class );
+        argumentMsg = ArgumentCaptor.forClass( String.class );
+        Exception e = new Exception();
+        decorator.error( "error", e );
+        verify( logger, times( 1 ) ).error( argumentMsg.capture(), argumentThrowable.capture() );
+        assertThat( argumentMsg.getAllValues() ).hasSize( 1 );
+        assertThat( argumentMsg.getAllValues().get( 0 ) ).isEqualTo( "error" );
+        assertThat( argumentThrowable.getAllValues() ).hasSize( 1 );
+        assertThat( argumentThrowable.getAllValues().get( 0 ) ).isSameAs( e );
+
+        argumentThrowable = ArgumentCaptor.forClass( Throwable.class );
+        decorator.error( e );
+        verify( logger, times( 1 ) ).error( argumentThrowable.capture() );
+        assertThat( argumentThrowable.getAllValues() ).hasSize( 1 );
+        assertThat( argumentThrowable.getAllValues().get( 0 ) ).isSameAs( e );
+    }
+}
diff --git a/surefire-providers/common-java5/pom.xml b/surefire-providers/common-java5/pom.xml
index ba44a98..7ecf6af 100644
--- a/surefire-providers/common-java5/pom.xml
+++ b/surefire-providers/common-java5/pom.xml
@@ -41,6 +41,29 @@
   <build>
     <plugins>
       <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>jacoco-agent</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <!--todo remove-->
+            <id>jacoco-report</id>
+            <phase>prepare-package</phase>
+            <goals>
+              <goal>report</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <propertyName>jacoco.agent</propertyName>
+        </configuration>
+      </plugin>
+      <plugin>
         <artifactId>maven-shade-plugin</artifactId>
         <executions>
           <execution>
@@ -68,10 +91,15 @@
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
-          <excludes>
-            <exclude>**/fixture/**</exclude>
-          </excludes>
+          <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
         </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.apache.maven.surefire</groupId>
+            <artifactId>surefire-shadefire</artifactId>
+            <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
+          </dependency>
+        </dependencies>
       </plugin>
     </plugins>
   </build>
diff --git a/surefire-providers/common-junit3/pom.xml b/surefire-providers/common-junit3/pom.xml
index 2089eec..9d314ba 100644
--- a/surefire-providers/common-junit3/pom.xml
+++ b/surefire-providers/common-junit3/pom.xml
@@ -40,4 +40,45 @@
     </dependency>
   </dependencies>
 
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>jacoco-agent</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <!--todo remove-->
+            <id>jacoco-report</id>
+            <phase>prepare-package</phase>
+            <goals>
+              <goal>report</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <propertyName>jacoco.agent</propertyName>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
+        </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.apache.maven.surefire</groupId>
+            <artifactId>surefire-shadefire</artifactId>
+            <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
+          </dependency>
+        </dependencies>
+      </plugin>
+    </plugins>
+  </build>
+
 </project>
diff --git a/surefire-providers/common-junit4/pom.xml b/surefire-providers/common-junit4/pom.xml
index 0af781c..0bcd734 100644
--- a/surefire-providers/common-junit4/pom.xml
+++ b/surefire-providers/common-junit4/pom.xml
@@ -49,4 +49,45 @@
       <version>${project.version}</version>
     </dependency>
   </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>jacoco-agent</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <!--todo remove-->
+            <id>jacoco-report</id>
+            <phase>prepare-package</phase>
+            <goals>
+              <goal>report</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <propertyName>jacoco.agent</propertyName>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
+        </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.apache.maven.surefire</groupId>
+            <artifactId>surefire-shadefire</artifactId>
+            <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
+          </dependency>
+        </dependencies>
+      </plugin>
+    </plugins>
+  </build>
 </project>
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 234bcf5..c4a7991 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
@@ -76,7 +76,7 @@ public class JUnit4RunListener
     {
         String reason = getAnnotatedIgnoreValue( description );
         ClassMethod classMethod = toClassMethod( description );
-        reporter.testSkipped( ignored( classMethod.getClazz(), classMethod.getMethod(), reason ) );
+        reporter.testSkipped( ignored( classMethod.getClazz(), null, classMethod.getMethod(), null, reason ) );
     }
 
     /**
@@ -112,7 +112,8 @@ public class JUnit4RunListener
         {
             StackTraceWriter stackTrace = createStackTraceWriter( failure );
             ClassMethod classMethod = toClassMethod( failure.getDescription() );
-            ReportEntry report = withException( classMethod.getClazz(), classMethod.getMethod(), stackTrace );
+            ReportEntry report =
+                    withException( classMethod.getClazz(), null, classMethod.getMethod(), null, stackTrace );
 
             if ( failure.getException() instanceof AssertionError )
             {
@@ -135,7 +136,8 @@ public class JUnit4RunListener
         {
             Description desc = failure.getDescription();
             ClassMethod classMethod = toClassMethod( desc );
-            ReportEntry report = assumption( classMethod.getClazz(), classMethod.getMethod(), failure.getMessage() );
+            ReportEntry report = assumption( classMethod.getClazz(), null, classMethod.getMethod(), null,
+                    failure.getMessage() );
             reporter.testAssumptionFailure( report );
         }
         finally
@@ -176,7 +178,7 @@ public class JUnit4RunListener
     protected SimpleReportEntry createReportEntry( Description description )
     {
         ClassMethod classMethod = toClassMethod( description );
-        return new SimpleReportEntry( classMethod.getClazz(), classMethod.getMethod() );
+        return new SimpleReportEntry( classMethod.getClazz(), null, classMethod.getMethod(), null );
     }
 
     public static void rethrowAnyTestMechanismFailures( Result run )
diff --git a/surefire-providers/common-junit48/pom.xml b/surefire-providers/common-junit48/pom.xml
index ddfdf75..569a1a5 100644
--- a/surefire-providers/common-junit48/pom.xml
+++ b/surefire-providers/common-junit48/pom.xml
@@ -58,12 +58,43 @@
   <build>
     <plugins>
       <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>jacoco-agent</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <!--todo remove-->
+            <id>jacoco-report</id>
+            <phase>prepare-package</phase>
+            <goals>
+              <goal>report</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <propertyName>jacoco.agent</propertyName>
+        </configuration>
+      </plugin>
+      <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
+          <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
           <includes>
             <include>**/JUnit4SuiteTest.java</include>
           </includes>
         </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.apache.maven.surefire</groupId>
+            <artifactId>surefire-shadefire</artifactId>
+            <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
+          </dependency>
+        </dependencies>
       </plugin>
     </plugins>
   </build>
diff --git a/surefire-providers/common-junit48/src/test/java/org/apache/maven/surefire/common/junit48/JUnit4SuiteTest.java b/surefire-providers/common-junit48/src/test/java/org/apache/maven/surefire/common/junit48/JUnit4SuiteTest.java
index 547c381..191e1e8 100644
--- a/surefire-providers/common-junit48/src/test/java/org/apache/maven/surefire/common/junit48/JUnit4SuiteTest.java
+++ b/surefire-providers/common-junit48/src/test/java/org/apache/maven/surefire/common/junit48/JUnit4SuiteTest.java
@@ -21,8 +21,8 @@ package org.apache.maven.surefire.common.junit48;
 
 import junit.framework.JUnit4TestAdapter;
 import junit.framework.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
 
 /**
  * Adapt the JUnit4 tests which use only annotations to the JUnit3 test suite.
@@ -30,16 +30,14 @@ import org.junit.runners.Suite;
  * @author Tibor Digana (tibor17)
  * @since 2.19
  */
-@Suite.SuiteClasses( {
-    FilterFactoryTest.class,
-    JUnit48ReflectorTest.class,
-    JUnit48TestCheckerTest.class
-} )
-@RunWith( Suite.class )
-public class JUnit4SuiteTest
+public class JUnit4SuiteTest extends TestCase
 {
     public static Test suite()
     {
-        return new JUnit4TestAdapter( JUnit4SuiteTest.class );
+        TestSuite suite = new TestSuite();
+        suite.addTestSuite( JUnit48ReflectorTest.class );
+        suite.addTest( new JUnit4TestAdapter( JUnit48TestCheckerTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( FilterFactoryTest.class ) );
+        return suite;
     }
 }
diff --git a/surefire-providers/surefire-junit-platform/pom.xml b/surefire-providers/surefire-junit-platform/pom.xml
index 8533b56..bdf5c9a 100644
--- a/surefire-providers/surefire-junit-platform/pom.xml
+++ b/surefire-providers/surefire-junit-platform/pom.xml
@@ -101,6 +101,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-reflect</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
             <scope>test</scope>
@@ -110,6 +115,29 @@
     <build>
         <plugins>
             <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>jacoco-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <!--todo remove-->
+                        <id>jacoco-report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <propertyName>jacoco.agent</propertyName>
+                </configuration>
+            </plugin>
+            <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>animal-sniffer-maven-plugin</artifactId>
                 <executions>
@@ -138,6 +166,7 @@
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
                 <configuration>
+                    <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
                     <jvm>${java.home}/bin/java</jvm>
                     <redirectTestOutputToFile>true</redirectTestOutputToFile>
                     <includes>
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 0f9f8bb..29f3eeb 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
@@ -23,6 +23,7 @@ import static java.util.Collections.emptyMap;
 import static org.apache.maven.surefire.util.internal.ObjectUtils.systemProps;
 
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -74,12 +75,12 @@ final class RunListenerAdapter
                         && testIdentifier.getSource().filter( ClassSource.class::isInstance ).isPresent() )
         {
             testStartTime.put( testIdentifier, System.currentTimeMillis() );
-            runListener.testSetStarting( createTestSetReportEntry( testIdentifier ) );
+            runListener.testSetStarting( createReportEntry( testIdentifier ) );
         }
         else if ( testIdentifier.isTest() )
         {
             testStartTime.put( testIdentifier, System.currentTimeMillis() );
-            runListener.testStarting( createTestSetReportEntry( testIdentifier ) );
+            runListener.testStarting( createReportEntry( testIdentifier ) );
         }
     }
 
@@ -104,15 +105,15 @@ final class RunListenerAdapter
                     }
                     else
                     {
-                        runListener.testSetCompleted( createTestSetReportEntry( testIdentifier, testExecutionResult,
-                                systemProps(), elapsed ) );
+                        runListener.testSetCompleted( createReportEntry( testIdentifier, testExecutionResult,
+                                systemProps(), null, elapsed ) );
                     }
                     break;
                 case FAILED:
                     if ( !isTest )
                     {
-                        runListener.testSetCompleted( createTestSetReportEntry( testIdentifier, testExecutionResult,
-                                systemProps(), elapsed ) );
+                        runListener.testSetCompleted( createReportEntry( testIdentifier, testExecutionResult,
+                                systemProps(), null, elapsed ) );
                     }
                     else if ( testExecutionResult.getThrowable()
                             .filter( AssertionError.class::isInstance ).isPresent() )
@@ -127,12 +128,12 @@ final class RunListenerAdapter
                 default:
                     if ( isTest )
                     {
-                        runListener.testSucceeded( createReportEntry( testIdentifier, elapsed ) );
+                        runListener.testSucceeded( createReportEntry( testIdentifier, null, elapsed ) );
                     }
                     else
                     {
                         runListener.testSetCompleted(
-                                createTestSetReportEntry( testIdentifier, null, systemProps(), elapsed ) );
+                                createReportEntry( testIdentifier, null, systemProps(), null, elapsed ) );
                     }
             }
         }
@@ -149,58 +150,43 @@ final class RunListenerAdapter
     public void executionSkipped( TestIdentifier testIdentifier, String reason )
     {
         testStartTime.remove( testIdentifier );
-        String[] classMethodName = toClassMethodName( testIdentifier );
-        String className = classMethodName[1];
-        String methodName = classMethodName[3];
-        runListener.testSkipped( new SimpleReportEntry( className, methodName, reason ) );
+        runListener.testSkipped( createReportEntry( testIdentifier, null, emptyMap(), reason, null ) );
     }
 
-    private SimpleReportEntry createTestSetReportEntry( TestIdentifier testIdentifier,
-                                                        TestExecutionResult testExecutionResult,
-                                                        Map<String, String> systemProperties,
-                                                        Integer elapsedTime )
+    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier,
+                                                 TestExecutionResult testExecutionResult,
+                                                 Map<String, String> systemProperties,
+                                                 String reason,
+                                                 Integer elapsedTime )
     {
         String[] classMethodName = toClassMethodName( testIdentifier );
-        String className = classMethodName[1];
-        String methodName = classMethodName[3];
+        String className = classMethodName[0];
+        String classText = classMethodName[1];
+        if ( Objects.equals( className, classText ) )
+        {
+            classText = null;
+        }
+        String methodName = testIdentifier.isTest() ? classMethodName[2] : null;
+        String methodText = testIdentifier.isTest() ? classMethodName[3] : null;
+        if ( Objects.equals( methodName, methodText ) )
+        {
+            methodText = null;
+        }
         StackTraceWriter stw =
                 testExecutionResult == null ? null : toStackTraceWriter( className, methodName, testExecutionResult );
-        return new SimpleReportEntry( className, methodName, stw, elapsedTime, systemProperties );
+        return new SimpleReportEntry( className, classText, methodName, methodText,
+                stw, elapsedTime, reason, systemProperties );
     }
 
-    private SimpleReportEntry createTestSetReportEntry( TestIdentifier testIdentifier )
+    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier )
     {
-        return createTestSetReportEntry( testIdentifier, emptyMap() );
-    }
-
-    private SimpleReportEntry createTestSetReportEntry( TestIdentifier testIdentifier,
-                                                        Map<String, String> systemProperties )
-    {
-        return createTestSetReportEntry( testIdentifier, null, systemProperties, null );
-    }
-
-    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier, Integer elapsedTime )
-    {
-        return createReportEntry( testIdentifier, (StackTraceWriter) null, elapsedTime );
+        return createReportEntry( testIdentifier, null, null );
     }
 
     private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier,
                                                  TestExecutionResult testExecutionResult, Integer elapsedTime )
     {
-        String[] classMethodNames = toClassMethodName( testIdentifier );
-        String realClassName = classMethodNames[0];
-        String realMethodName = classMethodNames[2];
-        return createReportEntry( testIdentifier,
-                toStackTraceWriter( realClassName, realMethodName, testExecutionResult ), elapsedTime );
-    }
-
-    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier, StackTraceWriter stackTraceWriter,
-                                                 Integer elapsedTime )
-    {
-        String[] classMethodNames = toClassMethodName( testIdentifier );
-        String className = classMethodNames[1];
-        String methodName = classMethodNames[3];
-        return new SimpleReportEntry( className, methodName, stackTraceWriter, elapsedTime );
+        return createReportEntry( testIdentifier, testExecutionResult, emptyMap(), null, elapsedTime );
     }
 
     private StackTraceWriter toStackTraceWriter( String realClassName, String realMethodName,
@@ -263,7 +249,7 @@ final class RunListenerAdapter
             String className = classSource.getClassName();
             String simpleClassName = className.substring( 1 + className.lastIndexOf( '.' ) );
             String source = display.equals( simpleClassName ) ? className : display;
-            return new String[] { source, source, null, null };
+            return new String[] { className, source, null, null };
         }
         else
         {
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnit47SuiteTest.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnit47SuiteTest.java
index d0d4af9..7db25c9 100644
--- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnit47SuiteTest.java
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnit47SuiteTest.java
@@ -21,26 +21,23 @@ package org.apache.maven.surefire.junitplatform;
 
 import junit.framework.JUnit4TestAdapter;
 import junit.framework.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-import org.junit.runners.Suite.SuiteClasses;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
 
 /**
  * Adapt the JUnit4 tests which use only annotations to the JUnit3 test suite.
  *
  * @since 3.0.0-M4
  */
-@SuiteClasses( {
-        JUnitPlatformProviderTest.class,
-        RunListenerAdapterTest.class,
-        TestMethodFilterTest.class,
-        TestPlanScannerFilterTest.class
-} )
-@RunWith( Suite.class )
-public class JUnit47SuiteTest
+public class JUnit47SuiteTest extends TestCase
 {
     public static Test suite()
     {
-        return new JUnit4TestAdapter( JUnit47SuiteTest.class );
+        TestSuite suite = new TestSuite();
+        suite.addTest( new JUnit4TestAdapter( JUnitPlatformProviderTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( RunListenerAdapterTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( TestMethodFilterTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( TestPlanScannerFilterTest.class ) );
+        return suite;
     }
 }
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 bbc4457..e9c53ba 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
@@ -33,8 +33,10 @@ import static org.junit.platform.engine.TestExecutionResult.failed;
 import static org.junit.platform.engine.TestExecutionResult.successful;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.*;
+import static org.powermock.reflect.Whitebox.getInternalState;
 
 import java.lang.reflect.Method;
+import java.util.Map;
 import java.util.Optional;
 
 import org.apache.maven.surefire.report.PojoStackTraceWriter;
@@ -50,7 +52,6 @@ import org.junit.jupiter.engine.descriptor.ClassTestDescriptor;
 import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;
 import org.junit.platform.engine.ConfigurationParameters;
 import org.junit.platform.engine.TestDescriptor;
-import org.junit.platform.engine.TestDescriptor.Type;
 import org.junit.platform.engine.TestSource;
 import org.junit.platform.engine.UniqueId;
 import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;
@@ -121,8 +122,10 @@ public class RunListenerAdapterTest
         verify( listener ).testStarting( entryCaptor.capture() );
 
         ReportEntry entry = entryCaptor.getValue();
-        assertEquals( MY_TEST_METHOD_NAME + "(String)", entry.getName() );
+        assertEquals( MY_TEST_METHOD_NAME, entry.getName() );
+        assertEquals( MY_TEST_METHOD_NAME + "(String)", entry.getNameText() );
         assertEquals( MyTestClass.class.getName(), entry.getSourceName() );
+        assertNull( entry.getSourceText() );
         assertNull( entry.getStackTraceWriter() );
     }
 
@@ -137,23 +140,31 @@ public class RunListenerAdapterTest
         parent.addChild( child );
         TestPlan plan = TestPlan.from( singletonList( engine ) );
 
+        String className = MyTestClass.class.getName();
+
         adapter.testPlanExecutionStarted( plan );
         adapter.executionStarted( TestIdentifier.from( engine ) );
         adapter.executionStarted( TestIdentifier.from( parent ) );
-        verify( listener ).testSetStarting( new SimpleReportEntry( MyTestClass.class.getName(), null ) );
+        verify( listener )
+                .testSetStarting( new SimpleReportEntry( className, null, null, null ) );
         verifyNoMoreInteractions( listener );
 
         adapter.executionStarted( TestIdentifier.from( child ) );
-        verify( listener ).testStarting( new SimpleReportEntry( MyTestClass.class.getName(), MY_TEST_METHOD_NAME ) );
+        verify( listener )
+                .testStarting( new SimpleReportEntry( 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().getSourceName() )
-                .isEqualTo( MyTestClass.class.getName() );
+                .isEqualTo( className );
+        assertThat( report.getValue().getSourceText() )
+                .isNull();
         assertThat( report.getValue().getName() )
                 .isEqualTo( MY_TEST_METHOD_NAME );
+        assertThat( report.getValue().getNameText() )
+                .isNull();
         assertThat( report.getValue().getElapsed() )
                 .isNotNull();
         assertThat( report.getValue().getSystemProperties() )
@@ -164,7 +175,7 @@ public class RunListenerAdapterTest
         report = ArgumentCaptor.forClass( SimpleReportEntry.class );
         verify( listener ).testSetCompleted( report.capture() );
         assertThat( report.getValue().getSourceName() )
-                .isEqualTo( MyTestClass.class.getName() );
+                .isEqualTo( className );
         assertThat( report.getValue().getName() )
                 .isNull();
         assertThat( report.getValue().getElapsed() )
@@ -179,14 +190,22 @@ public class RunListenerAdapterTest
 
     @Test
     public void displayNamesInClassAndMethods()
+            throws Exception
     {
         EngineDescriptor engine = newEngineDescriptor();
         TestDescriptor parent = newClassDescriptor( "parent" );
         engine.addChild( parent );
-        TestDescriptor child1 = newTestDescriptor( parent.getUniqueId().append( "test", "child1" ), "child1", TEST );
+
+        UniqueId id1 = parent.getUniqueId().append( MyTestClass.class.getName(), MY_NAMED_TEST_METHOD_NAME );
+        Method m1 = MyTestClass.class.getDeclaredMethod( MY_NAMED_TEST_METHOD_NAME );
+        TestDescriptor child1 = new TestMethodTestDescriptorWithDisplayName( id1, MyTestClass.class, m1, "dn1" );
         parent.addChild( child1 );
-        TestDescriptor child2 = newTestDescriptor( parent.getUniqueId().append( "test", "child2" ), "child2", TEST );
+
+        UniqueId id2 = parent.getUniqueId().append( MyTestClass.class.getName(), MY_TEST_METHOD_NAME );
+        Method m2 = MyTestClass.class.getDeclaredMethod( MY_TEST_METHOD_NAME, String.class );
+        TestDescriptor child2 = new TestMethodTestDescriptor( id2, MyTestClass.class, m2 );
         parent.addChild( child2 );
+
         TestPlan plan = TestPlan.from( singletonList( engine ) );
 
         InOrder inOrder = inOrder( listener );
@@ -198,6 +217,8 @@ public class RunListenerAdapterTest
         ArgumentCaptor<SimpleReportEntry> report = ArgumentCaptor.forClass( SimpleReportEntry.class );
         inOrder.verify( listener ).testSetStarting( report.capture() );
         assertThat( report.getValue().getSourceName() )
+                .isEqualTo( MyTestClass.class.getName() );
+        assertThat( report.getValue().getSourceText() )
                 .isEqualTo( "parent" );
         assertThat( report.getValue().getName() )
                 .isNull();
@@ -206,53 +227,76 @@ public class RunListenerAdapterTest
         verifyZeroInteractions( listener );
 
         adapter.executionStarted( TestIdentifier.from( child1 ) );
-        inOrder.verify( listener ).testStarting( new SimpleReportEntry( "parent", "child1" ) );
-        verifyNoMoreInteractions( listener );
+        inOrder.verify( listener )
+                .testStarting( new SimpleReportEntry( 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().getSourceName() )
+                .isEqualTo( MyTestClass.class.getName() );
+        assertThat( report.getValue().getSourceText() )
                 .isEqualTo( "parent" );
         assertThat( report.getValue().getName() )
-                .isEqualTo( "child1" );
+                .isEqualTo( MY_NAMED_TEST_METHOD_NAME );
+        assertThat( report.getValue().getNameText() )
+                .isEqualTo( "dn1" );
         assertThat( report.getValue().getElapsed() )
                 .isNotNull();
         assertThat( report.getValue().getSystemProperties() )
                 .isEmpty();
-        verifyNoMoreInteractions( listener );
+        inOrder.verifyNoMoreInteractions();
 
         adapter.executionStarted( TestIdentifier.from( child2 ) );
-        inOrder.verify( listener ).testStarting( new SimpleReportEntry( "parent", "child2" ) );
-        verifyNoMoreInteractions( listener );
+        inOrder.verify( listener )
+                .testStarting( new SimpleReportEntry( MyTestClass.class.getName(), "parent",
+                        MY_TEST_METHOD_NAME, MY_TEST_METHOD_NAME + "(String)" ) );
+        inOrder.verifyNoMoreInteractions();
 
-        adapter.executionFinished( TestIdentifier.from( child2 ), successful() );
+        Exception assumptionFailure = new Exception();
+        adapter.executionFinished( TestIdentifier.from( child2 ), aborted( assumptionFailure ) );
         report = ArgumentCaptor.forClass( SimpleReportEntry.class );
-        inOrder.verify( listener ).testSucceeded( report.capture() );
+        inOrder.verify( listener ).testAssumptionFailure( report.capture() );
         assertThat( report.getValue().getSourceName() )
+                .isEqualTo( MyTestClass.class.getName() );
+        assertThat( report.getValue().getSourceText() )
                 .isEqualTo( "parent" );
         assertThat( report.getValue().getName() )
-                .isEqualTo( "child2" );
+                .isEqualTo( MY_TEST_METHOD_NAME );
+        assertThat( report.getValue().getNameText() )
+                .isEqualTo( MY_TEST_METHOD_NAME + "(String)" );
         assertThat( report.getValue().getElapsed() )
                 .isNotNull();
         assertThat( report.getValue().getSystemProperties() )
                 .isEmpty();
-        verifyNoMoreInteractions( listener );
+        assertThat( report.getValue().getStackTraceWriter() )
+                .isNotNull();
+        assertThat( report.getValue().getStackTraceWriter().getThrowable().getTarget() )
+                .isSameAs(assumptionFailure);
+        inOrder.verifyNoMoreInteractions();
 
         adapter.executionFinished( TestIdentifier.from( parent ), successful() );
         inOrder.verify( listener ).testSetCompleted( report.capture() );
         assertThat( report.getValue().getSourceName() )
+                .isEqualTo( MyTestClass.class.getName() );
+        assertThat( report.getValue().getSourceText() )
                 .isEqualTo( "parent" );
         assertThat( report.getValue().getName() )
                 .isNull();
+        assertThat( report.getValue().getNameText() )
+                .isNull();
         assertThat( report.getValue().getElapsed() )
                 .isNotNull();
         assertThat( report.getValue().getSystemProperties() )
                 .isNotEmpty();
-        verifyNoMoreInteractions( listener );
+        assertThat( report.getValue().getStackTraceWriter() )
+                .isNull();
+        inOrder.verifyNoMoreInteractions();
 
         adapter.executionFinished( TestIdentifier.from( engine ), successful() );
-        verifyNoMoreInteractions( listener );
+        inOrder.verifyNoMoreInteractions();
     }
 
     @Test
@@ -269,8 +313,15 @@ public class RunListenerAdapterTest
         TestPlan plan = TestPlan.from( singletonList( engine ) );
 
         adapter.testPlanExecutionStarted( plan );
+        assertThat( (TestPlan) getInternalState( adapter, "testPlan" ) )
+                .isSameAs( plan );
+        assertThat( (Map) getInternalState( adapter, "testStartTime" ) )
+                .isEmpty();
+
+
         adapter.executionStarted( TestIdentifier.from( engine ) );
-        verify( listener ).testStarting( new SimpleReportEntry( "engine", "engine" ) );
+        verify( listener )
+                .testStarting( new SimpleReportEntry( "engine", null, "engine", null ) );
         verifyNoMoreInteractions( listener );
 
         adapter.executionFinished( TestIdentifier.from( engine ), successful() );
@@ -278,12 +329,25 @@ public class RunListenerAdapterTest
         verify( listener ).testSucceeded( report.capture() );
         assertThat( report.getValue().getSourceName() )
                 .isEqualTo( "engine" );
+        assertThat( report.getValue().getSourceText() )
+                .isNull();
         assertThat( report.getValue().getName() )
                 .isEqualTo( "engine" );
-        assertThat( report.getValue().getElapsed() )
+        assertThat( report.getValue().getNameText() )
+                .isNull();
+        assertThat(report.getValue().getElapsed())
                 .isNotNull();
+        assertThat( report.getValue().getStackTraceWriter() )
+                .isNull();
         assertThat( report.getValue().getSystemProperties() )
                 .isEmpty();
+
+        adapter.testPlanExecutionFinished( plan );
+        assertThat( (TestPlan) getInternalState( adapter, "testPlan" ) )
+                .isNull();
+        assertThat( (Map) getInternalState( adapter, "testStartTime" ) )
+                .isEmpty();
+
         verifyNoMoreInteractions( listener );
     }
 
@@ -405,20 +469,34 @@ public class RunListenerAdapterTest
 
         adapter.executionFinished( TestIdentifier.from( classDescriptor ), successful() );
 
-        verify( listener ).testSetStarting( new SimpleReportEntry( MyTestClass.class.getName(), null ) );
+        String className = MyTestClass.class.getName();
+
+        verify( listener )
+                .testSetStarting( new SimpleReportEntry( className, null, null, null ) );
 
         ArgumentCaptor<SimpleReportEntry> report = ArgumentCaptor.forClass( SimpleReportEntry.class );
-        verify( listener ).testSetCompleted( report.capture() );
+        verify( listener )
+                .testSetCompleted(report.capture() );
+
         assertThat( report.getValue().getSourceName() )
-                .isEqualTo( MyTestClass.class.getName() );
+                .isEqualTo( className );
+        assertThat( report.getValue().getSourceText() )
+                .isNull();
         assertThat( report.getValue().getName() )
                 .isNull();
+        assertThat( report.getValue().getNameText() )
+                .isNull();
         assertThat( report.getValue().getStackTraceWriter() )
                 .isNull();
         assertThat( report.getValue().getElapsed() )
                 .isNotNull();
+        assertThat( report.getValue().getSystemProperties() )
+                .isNotEmpty();
 
-        verify( listener, never() ).testSucceeded( any() );
+        verify( listener, never() )
+                .testSucceeded(any() );
+
+        verifyNoMoreInteractions( listener );
     }
 
     @Test
@@ -438,6 +516,9 @@ public class RunListenerAdapterTest
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
         verify( listener ).testStarting( entryCaptor.capture() );
         assertEquals( parentDisplay, entryCaptor.getValue().getSourceName() );
+        assertNull(entryCaptor.getValue().getSourceText());
+        assertNull( entryCaptor.getValue().getName() );
+        assertNull( entryCaptor.getValue().getNameText() );
     }
 
     @Test
@@ -484,22 +565,8 @@ public class RunListenerAdapterTest
     public void displayNamesIgnoredInReport()
                     throws NoSuchMethodException
     {
-        class TestMethodTestDescriptorWithDisplayName extends AbstractTestDescriptor
-        {
-            private TestMethodTestDescriptorWithDisplayName( UniqueId uniqueId, Class<?> testClass, Method testMethod )
-            {
-                super( uniqueId, "some display name", MethodSource.from( testClass, testMethod ) );
-            }
-
-            @Override
-            public Type getType()
-            {
-                return Type.TEST;
-            }
-        }
-
         TestMethodTestDescriptorWithDisplayName descriptor = new TestMethodTestDescriptorWithDisplayName( newId(),
-                MyTestClass.class, MyTestClass.class.getDeclaredMethod( "myNamedTestMethod" ) );
+                MyTestClass.class, MyTestClass.class.getDeclaredMethod( "myNamedTestMethod" ), "some display name" );
 
         TestIdentifier factoryIdentifier = TestIdentifier.from( descriptor );
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
@@ -509,7 +576,10 @@ public class RunListenerAdapterTest
 
         ReportEntry value = entryCaptor.getValue();
 
-        assertEquals( "some display name", value.getName() );
+        assertEquals( MyTestClass.class.getName(), value.getSourceName() );
+        assertNull(value.getSourceText());
+        assertEquals( "myNamedTestMethod", value.getName() );
+        assertEquals( "some display name", value.getNameText() );
     }
 
     private static TestIdentifier newMethodIdentifier()
@@ -584,18 +654,6 @@ public class RunListenerAdapterTest
         return new EngineDescriptor( UniqueId.forEngine( "engine" ), "engine" );
     }
 
-    private TestDescriptor newTestDescriptor( UniqueId uniqueId, String displayName, Type type )
-    {
-        return new AbstractTestDescriptor( uniqueId, displayName )
-        {
-            @Override
-            public Type getType()
-            {
-                return type;
-            }
-        };
-    }
-
     private static TestIdentifier identifiersAsParentOnTestPlan(
                     TestPlan plan, TestDescriptor parent, TestDescriptor child )
     {
@@ -623,6 +681,7 @@ public class RunListenerAdapterTest
     }
 
     private static final String MY_TEST_METHOD_NAME = "myTestMethod";
+    private static final String MY_NAMED_TEST_METHOD_NAME = "myNamedTestMethod";
 
     private static class MyTestClass
     {
@@ -642,4 +701,19 @@ public class RunListenerAdapterTest
         {
         }
     }
+
+    static class TestMethodTestDescriptorWithDisplayName extends AbstractTestDescriptor
+    {
+        private TestMethodTestDescriptorWithDisplayName( UniqueId uniqueId,
+                                                         Class<?> testClass, Method testMethod, String displayName )
+        {
+            super( uniqueId, displayName, MethodSource.from( testClass, testMethod ) );
+        }
+
+        @Override
+        public Type getType()
+        {
+            return Type.TEST;
+        }
+    }
 }
diff --git a/surefire-providers/surefire-junit3/pom.xml b/surefire-providers/surefire-junit3/pom.xml
index 44d608e..a163a7f 100644
--- a/surefire-providers/surefire-junit3/pom.xml
+++ b/surefire-providers/surefire-junit3/pom.xml
@@ -44,4 +44,45 @@
       <version>${project.version}</version>
     </dependency>
   </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>jacoco-agent</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <!--todo remove-->
+            <id>jacoco-report</id>
+            <phase>prepare-package</phase>
+            <goals>
+              <goal>report</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <propertyName>jacoco.agent</propertyName>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
+        </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.apache.maven.surefire</groupId>
+            <artifactId>surefire-shadefire</artifactId>
+            <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
+          </dependency>
+        </dependencies>
+      </plugin>
+    </plugins>
+  </build>
 </project>
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 bfdb4eb..d44d92b 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
@@ -133,9 +133,9 @@ public class JUnit3Provider
                                  Map<String, String> systemProperties )
         throws TestSetFailedException
     {
-        reporter.testSetStarting( new SimpleReportEntry( testSet.getName(), null ) );
+        reporter.testSetStarting( new SimpleReportEntry( testSet.getName(), null, null, null ) );
         testSet.execute( reporter, classLoader );
-        reporter.testSetCompleted( new SimpleReportEntry( testSet.getName(), null, systemProperties ) );
+        reporter.testSetCompleted( new SimpleReportEntry( testSet.getName(), null, null, null, systemProperties ) );
     }
 
     private TestsToRun scanClassPath()
diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/PojoTestSet.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/PojoTestSet.java
index 1b23322..7ee787e 100644
--- a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/PojoTestSet.java
+++ b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/PojoTestSet.java
@@ -116,7 +116,7 @@ public class PojoTestSet
         final String userFriendlyMethodName = methodName + '(' + ( args.length == 0 ? "" : "Reporter" ) + ')';
         final String testName = getTestName( userFriendlyMethodName );
 
-        reportManager.testStarting( new SimpleReportEntry( testClassName, testName ) );
+        reportManager.testStarting( new SimpleReportEntry( testClassName, null, testName, null ) );
 
         try
         {
@@ -125,7 +125,7 @@ public class PojoTestSet
         catch ( Throwable e )
         {
             StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( testClassName, methodName, e );
-            reportManager.testFailed( withException( testClassName, testName, stackTraceWriter ) );
+            reportManager.testFailed( withException( 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
@@ -139,20 +139,20 @@ public class PojoTestSet
         try
         {
             method.invoke( testObject, args );
-            reportManager.testSucceeded( new SimpleReportEntry( testClassName, testName ) );
+            reportManager.testSucceeded( new SimpleReportEntry( testClassName, null, testName, null ) );
         }
         catch ( InvocationTargetException e )
         {
             Throwable t = e.getTargetException();
             StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( testClassName, methodName, t );
-            reportManager.testFailed( withException( testClassName, testName, stackTraceWriter ) );
+            reportManager.testFailed( withException( 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, testName, stackTraceWriter ) );
+            reportManager.testFailed( withException( testClassName, null, testName, null, stackTraceWriter ) );
             // Don't return  here, because tearDownFixture should be called even
             // if the test method throws an exception.
         }
@@ -165,7 +165,7 @@ public class PojoTestSet
         {
             StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( testClassName, methodName, t );
             // Treat any exception from tearDownFixture as a failure of the test.
-            reportManager.testFailed( withException( testClassName, testName, stackTraceWriter ) );
+            reportManager.testFailed( withException( 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/TestListenerInvocationHandler.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/TestListenerInvocationHandler.java
index e5d2232..db0563a 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
@@ -217,12 +217,12 @@ public class TestListenerInvocationHandler
         String className = extractClassName( description );
         String methodName = extractMethodName( description );
         StackTraceWriter stackTraceWriter = toStackTraceWriter( args );
-        return withException( className, methodName, stackTraceWriter );
+        return withException( className, null, methodName, null, stackTraceWriter );
     }
 
     private static SimpleReportEntry createStartEndReportEntry( Object[] args )
     {
         String description = args[0].toString();
-        return new SimpleReportEntry( extractClassName( description ), extractMethodName( description ) );
+        return new SimpleReportEntry( extractClassName( description ), null, extractMethodName( description ), null );
     }
 }
diff --git a/surefire-providers/surefire-junit4/pom.xml b/surefire-providers/surefire-junit4/pom.xml
index d3816ad..693185a 100644
--- a/surefire-providers/surefire-junit4/pom.xml
+++ b/surefire-providers/surefire-junit4/pom.xml
... 565 lines suppressed ...


[maven-surefire] 02/02: removed temporary execution 'jacoco-report' extension SurefireConsoleOutputReporter should be used instead of ConsoleOutputReporter

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 69020a30ae0b1c18cc6abf04d5604d3e7d117641
Author: tibordigana <ti...@apache.org>
AuthorDate: Thu May 23 00:29:54 2019 +0200

    removed temporary execution 'jacoco-report'
    extension SurefireConsoleOutputReporter should be used instead of ConsoleOutputReporter
---
 maven-failsafe-plugin/pom.xml                                 |  8 --------
 maven-surefire-common/pom.xml                                 |  8 --------
 .../maven/plugin/surefire/StartupReportConfiguration.java     | 11 ++++-------
 maven-surefire-plugin/pom.xml                                 |  8 --------
 maven-surefire-report-plugin/pom.xml                          |  8 --------
 surefire-api/pom.xml                                          |  8 --------
 surefire-booter/pom.xml                                       |  8 --------
 surefire-extensions-api/pom.xml                               |  8 --------
 surefire-grouper/pom.xml                                      |  8 --------
 surefire-logger-api/pom.xml                                   |  8 --------
 surefire-providers/common-java5/pom.xml                       |  8 --------
 surefire-providers/common-junit3/pom.xml                      |  8 --------
 surefire-providers/common-junit4/pom.xml                      |  8 --------
 surefire-providers/common-junit48/pom.xml                     |  8 --------
 surefire-providers/surefire-junit-platform/pom.xml            |  8 --------
 surefire-providers/surefire-junit3/pom.xml                    |  8 --------
 surefire-providers/surefire-junit4/pom.xml                    |  8 --------
 surefire-providers/surefire-junit47/pom.xml                   |  8 --------
 surefire-providers/surefire-testng-utils/pom.xml              |  8 --------
 surefire-providers/surefire-testng/pom.xml                    |  8 --------
 surefire-report-parser/pom.xml                                |  8 --------
 21 files changed, 4 insertions(+), 167 deletions(-)

diff --git a/maven-failsafe-plugin/pom.xml b/maven-failsafe-plugin/pom.xml
index 9928f11..bf61284 100644
--- a/maven-failsafe-plugin/pom.xml
+++ b/maven-failsafe-plugin/pom.xml
@@ -128,14 +128,6 @@
                             <goal>prepare-agent</goal>
                         </goals>
                     </execution>
-                    <execution>
-                        <!--todo remove-->
-                        <id>jacoco-report</id>
-                        <phase>prepare-package</phase>
-                        <goals>
-                            <goal>report</goal>
-                        </goals>
-                    </execution>
                 </executions>
                 <configuration>
                     <propertyName>jacoco.agent</propertyName>
diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml
index ff7eb97..896c8be 100644
--- a/maven-surefire-common/pom.xml
+++ b/maven-surefire-common/pom.xml
@@ -162,14 +162,6 @@
                             <goal>restore-instrumented-classes</goal>
                         </goals>
                     </execution>
-                    <execution>
-                        <!--todo remove-->
-                        <id>jacoco-report</id>
-                        <phase>prepare-package</phase>
-                        <goals>
-                            <goal>report</goal>
-                        </goals>
-                    </execution>
                 </executions>
             </plugin>
             <plugin>
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
index 2b82afe..02ec44b 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
@@ -28,12 +28,9 @@ import org.apache.maven.plugin.surefire.report.TestSetStats;
 import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
 import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
-import org.apache.maven.surefire.extensions.ConsoleOutputReporter;
 import org.apache.maven.surefire.extensions.StatelessReportEventListener;
-import org.apache.maven.surefire.extensions.StatelessReporter;
 import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
 import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
-import org.apache.maven.surefire.extensions.StatelessTestsetInfoReporter;
 
 import javax.annotation.Nonnull;
 import java.io.File;
@@ -91,7 +88,7 @@ public final class StartupReportConfiguration
 
     private final SurefireStatelessReporter xmlReporter;
 
-    private final ConsoleOutputReporter consoleOutputReporter;
+    private final SurefireConsoleOutputReporter consoleOutputReporter;
 
     private final SurefireStatelessTestsetInfoReporter testsetReporter;
 
@@ -256,17 +253,17 @@ public final class StartupReportConfiguration
         return forkNumber == null ? reportsDirectory : replaceForkThreadsInPath( reportsDirectory, forkNumber );
     }
 
-    public StatelessReporter<WrappedReportEntry, TestSetStats, DefaultStatelessReportMojoConfiguration> getXmlReporter()
+    public SurefireStatelessReporter getXmlReporter()
     {
         return xmlReporter;
     }
 
-    public ConsoleOutputReporter getConsoleOutputReporter()
+    public SurefireConsoleOutputReporter getConsoleOutputReporter()
     {
         return consoleOutputReporter;
     }
 
-    public StatelessTestsetInfoReporter<WrappedReportEntry, TestSetStats> getTestsetReporter()
+    public SurefireStatelessTestsetInfoReporter getTestsetReporter()
     {
         return testsetReporter;
     }
diff --git a/maven-surefire-plugin/pom.xml b/maven-surefire-plugin/pom.xml
index 5a1261f..c1c25cf 100644
--- a/maven-surefire-plugin/pom.xml
+++ b/maven-surefire-plugin/pom.xml
@@ -95,14 +95,6 @@
                             <goal>prepare-agent</goal>
                         </goals>
                     </execution>
-                    <execution>
-                        <!--todo remove-->
-                        <id>jacoco-report</id>
-                        <phase>prepare-package</phase>
-                        <goals>
-                            <goal>report</goal>
-                        </goals>
-                    </execution>
                 </executions>
                 <configuration>
                     <propertyName>jacoco.agent</propertyName>
diff --git a/maven-surefire-report-plugin/pom.xml b/maven-surefire-report-plugin/pom.xml
index 5f855d0..49eeb5f 100644
--- a/maven-surefire-report-plugin/pom.xml
+++ b/maven-surefire-report-plugin/pom.xml
@@ -188,14 +188,6 @@
                             <goal>prepare-agent</goal>
                         </goals>
                     </execution>
-                    <execution>
-                        <!--todo remove-->
-                        <id>jacoco-report</id>
-                        <phase>prepare-package</phase>
-                        <goals>
-                            <goal>report</goal>
-                        </goals>
-                    </execution>
                 </executions>
                 <configuration>
                     <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-api/pom.xml b/surefire-api/pom.xml
index e791c9d..65ca886 100644
--- a/surefire-api/pom.xml
+++ b/surefire-api/pom.xml
@@ -69,14 +69,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-booter/pom.xml b/surefire-booter/pom.xml
index 1fe33e7..460293d 100644
--- a/surefire-booter/pom.xml
+++ b/surefire-booter/pom.xml
@@ -112,14 +112,6 @@
               <goal>restore-instrumented-classes</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
       </plugin>
       <plugin>
diff --git a/surefire-extensions-api/pom.xml b/surefire-extensions-api/pom.xml
index db0aafa..bfce047 100644
--- a/surefire-extensions-api/pom.xml
+++ b/surefire-extensions-api/pom.xml
@@ -73,14 +73,6 @@
                             <goal>restore-instrumented-classes</goal>
                         </goals>
                     </execution>
-                    <execution>
-                        <!--todo remove-->
-                        <id>jacoco-report</id>
-                        <phase>prepare-package</phase>
-                        <goals>
-                            <goal>report</goal>
-                        </goals>
-                    </execution>
                 </executions>
             </plugin>
             <plugin>
diff --git a/surefire-grouper/pom.xml b/surefire-grouper/pom.xml
index d3ae750..96c7bc4 100644
--- a/surefire-grouper/pom.xml
+++ b/surefire-grouper/pom.xml
@@ -61,14 +61,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-logger-api/pom.xml b/surefire-logger-api/pom.xml
index 429c754..5f0ea0d 100644
--- a/surefire-logger-api/pom.xml
+++ b/surefire-logger-api/pom.xml
@@ -69,14 +69,6 @@
                             <goal>prepare-agent</goal>
                         </goals>
                     </execution>
-                    <execution>
-                        <!--todo remove-->
-                        <id>jacoco-report</id>
-                        <phase>prepare-package</phase>
-                        <goals>
-                            <goal>report</goal>
-                        </goals>
-                    </execution>
                 </executions>
                 <configuration>
                     <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-providers/common-java5/pom.xml b/surefire-providers/common-java5/pom.xml
index 7ecf6af..6beeff4 100644
--- a/surefire-providers/common-java5/pom.xml
+++ b/surefire-providers/common-java5/pom.xml
@@ -50,14 +50,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-providers/common-junit3/pom.xml b/surefire-providers/common-junit3/pom.xml
index 9d314ba..bef14cb 100644
--- a/surefire-providers/common-junit3/pom.xml
+++ b/surefire-providers/common-junit3/pom.xml
@@ -52,14 +52,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-providers/common-junit4/pom.xml b/surefire-providers/common-junit4/pom.xml
index 0bcd734..09bf4cd 100644
--- a/surefire-providers/common-junit4/pom.xml
+++ b/surefire-providers/common-junit4/pom.xml
@@ -62,14 +62,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-providers/common-junit48/pom.xml b/surefire-providers/common-junit48/pom.xml
index 569a1a5..006b78e 100644
--- a/surefire-providers/common-junit48/pom.xml
+++ b/surefire-providers/common-junit48/pom.xml
@@ -67,14 +67,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-providers/surefire-junit-platform/pom.xml b/surefire-providers/surefire-junit-platform/pom.xml
index bdf5c9a..823885f 100644
--- a/surefire-providers/surefire-junit-platform/pom.xml
+++ b/surefire-providers/surefire-junit-platform/pom.xml
@@ -124,14 +124,6 @@
                             <goal>prepare-agent</goal>
                         </goals>
                     </execution>
-                    <execution>
-                        <!--todo remove-->
-                        <id>jacoco-report</id>
-                        <phase>prepare-package</phase>
-                        <goals>
-                            <goal>report</goal>
-                        </goals>
-                    </execution>
                 </executions>
                 <configuration>
                     <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-providers/surefire-junit3/pom.xml b/surefire-providers/surefire-junit3/pom.xml
index a163a7f..d38b312 100644
--- a/surefire-providers/surefire-junit3/pom.xml
+++ b/surefire-providers/surefire-junit3/pom.xml
@@ -57,14 +57,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-providers/surefire-junit4/pom.xml b/surefire-providers/surefire-junit4/pom.xml
index 693185a..fc4d450 100644
--- a/surefire-providers/surefire-junit4/pom.xml
+++ b/surefire-providers/surefire-junit4/pom.xml
@@ -57,14 +57,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-providers/surefire-junit47/pom.xml b/surefire-providers/surefire-junit47/pom.xml
index af32cc0..d08b0b4 100644
--- a/surefire-providers/surefire-junit47/pom.xml
+++ b/surefire-providers/surefire-junit47/pom.xml
@@ -68,14 +68,6 @@
                             <goal>prepare-agent</goal>
                         </goals>
                     </execution>
-                    <execution>
-                        <!--todo remove-->
-                        <id>jacoco-report</id>
-                        <phase>prepare-package</phase>
-                        <goals>
-                            <goal>report</goal>
-                        </goals>
-                    </execution>
                 </executions>
                 <configuration>
                     <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-providers/surefire-testng-utils/pom.xml b/surefire-providers/surefire-testng-utils/pom.xml
index bfc4b55..368e050 100644
--- a/surefire-providers/surefire-testng-utils/pom.xml
+++ b/surefire-providers/surefire-testng-utils/pom.xml
@@ -64,14 +64,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-providers/surefire-testng/pom.xml b/surefire-providers/surefire-testng/pom.xml
index d3d308d..df1bf4f 100644
--- a/surefire-providers/surefire-testng/pom.xml
+++ b/surefire-providers/surefire-testng/pom.xml
@@ -69,14 +69,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>
diff --git a/surefire-report-parser/pom.xml b/surefire-report-parser/pom.xml
index d1e2854..8620896 100644
--- a/surefire-report-parser/pom.xml
+++ b/surefire-report-parser/pom.xml
@@ -63,14 +63,6 @@
               <goal>prepare-agent</goal>
             </goals>
           </execution>
-          <execution>
-            <!--todo remove-->
-            <id>jacoco-report</id>
-            <phase>prepare-package</phase>
-            <goals>
-              <goal>report</goal>
-            </goals>
-          </execution>
         </executions>
         <configuration>
           <propertyName>jacoco.agent</propertyName>