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/01/05 14:48:50 UTC

[maven-surefire] 01/01: junit5-displayname

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

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

commit ff871c85dab97cd382190b89a123664c178d1697
Author: tibordigana <ti...@apache.org>
AuthorDate: Sat Jan 5 15:48:30 2019 +0100

    junit5-displayname
---
 .../plugin/surefire/report/WrappedReportEntry.java |   9 +-
 .../surefire/runorder/StatisticsReporter.java      |   5 +-
 .../surefire/report/WrappedReportEntryTest.java    |  14 +-
 .../runorder/RunEntryStatisticsMapTest.java        | 128 ++++++++-
 pom.xml                                            |   4 +-
 .../surefire/runorder/RunEntryStatistics.java      |  47 +---
 .../surefire/runorder/RunEntryStatisticsMap.java   | 172 ++++++++-----
 .../apache/maven/surefire/report/ReportEntry.java  |   8 +
 .../maven/surefire/report/SimpleReportEntry.java   |   6 +
 .../maven/surefire/util/internal}/ClassMethod.java |  32 ++-
 .../util/internal/TestClassMethodNameUtils.java    |  32 ++-
 .../runorder/ThreadedExecutionSchedulerTest.java   |  26 +-
 surefire-its/pom.xml                               |   2 +-
 .../maven/surefire/its/JUnitPlatformEnginesIT.java |   8 +-
 .../surefire/report/PojoStackTraceWriter.java      |  24 ++
 .../surefire/common/junit4/JUnit4ProviderUtil.java |  38 +--
 .../surefire/common/junit4/JUnit4RunListener.java  |  48 +---
 .../common/junit4/JUnit4StackTraceWriter.java      |  23 +-
 .../maven/surefire/common/junit4/Notifier.java     |   4 +-
 .../common/junit4/JUnit4ProviderUtilTest.java      |  15 +-
 .../common/junit48/JUnit46StackTraceWriter.java    |  60 -----
 surefire-providers/surefire-junit-platform/pom.xml |   4 +
 .../surefire/junitplatform/RunListenerAdapter.java | 286 +++++++++------------
 .../surefire/junitplatform/JUnit47SuiteTest.java}  |  45 ++--
 .../junitplatform/JUnitPlatformProviderTest.java   |   8 +-
 .../junitplatform/RunListenerAdapterTest.java      | 124 +++++----
 .../maven/surefire/junit/JUnitTestSetTest.java     |   8 +-
 .../maven/surefire/junit4/TestResolverFilter.java  |   2 +-
 .../surefire/junitcore/JUnitCoreRunListener.java   |  20 +-
 .../junitcore/NonConcurrentRunListener.java        |  21 +-
 .../junitcore/JUnitCoreRunListenerTest.java        |   2 -
 .../maven/surefire/testng/TestNGReporter.java      |  32 +--
 .../surefire/report/SurefireReportParser.java      |  29 +--
 .../surefire/report/SurefireReportParserTest.java  |  42 +--
 34 files changed, 677 insertions(+), 651 deletions(-)

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 3426e3a..ba11bbc 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
@@ -74,6 +74,12 @@ public class WrappedReportEntry
         return elapsed;
     }
 
+    @Override
+    public int getElapsed( int fallback )
+    {
+        return elapsed == null ? fallback : elapsed;
+    }
+
     public ReportEntryType getReportEntryType()
     {
         return reportEntryType;
@@ -137,8 +143,7 @@ public class WrappedReportEntry
 
     public String getReportName()
     {
-        final int i = getName().lastIndexOf( "(" );
-        return i > 0 ? getName().substring( 0, i ) : getName();
+        return getName();
     }
 
     public String getReportName( String suffix )
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java
index a53db02..2d9a175 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java
@@ -20,7 +20,8 @@ package org.apache.maven.plugin.surefire.runorder;
  */
 
 import java.io.File;
-import java.io.FileNotFoundException;
+import java.io.IOException;
+
 import org.apache.maven.surefire.report.ReportEntry;
 
 import static org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMap.fromFile;
@@ -54,7 +55,7 @@ public class StatisticsReporter
         {
             newResults.serialize( dataFile );
         }
-        catch ( FileNotFoundException e )
+        catch ( IOException e )
         {
             throw new RuntimeException( e );
         }
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 030fc2f..f499ec3 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
@@ -29,9 +29,7 @@ import junit.framework.TestCase;
 public class WrappedReportEntryTest
     extends TestCase
 {
-
     public void testClassNameOnly()
-        throws Exception
     {
         String category = "surefire.testcase.JunitParamsTest";
         WrappedReportEntry wr =
@@ -42,16 +40,15 @@ public class WrappedReportEntryTest
 
     public void testRegular()
     {
-        ReportEntry reportEntry = new SimpleReportEntry( "fud", "testSum(surefire.testcase.NonJunitParamsTest)" );
+        ReportEntry reportEntry = new SimpleReportEntry( "fud", "testSum" );
         WrappedReportEntry wr = new WrappedReportEntry( reportEntry, null, 12, null, null );
         final String reportName = wr.getReportName();
         assertEquals( "testSum", reportName );
     }
 
     public void testGetReportNameWithParams()
-        throws Exception
     {
-        String category = "[0] 1\u002C 2\u002C 3 (testSum)(surefire.testcase.JunitParamsTest)";
+        String category = "[0] 1\u002C 2\u002C 3 (testSum)";
         ReportEntry reportEntry = new SimpleReportEntry( "fud", category );
         WrappedReportEntry wr = new WrappedReportEntry( reportEntry, null, 12, null, null );
         final String reportName = wr.getReportName();
@@ -59,15 +56,12 @@ public class WrappedReportEntryTest
     }
 
     public void testElapsed()
-        throws Exception
     {
-        String category = "[0] 1\u002C 2\u002C 3 (testSum)(surefire.testcase.JunitParamsTest)";
+        String category = "[0] 1\u002C 2\u002C 3 (testSum)";
         ReportEntry reportEntry = new SimpleReportEntry( "fud", category );
         WrappedReportEntry wr = new WrappedReportEntry( reportEntry, null, 12, null, null );
         String elapsedTimeSummary = wr.getElapsedTimeSummary();
-        assertEquals( "[0] 1, 2, 3 (testSum)(surefire.testcase.JunitParamsTest)  Time elapsed: 0.012 s",
+        assertEquals( "[0] 1, 2, 3 (testSum)  Time elapsed: 0.012 s",
                       elapsedTimeSummary );
     }
-
-
 }
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 5a171ab..42698d7 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
@@ -20,15 +20,23 @@ package org.apache.maven.plugin.surefire.runorder;
  */
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.StringReader;
+import java.io.InputStream;
+import java.io.StringBufferInputStream;
 import java.util.Arrays;
 import java.util.List;
+
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.SimpleReportEntry;
 
 import junit.framework.TestCase;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.commons.io.IOUtils.readLines;
+import static org.apache.maven.surefire.util.internal.StringUtils.NL;
+import static org.fest.assertions.Assertions.assertThat;
+
 /**
  * @author Kristian Rosenvold
  */
@@ -36,9 +44,8 @@ public class RunEntryStatisticsMapTest
     extends TestCase
 {
     public void testPrioritizedClassRuntime()
-        throws IOException
     {
-        final RunEntryStatisticsMap runEntryStatisticsMap = RunEntryStatisticsMap.fromReader( getStatisticsFile() );
+        final RunEntryStatisticsMap runEntryStatisticsMap = RunEntryStatisticsMap.fromStream( getStatisticsFile() );
         final List<Class<?>> list = Arrays.<Class<?>>asList( A.class, B.class, C.class );
         final List<Class<?>> prioritizedTestsClassRunTime =
             runEntryStatisticsMap.getPrioritizedTestsClassRunTime( list, 2 );
@@ -48,9 +55,8 @@ public class RunEntryStatisticsMapTest
     }
 
     public void testPrioritizedFailureFirst()
-        throws IOException
     {
-        final RunEntryStatisticsMap runEntryStatisticsMap = RunEntryStatisticsMap.fromReader( getStatisticsFile() );
+        final RunEntryStatisticsMap runEntryStatisticsMap = RunEntryStatisticsMap.fromStream( getStatisticsFile() );
         final List<Class<?>> list = Arrays.<Class<?>>asList( A.class, B.class, NewClass.class, C.class );
         final List<Class<?>> prioritizedTestsClassRunTime =
             runEntryStatisticsMap.getPrioritizedTestsByFailureFirst( list );
@@ -60,12 +66,12 @@ public class RunEntryStatisticsMapTest
         assertEquals( B.class, prioritizedTestsClassRunTime.get( 3 ) );
     }
 
-    private StringReader getStatisticsFile()
+    private InputStream getStatisticsFile()
     {
-        String content = "0,17,testA(org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest$A)\n" +
-            "2,42,testB(org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest$B)\n" +
-            "1,100,testC(org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest$C)\n";
-        return new StringReader( content );
+        String content = "0,17,org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest$A,testA\n" +
+            "2,42,org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest$B,testB\n" +
+            "1,100,org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest$C,testC\n";
+        return new StringBufferInputStream( content );
     }
 
     public void testSerialize()
@@ -84,15 +90,111 @@ public class RunEntryStatisticsMapTest
         newResults.add( existingEntries.createNextGeneration( reportEntry3 ) );
 
         newResults.serialize( data );
+        try ( InputStream io = new FileInputStream( data) )
+        {
+            List<String> lines = readLines( io, UTF_8 );
+
+            assertThat( lines )
+                    .hasSize( 3 );
+
+            assertThat( lines )
+                    .containsSequence( "1,17,abc,willFail", "1,42,abc,method1", "1,100,abc,method3" );
+        }
 
         RunEntryStatisticsMap nextRun = RunEntryStatisticsMap.fromFile( data );
         newResults = new RunEntryStatisticsMap();
 
-        newResults.add( existingEntries.createNextGeneration( reportEntry1 ) );
-        newResults.add( existingEntries.createNextGenerationFailure( reportEntry2 ) );
-        newResults.add( existingEntries.createNextGeneration( reportEntry3 ) );
+        ReportEntry newRunReportEntry1 = new SimpleReportEntry( "abc", "method1", 52 );
+        ReportEntry newRunReportEntry2 = new SimpleReportEntry( "abc", "willFail", 27 );
+        ReportEntry newRunReportEntry3 = new SimpleReportEntry( "abc", "method3", 110 );
+
+        newResults.add( nextRun.createNextGeneration( newRunReportEntry1 ) );
+        newResults.add( nextRun.createNextGenerationFailure( newRunReportEntry2 ) );
+        newResults.add( nextRun.createNextGeneration( newRunReportEntry3 ) );
 
         newResults.serialize( data );
+        try ( InputStream io = new FileInputStream( data ) )
+        {
+            List<String> lines = readLines( io, UTF_8 );
+
+            assertThat( lines )
+                    .hasSize( 3 );
+
+            assertThat( lines )
+                    .containsSequence( "0,27,abc,willFail", "2,52,abc,method1", "2,110,abc,method3" );
+        }
+    }
+
+    public void testMultiLineTestMethodName() throws IOException
+    {
+        File data = File.createTempFile( "surefire-unit", "test" );
+        RunEntryStatisticsMap reportEntries = RunEntryStatisticsMap.fromFile( data );
+        ReportEntry reportEntry = new SimpleReportEntry( "abc", "line1\nline2" + NL + " line3", 42 );
+        reportEntries.add( reportEntries.createNextGeneration( reportEntry ) );
+
+        reportEntries.serialize( data );
+        try ( InputStream io = new FileInputStream( data ) )
+        {
+            List<String> lines = readLines( io, UTF_8 );
+
+            assertThat( lines )
+                    .hasSize( 3 );
+
+            assertThat( lines )
+                    .containsSequence( "1,42,abc,line1", " line2", "  line3" );
+        }
+
+        RunEntryStatisticsMap nextRun = RunEntryStatisticsMap.fromFile( data );
+        assertThat( data.delete() ).isTrue();
+        nextRun.serialize( data );
+        try ( InputStream io = new FileInputStream( data ) )
+        {
+            List<String> lines = readLines( io, UTF_8 );
+
+            assertThat( lines )
+                    .hasSize( 3 );
+
+            assertThat( lines )
+                    .containsSequence( "1,42,abc,line1", " line2", "  line3" );
+        }
+    }
+
+    public void testCombinedMethodNames() throws IOException
+    {
+        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.serialize( data );
+        try ( InputStream io = new FileInputStream( data ) )
+        {
+            List<String> lines = readLines( io, UTF_8 );
+
+            assertThat( lines )
+                    .hasSize( 3 );
+
+            assertThat( lines )
+                    .containsSequence( "1,10,abc,test",
+                                       "1,42,abc,line1",
+                                       " line2" );
+        }
+
+        RunEntryStatisticsMap nextRun = RunEntryStatisticsMap.fromFile( data );
+        assertThat( data.delete() ).isTrue();
+        nextRun.serialize( data );
+        try ( InputStream io = new FileInputStream( data ) )
+        {
+            List<String> lines = readLines( io, UTF_8 );
+
+            assertThat( lines )
+                    .hasSize( 3 );
+
+            assertThat( lines )
+                    .containsSequence( "1,10,abc,test",
+                                       "1,42,abc,line1",
+                                       " line2" );
+        }
     }
 
     class A
diff --git a/pom.xml b/pom.xml
index 72b9b5a..a048c46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -277,12 +277,12 @@
       <dependency>
         <groupId>org.junit.platform</groupId>
         <artifactId>junit-platform-launcher</artifactId>
-        <version>1.3.1</version>
+        <version>1.3.2</version>
       </dependency>
       <dependency>
         <groupId>org.junit.jupiter</groupId>
         <artifactId>junit-jupiter-engine</artifactId>
-        <version>5.3.1</version>
+        <version>5.3.2</version>
       </dependency>
       <dependency>
         <groupId>org.mockito</groupId>
diff --git a/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatistics.java b/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatistics.java
index 7c794dc..1ccc4ee 100644
--- a/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatistics.java
+++ b/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatistics.java
@@ -19,8 +19,7 @@ package org.apache.maven.plugin.surefire.runorder;
  * under the License.
  */
 
-import java.util.StringTokenizer;
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.util.internal.ClassMethod;
 
 /**
  * @author Kristian Rosenvold
@@ -31,39 +30,33 @@ public class RunEntryStatistics
 
     private final int successfulBuilds;
 
-    private final String testName;
+    private final ClassMethod classMethod;
 
-    private RunEntryStatistics( int runTime, int successfulBuilds, String testName )
+    RunEntryStatistics( int runTime, int successfulBuilds, String clazz, String method )
     {
-        this.runTime = runTime;
-        this.successfulBuilds = successfulBuilds;
-        this.testName = testName;
+        this( runTime, successfulBuilds, new ClassMethod( clazz, method ) );
     }
 
-    public static RunEntryStatistics fromReportEntry( ReportEntry previous )
+    RunEntryStatistics( int runTime, int successfulBuilds, ClassMethod classMethod )
     {
-        final Integer elapsed = previous.getElapsed();
-        return new RunEntryStatistics( elapsed != null ? elapsed : 0, 0, previous.getName() );
+        this.runTime = runTime;
+        this.successfulBuilds = successfulBuilds;
+        this.classMethod = classMethod;
     }
 
-    public static RunEntryStatistics fromValues( int runTime, int successfulBuilds, Class clazz, String testName )
+    public ClassMethod getClassMethod()
     {
-        return new RunEntryStatistics( runTime, successfulBuilds, testName + "(" + clazz.getName() + ")" );
+        return classMethod;
     }
 
     public RunEntryStatistics nextGeneration( int runTime )
     {
-        return new RunEntryStatistics( runTime, this.successfulBuilds + 1, this.testName );
+        return new RunEntryStatistics( runTime, successfulBuilds + 1, classMethod );
     }
 
     public RunEntryStatistics nextGenerationFailure( int runTime )
     {
-        return new RunEntryStatistics( runTime, 0, this.testName );
-    }
-
-    public String getTestName()
-    {
-        return testName;
+        return new RunEntryStatistics( runTime, 0, classMethod );
     }
 
     public int getRunTime()
@@ -75,20 +68,4 @@ public class RunEntryStatistics
     {
         return successfulBuilds;
     }
-
-    public static RunEntryStatistics fromString( String line )
-    {
-        StringTokenizer tok = new StringTokenizer( line, "," );
-        int successfulBuilds = Integer.parseInt( tok.nextToken() );
-        int runTime = Integer.parseInt( tok.nextToken() );
-        String className = tok.nextToken();
-        return new RunEntryStatistics( runTime, successfulBuilds, className );
-    }
-
-    @Override
-    public String toString()
-    {
-        return successfulBuilds + "," + runTime + "," + testName;
-    }
-
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java b/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java
index 1a685dc..09a4445 100644
--- a/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java
+++ b/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java
@@ -19,38 +19,41 @@ package org.apache.maven.plugin.surefire.runorder;
  * under the License.
  */
 
-
 import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.util.internal.ClassMethod;
 
-import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileNotFoundException;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.Reader;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Scanner;
+import java.util.StringTokenizer;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
+import static java.lang.Integer.parseInt;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Collections.sort;
-import static org.apache.maven.plugin.surefire.runorder.RunEntryStatistics.fromReportEntry;
-import static org.apache.maven.plugin.surefire.runorder.RunEntryStatistics.fromString;
+import static org.apache.maven.surefire.util.internal.StringUtils.NL;
 
 /**
  * @author Kristian Rosenvold
  */
 public final class RunEntryStatisticsMap
 {
-    private final Map<String, RunEntryStatistics> runEntryStatistics;
+    private final Map<ClassMethod, RunEntryStatistics> runEntryStatistics;
 
-    public RunEntryStatisticsMap( Map<String, RunEntryStatistics> runEntryStatistics )
+    private RunEntryStatisticsMap( Map<ClassMethod, RunEntryStatistics> runEntryStatistics )
     {
         this.runEntryStatistics = new ConcurrentHashMap<>( runEntryStatistics );
     }
@@ -66,7 +69,7 @@ public final class RunEntryStatisticsMap
         {
             try
             {
-                return fromReader( new FileReader( file ) );
+                return fromStream( new FileInputStream( file ) );
             }
             catch ( IOException e )
             {
@@ -79,62 +82,124 @@ public final class RunEntryStatisticsMap
         }
     }
 
-    static RunEntryStatisticsMap fromReader( Reader fileReader )
-        throws IOException
+    static RunEntryStatisticsMap fromStream( InputStream fileReader )
     {
-        Map<String, RunEntryStatistics> result = new HashMap<>();
-        BufferedReader bufferedReader = new BufferedReader( fileReader );
-        String line = bufferedReader.readLine();
-        while ( line != null )
+        Map<ClassMethod, RunEntryStatistics> result = new HashMap<>();
+        try ( Scanner scanner = new Scanner( fileReader, "UTF-8" ) )
         {
-            if ( !line.startsWith( "#" ) )
+            RunEntryStatistics previous = null;
+            while ( scanner.hasNextLine() )
             {
-                final RunEntryStatistics stats = fromString( line );
-                result.put( stats.getTestName(), stats );
+                String line = scanner.nextLine();
+
+                if ( line.charAt( 0 ) == ' ' )
+                {
+                    previous = new RunEntryStatistics( previous.getRunTime(),
+                            previous.getSuccessfulBuilds(),
+                            previous.getClassMethod().getClazz(),
+                            previous.getClassMethod().getMethod() + NL + line.substring( 1 ) );
+                }
+                else
+                {
+                    if ( previous != null )
+                    {
+                        result.put( previous.getClassMethod(), previous );
+                    }
+                    StringTokenizer tokenizer = new StringTokenizer( line, "," );
+
+                    int methodIndex = 3;
+
+                    String successfulBuildsString = tokenizer.nextToken();
+                    int successfulBuilds = parseInt( successfulBuildsString );
+
+                    methodIndex += successfulBuildsString.length();
+
+                    String runTimeString = tokenizer.nextToken();
+                    int runTime = parseInt( runTimeString );
+
+                    methodIndex += runTimeString.length();
+
+                    String className = tokenizer.nextToken();
+
+                    methodIndex += className.length();
+
+                    String methodName = line.substring( methodIndex );
+
+                    ClassMethod classMethod = new ClassMethod( className, methodName );
+                    previous = new RunEntryStatistics( runTime, successfulBuilds, classMethod );
+                }
+            }
+            if ( previous != null )
+            {
+                result.put( previous.getClassMethod(), previous );
             }
-            line = bufferedReader.readLine();
         }
         return new RunEntryStatisticsMap( result );
     }
 
-    public void serialize( File file )
-        throws FileNotFoundException
+    public void serialize( File statsFile )
+        throws IOException
     {
-        FileOutputStream fos = new FileOutputStream( file );
-        try ( PrintWriter printWriter = new PrintWriter( fos ) )
+        if ( statsFile.isFile() )
+        {
+            //noinspection ResultOfMethodCallIgnored
+            statsFile.delete();
+        }
+        OutputStream os = new FileOutputStream( statsFile );
+        try ( BufferedWriter writer = new BufferedWriter( new OutputStreamWriter( os, UTF_8 ), 64 * 1024 ) )
         {
             List<RunEntryStatistics> items = new ArrayList<>( runEntryStatistics.values() );
             sort( items, new RunCountComparator() );
-            for ( RunEntryStatistics item : items )
+            for ( Iterator<RunEntryStatistics> it = items.iterator(); it.hasNext(); )
             {
-                printWriter.println( item.toString() );
+                RunEntryStatistics item = it.next();
+                ClassMethod test = item.getClassMethod();
+                String line = item.getSuccessfulBuilds() + "," + item.getRunTime() + "," + test.getClazz() + ",";
+                writer.write( line );
+                boolean wasFirstLine = false;
+                for ( Scanner scanner = new Scanner( test.getMethod() ); scanner.hasNextLine(); wasFirstLine = true )
+                {
+                    String methodLine = scanner.nextLine();
+                    if ( wasFirstLine )
+                    {
+                        writer.write( ' ' );
+                    }
+                    writer.write( methodLine );
+                    if ( scanner.hasNextLine() )
+                    {
+                        writer.newLine();
+                    }
+                }
+                if ( it.hasNext() )
+                {
+                    writer.newLine();
+                }
             }
         }
     }
 
-    public RunEntryStatistics findOrCreate( ReportEntry reportEntry )
+    private RunEntryStatistics findOrCreate( ReportEntry reportEntry )
     {
-        final RunEntryStatistics item = runEntryStatistics.get( reportEntry.getName() );
-        return item != null ? item : fromReportEntry( reportEntry );
+        ClassMethod classMethod = new ClassMethod( reportEntry.getSourceName(), reportEntry.getName() );
+        RunEntryStatistics item = runEntryStatistics.get( classMethod );
+        return item != null ? item : new RunEntryStatistics( reportEntry.getElapsed( 0 ), 0, classMethod );
     }
 
     public RunEntryStatistics createNextGeneration( ReportEntry reportEntry )
     {
-        final RunEntryStatistics newItem = findOrCreate( reportEntry );
-        final Integer elapsed = reportEntry.getElapsed();
-        return newItem.nextGeneration( elapsed != null ? elapsed : 0 );
+        RunEntryStatistics newItem = findOrCreate( reportEntry );
+        return newItem.nextGeneration( reportEntry.getElapsed( 0 ) );
     }
 
     public RunEntryStatistics createNextGenerationFailure( ReportEntry reportEntry )
     {
-        final RunEntryStatistics newItem = findOrCreate( reportEntry );
-        final Integer elapsed = reportEntry.getElapsed();
-        return newItem.nextGenerationFailure( elapsed != null ? elapsed : 0 );
+        RunEntryStatistics newItem = findOrCreate( reportEntry );
+        return newItem.nextGenerationFailure( reportEntry.getElapsed( 0 ) );
     }
 
     public void add( RunEntryStatistics item )
     {
-        runEntryStatistics.put( item.getTestName(), item );
+        runEntryStatistics.put( item.getClassMethod(), item );
     }
 
     static final class RunCountComparator
@@ -169,12 +234,12 @@ public final class RunEntryStatisticsMap
     private List<PrioritizedTest> getPrioritizedTests( List<Class<?>> testsToRun,
                                                        Comparator<Priority> priorityComparator )
     {
-        Map classPriorities = getPriorities( priorityComparator );
+        Map<String, Priority> classPriorities = getPriorities( priorityComparator );
 
         List<PrioritizedTest> tests = new ArrayList<>();
         for ( Class<?> clazz : testsToRun )
         {
-            Priority pri = (Priority) classPriorities.get( clazz.getName() );
+            Priority pri = classPriorities.get( clazz.getName() );
             if ( pri == null )
             {
                 pri = Priority.newTestClassPriority( clazz.getName() );
@@ -186,7 +251,7 @@ public final class RunEntryStatisticsMap
         return tests;
     }
 
-    private List<Class<?>> transformToClasses( List<PrioritizedTest> tests )
+    private static List<Class<?>> transformToClasses( List<PrioritizedTest> tests )
     {
         List<Class<?>> result = new ArrayList<>();
         for ( PrioritizedTest test : tests )
@@ -196,22 +261,19 @@ public final class RunEntryStatisticsMap
         return result;
     }
 
-    private Map getPriorities( Comparator<Priority> priorityComparator )
+    private Map<String, Priority> getPriorities( Comparator<Priority> priorityComparator )
     {
         Map<String, Priority> priorities = new HashMap<>();
-        for ( Object o : runEntryStatistics.keySet() )
+        for ( Entry<ClassMethod, RunEntryStatistics> testNames : runEntryStatistics.entrySet() )
         {
-            String testNames = (String) o;
-            String clazzName = extractClassName( testNames );
+            String clazzName = testNames.getKey().getClazz();
             Priority priority = priorities.get( clazzName );
             if ( priority == null )
             {
                 priority = new Priority( clazzName );
                 priorities.put( clazzName, priority );
             }
-
-            RunEntryStatistics itemStat = runEntryStatistics.get( testNames );
-            priority.addItem( itemStat );
+            priority.addItem( testNames.getValue() );
         }
 
         List<Priority> items = new ArrayList<>( priorities.values() );
@@ -255,16 +317,4 @@ public final class RunEntryStatisticsMap
             return o.getMinSuccessRate() - o1.getMinSuccessRate();
         }
     }
-
-
-    private static final Pattern PARENS = Pattern.compile( "^" + "[^\\(\\)]+" //non-parens
-                                                               + "\\((" // then an open-paren (start matching a group)
-                                                               + "[^\\\\(\\\\)]+" //non-parens
-                                                               + ")\\)" + "$" ); // then a close-paren (end group match)
-
-    String extractClassName( String displayName )
-    {
-        Matcher m = PARENS.matcher( displayName );
-        return m.find() ? m.group( 1 ) : displayName;
-    }
 }
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 ec0f782..e0f3468 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
@@ -61,6 +61,14 @@ public interface ReportEntry
      */
     Integer getElapsed();
 
+    /**
+     * Returns same value as {@link #getElapsed()} and fallbacks to {@code fallback} for <tt>null</tt> elapsed timed.
+     *
+     * @param fallback usually 0
+     * @return elapsed time if {@link #getElapsed()} is not null; otherwise returns {@code fallback}
+     */
+    int getElapsed( int fallback );
+
 
     /**
      * A message relating to a non-successful termination.
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 241c874..4172513 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
@@ -167,6 +167,12 @@ public class SimpleReportEntry
     }
 
     @Override
+    public int getElapsed( int fallback )
+    {
+        return elapsed == null ? fallback : elapsed;
+    }
+
+    @Override
     public String toString()
     {
         return "ReportEntry{" + "source='" + source + '\'' + ", name='" + name + '\'' + ", stackTraceWriter="
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/ClassMethod.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ClassMethod.java
similarity index 63%
copy from surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/ClassMethod.java
copy to surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ClassMethod.java
index a3cccca..03f9620 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/ClassMethod.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ClassMethod.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.common.junit4;
+package org.apache.maven.surefire.util.internal;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,7 +19,9 @@ package org.apache.maven.surefire.common.junit4;
  * under the License.
  */
 
-import org.apache.maven.surefire.util.internal.StringUtils;
+import java.util.Objects;
+
+import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
 
 /**
  * Data transfer object of class and method literals.
@@ -36,9 +38,9 @@ public final class ClassMethod
         this.method = method;
     }
 
-    public boolean isValid()
+    public boolean isValidTest()
     {
-        return !StringUtils.isBlank( clazz ) && !StringUtils.isBlank( method );
+        return !isBlank( clazz ) && !isBlank( method );
     }
 
     public String getClazz()
@@ -50,4 +52,26 @@ public final class ClassMethod
     {
         return method;
     }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+        ClassMethod that = ( ClassMethod ) o;
+        return Objects.equals( clazz, that.clazz )
+                && Objects.equals( method, that.method );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return Objects.hash( clazz, method );
+    }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/TestClassMethodNameUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/TestClassMethodNameUtils.java
index 23e72e1..94bf4c8 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/TestClassMethodNameUtils.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/TestClassMethodNameUtils.java
@@ -19,24 +19,15 @@ package org.apache.maven.surefire.util.internal;
  * under the License.
  */
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 /**
  * JUnit Description parser.
- * Used by JUnit Version lower than 4.7.
+ * Used by JUnit4+.
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.20
  */
 public final class TestClassMethodNameUtils
 {
-    /**
-     * This pattern is verbatim copy from JUnit's code in class {@code Description}.
-     * Parsing class and method from junit description would provide identical result to JUnit internal parser.
-     */
-    private static final Pattern METHOD_CLASS_PATTERN = Pattern.compile( "([\\s\\S]*)\\((.*)\\)" );
-
     private TestClassMethodNameUtils()
     {
         throw new IllegalStateException( "no instantiable constructor" );
@@ -44,13 +35,26 @@ public final class TestClassMethodNameUtils
 
     public static String extractClassName( String displayName )
     {
-        Matcher m = METHOD_CLASS_PATTERN.matcher( displayName );
-        return m.matches() ? m.group( 2 ) : displayName;
+        String clazz = displayName;
+        if ( displayName.endsWith( ")" ) )
+        {
+            int paren = displayName.lastIndexOf( '(' );
+            if ( paren != -1 )
+            {
+                clazz = displayName.substring( paren + 1, displayName.length() - 1 );
+            }
+        }
+        return clazz;
     }
 
     public static String extractMethodName( String displayName )
     {
-        Matcher m = METHOD_CLASS_PATTERN.matcher( displayName );
-        return m.matches() ? m.group( 1 ) : displayName;
+        String method = null;
+        int parent = displayName.lastIndexOf( '(' );
+        if ( parent != -1 )
+        {
+            method = displayName.substring( 0, parent );
+        }
+        return method;
     }
 }
diff --git a/surefire-api/src/test/java/org/apache/maven/plugin/surefire/runorder/ThreadedExecutionSchedulerTest.java b/surefire-api/src/test/java/org/apache/maven/plugin/surefire/runorder/ThreadedExecutionSchedulerTest.java
index 15963d7..6647504 100644
--- a/surefire-api/src/test/java/org/apache/maven/plugin/surefire/runorder/ThreadedExecutionSchedulerTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/plugin/surefire/runorder/ThreadedExecutionSchedulerTest.java
@@ -21,6 +21,7 @@ package org.apache.maven.plugin.surefire.runorder;
 import java.util.List;
 
 import junit.framework.TestCase;
+import org.apache.maven.surefire.util.internal.ClassMethod;
 
 /**
  * @author Kristian Rosenvold
@@ -29,24 +30,23 @@ public class ThreadedExecutionSchedulerTest
     extends TestCase
 {
 
-    private final RunEntryStatistics a1 = RunEntryStatistics.fromValues( 200, 2, A.class, "at1" );
+    private final RunEntryStatistics a1 = fromValues( 200, 2, A.class, "at1" );
 
-    private final RunEntryStatistics a2 = RunEntryStatistics.fromValues( 300, 2, A.class, "at2" );
+    private final RunEntryStatistics a2 = fromValues( 300, 2, A.class, "at2" );
 
-    private final RunEntryStatistics b1 = RunEntryStatistics.fromValues( 400, 2, B.class, "bt1" );
+    private final RunEntryStatistics b1 = fromValues( 400, 2, B.class, "bt1" );
 
-    private final RunEntryStatistics b2 = RunEntryStatistics.fromValues( 300, 2, B.class, "bt2" );
+    private final RunEntryStatistics b2 = fromValues( 300, 2, B.class, "bt2" );
 
-    private final RunEntryStatistics c1 = RunEntryStatistics.fromValues( 400, 2, C.class, "ct1" );
+    private final RunEntryStatistics c1 = fromValues( 400, 2, C.class, "ct1" );
 
-    private final RunEntryStatistics c2 = RunEntryStatistics.fromValues( 200, 2, C.class, "ct2" );
+    private final RunEntryStatistics c2 = fromValues( 200, 2, C.class, "ct2" );
 
-    private final RunEntryStatistics d1 = RunEntryStatistics.fromValues( 401, 2, D.class, "ct2" );
+    private final RunEntryStatistics d1 = fromValues( 401, 2, D.class, "ct2" );
 
-    private final RunEntryStatistics e1 = RunEntryStatistics.fromValues( 200, 2, E.class, "ct2" );
+    private final RunEntryStatistics e1 = fromValues( 200, 2, E.class, "ct2" );
 
     public void testAddTest()
-        throws Exception
     {
         ThreadedExecutionScheduler threadedExecutionScheduler = new ThreadedExecutionScheduler( 2 );
         addPrioritizedTests( threadedExecutionScheduler );
@@ -57,17 +57,14 @@ public class ThreadedExecutionSchedulerTest
         assertEquals( D.class, result.get( 2 ) );
         assertEquals( A.class, result.get( 3 ) );
         assertEquals( E.class, result.get( 4 ) );
-
     }
 
     public void testAddTestJaggedResult()
-        throws Exception
     {
         ThreadedExecutionScheduler threadedExecutionScheduler = new ThreadedExecutionScheduler( 4 );
         addPrioritizedTests( threadedExecutionScheduler );
         final List result = threadedExecutionScheduler.getResult();
         assertEquals( 5, result.size() );
-
     }
 
     private void addPrioritizedTests( ThreadedExecutionScheduler threadedExecutionScheduler )
@@ -94,6 +91,11 @@ public class ThreadedExecutionSchedulerTest
         return priority;
     }
 
+    private static RunEntryStatistics fromValues( int runTime, int successfulBuilds, Class clazz, String testName )
+    {
+        ClassMethod classMethod = new ClassMethod( clazz.getName(), testName );
+        return new RunEntryStatistics( runTime, successfulBuilds, classMethod );
+    }
 
     class A
     {
diff --git a/surefire-its/pom.xml b/surefire-its/pom.xml
index 0cea53e..d08d4bf 100644
--- a/surefire-its/pom.xml
+++ b/surefire-its/pom.xml
@@ -169,7 +169,7 @@
                     <forkMode>once</forkMode>
                     <argLine>-server -Xmx64m -XX:+UseG1GC -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Djava.awt.headless=true -Djdk.net.URLClassPath.disableClassPathURLCheck=true</argLine>
                     <includes>
-                        <include>org/apache/**/*IT*.java</include>
+                        <include>org/apache/**/*Platform*IT*.java</include>
                     </includes>
                     <!-- Pass current surefire version to the main suite so that it -->
                     <!-- can forward to all integration test projects. SUREFIRE-513 -->
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 332cbb9..ae04143 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
@@ -59,10 +59,10 @@ public class JUnitPlatformEnginesIT
     public static Iterable<Object[]> regexVersions()
     {
         ArrayList<Object[]> args = new ArrayList<Object[]>();
-        args.add( new Object[] { "1.0.0", "5.0.0", "1.0.0", "1.0.0" } );
+        args.add( new Object[] { "1.0.3", "5.0.3", "1.0.0", "1.0.0" } );
         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.1", "5.3.1", "1.1.1", "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" } );
         return args;
     }
@@ -114,7 +114,7 @@ public class JUnitPlatformEnginesIT
                 + "  surefire-api-*.jar"
                 + "  surefire-logger-api-*.jar"
                 + "  common-java5-*.jar"
-                + "  junit-platform-launcher-1.3.1.jar";
+                + "  junit-platform-launcher-1.3.2.jar";
 
         lines = validator.loadLogLines( startsWith( "[DEBUG] provider(compact) classpath" ) );
 
@@ -138,7 +138,7 @@ public class JUnitPlatformEnginesIT
                 + "  opentest4j-" + opentest + ".jar"
                 + "  junit-jupiter-api-" + jupiter + ".jar"
                 + "  surefire-junit-platform-*.jar"
-                + "  junit-platform-launcher-1.3.1.jar";
+                + "  junit-platform-launcher-1.3.2.jar";
 
         lines = validator.loadLogLines( startsWith( "[DEBUG] boot(compact) classpath" ) );
 
diff --git a/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/PojoStackTraceWriter.java b/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/PojoStackTraceWriter.java
index 626392b..2f369e8 100644
--- a/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/PojoStackTraceWriter.java
+++ b/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/PojoStackTraceWriter.java
@@ -24,6 +24,7 @@ import org.apache.maven.surefire.util.internal.StringUtils;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.Objects;
 
 /**
  * Write the trace out for a POJO test.
@@ -109,4 +110,27 @@ public class PojoStackTraceWriter
         }
         return false;
     }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+        PojoStackTraceWriter that = ( PojoStackTraceWriter ) o;
+        return Objects.equals( t, that.t )
+                && Objects.equals( testClass, that.testClass )
+                && Objects.equals( testMethod, that.testMethod );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return Objects.hash( t, testClass, testMethod );
+    }
 }
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtil.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtil.java
index d19a0e1..702ee7f 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtil.java
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtil.java
@@ -20,14 +20,17 @@ package org.apache.maven.surefire.common.junit4;
  */
 
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.maven.surefire.util.internal.ClassMethod;
 import org.junit.runner.Description;
 import org.junit.runner.manipulation.Filter;
 import org.junit.runner.notification.Failure;
 
-import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
+import static org.apache.maven.surefire.util.internal.TestClassMethodNameUtils.extractClassName;
+import static org.apache.maven.surefire.util.internal.TestClassMethodNameUtils.extractMethodName;
 import static org.junit.runner.Description.TEST_MECHANISM;
 
 /**
@@ -75,33 +78,30 @@ public final class JUnit4ProviderUtil
      * @param description method(class) or method[#](class) or method[#whatever-literals](class)
      * @return method JUnit test method
      */
-    public static ClassMethod cutTestClassAndMethod( Description description )
+    public static ClassMethod toClassMethod( Description description )
     {
-        String name = description.getDisplayName();
-        String clazz = null;
-        String method = null;
-        if ( name != null )
+        String clazz = extractClassName( description.getDisplayName() );
+        if ( clazz == null || isInsaneJunitNullString( clazz ) )
         {
-            // The order is : 1.method and then 2.class
-            // method(class)
-            name = name.trim();
-            if ( name.endsWith( ")" ) )
+            // This can happen upon early failures (class instantiation error etc)
+            Iterator<Description> it = description.getChildren().iterator();
+            if ( it.hasNext() )
             {
-                int classBracket = name.lastIndexOf( '(' );
-                if ( classBracket != -1 )
-                {
-                    clazz = tryBlank( name.substring( classBracket + 1, name.length() - 1 ) );
-                    method = tryBlank( name.substring( 0, classBracket ) );
-                }
+                description = it.next();
+                clazz = extractClassName( description.getDisplayName() );
+            }
+            if ( clazz == null )
+            {
+                clazz = "Test Instantiation Error";
             }
         }
+        String method = extractMethodName( description.getDisplayName() );
         return new ClassMethod( clazz, method );
     }
 
-    private static String tryBlank( String s )
+    private static boolean isInsaneJunitNullString( String value )
     {
-        s = s.trim();
-        return isBlank( s ) ? null : s;
+        return "null".equals( value );
     }
 
     public static Filter createMatchAnyDescriptionFilter( Iterable<Description> descriptions )
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 7ed2ad9..dc98fa9 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
@@ -24,17 +24,17 @@ import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.SimpleReportEntry;
 import org.apache.maven.surefire.report.StackTraceWriter;
 import org.apache.maven.surefire.testset.TestSetFailedException;
+import org.apache.maven.surefire.util.internal.ClassMethod;
 import org.junit.runner.Description;
 import org.junit.runner.Result;
 import org.junit.runner.notification.Failure;
 
 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.isFailureInsideJUnitItself;
+import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.toClassMethod;
 import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.getAnnotatedIgnoreValue;
 import static org.apache.maven.surefire.report.SimpleReportEntry.assumption;
 import static org.apache.maven.surefire.report.SimpleReportEntry.ignored;
 import static org.apache.maven.surefire.report.SimpleReportEntry.withException;
-import static org.apache.maven.surefire.util.internal.TestClassMethodNameUtils.extractClassName;
-import static org.apache.maven.surefire.util.internal.TestClassMethodNameUtils.extractMethodName;
 
 /**
  * RunListener for JUnit4, delegates to our own RunListener
@@ -74,7 +74,8 @@ public class JUnit4RunListener
         throws Exception
     {
         String reason = getAnnotatedIgnoreValue( description );
-        reporter.testSkipped( ignored( getClassName( description ), description.getDisplayName(), reason ) );
+        ClassMethod classMethod = toClassMethod( description );
+        reporter.testSkipped( ignored( classMethod.getClazz(), classMethod.getMethod(), reason ) );
     }
 
     /**
@@ -114,10 +115,10 @@ public class JUnit4RunListener
                 testHeader = "Failure when constructing test";
             }
 
-            String testClassName = getClassName( failure.getDescription() );
             StackTraceWriter stackTrace = createStackTraceWriter( failure );
 
-            ReportEntry report = withException( testClassName, testHeader, stackTrace );
+            ClassMethod classMethod = toClassMethod( failure.getDescription() );
+            ReportEntry report = withException( classMethod.getClazz(), testHeader, stackTrace );
 
             if ( failure.getException() instanceof AssertionError )
             {
@@ -140,8 +141,9 @@ public class JUnit4RunListener
         try
         {
             Description desc = failure.getDescription();
-            String test = getClassName( desc );
-            reporter.testAssumptionFailure( assumption( test, desc.getDisplayName(), failure.getMessage() ) );
+            ClassMethod classMethod = toClassMethod( desc );
+            ReportEntry report = assumption( classMethod.getClazz(), classMethod.getMethod(), failure.getMessage() );
+            reporter.testAssumptionFailure( report );
         }
         finally
         {
@@ -173,25 +175,6 @@ public class JUnit4RunListener
         reporter.testExecutionSkippedByUser();
     }
 
-    private String getClassName( Description description )
-    {
-        String name = extractDescriptionClassName( description );
-        if ( name == null || isInsaneJunitNullString( name ) )
-        {
-            // This can happen upon early failures (class instantiation error etc)
-            Description subDescription = description.getChildren().get( 0 );
-            if ( subDescription != null )
-            {
-                name = extractDescriptionClassName( subDescription );
-            }
-            if ( name == null )
-            {
-                name = "Test Instantiation Error";
-            }
-        }
-        return name;
-    }
-
     protected StackTraceWriter createStackTraceWriter( Failure failure )
     {
         return new JUnit4StackTraceWriter( failure );
@@ -199,17 +182,8 @@ public class JUnit4RunListener
 
     protected SimpleReportEntry createReportEntry( Description description )
     {
-        return new SimpleReportEntry( getClassName( description ), description.getDisplayName() );
-    }
-
-    protected String extractDescriptionClassName( Description description )
-    {
-        return extractClassName( description.getDisplayName() );
-    }
-
-    protected String extractDescriptionMethodName( Description description )
-    {
-        return extractMethodName( description.getDisplayName() );
+        ClassMethod classMethod = toClassMethod( description );
+        return new SimpleReportEntry( classMethod.getClazz(), classMethod.getMethod() );
     }
 
     public static void rethrowAnyTestMechanismFailures( Result run )
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4StackTraceWriter.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4StackTraceWriter.java
index 78cefb6..f60a30d 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4StackTraceWriter.java
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4StackTraceWriter.java
@@ -22,10 +22,10 @@ package org.apache.maven.surefire.common.junit4;
 import org.apache.maven.surefire.report.SafeThrowable;
 import org.apache.maven.surefire.report.SmartStackTraceParser;
 import org.apache.maven.surefire.report.StackTraceWriter;
+import org.apache.maven.surefire.util.internal.ClassMethod;
 import org.junit.runner.notification.Failure;
 
-import static org.apache.maven.surefire.util.internal.TestClassMethodNameUtils.extractClassName;
-import static org.apache.maven.surefire.util.internal.TestClassMethodNameUtils.extractMethodName;
+import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.toClassMethod;
 import static org.apache.maven.surefire.report.SmartStackTraceParser.stackTraceWithFocusOnClassAsString;
 
 /**
@@ -37,8 +37,7 @@ import static org.apache.maven.surefire.report.SmartStackTraceParser.stackTraceW
 public class JUnit4StackTraceWriter
     implements StackTraceWriter
 {
-    // Member Variables
-    protected final Failure junitFailure;
+    private final Failure junitFailure;
 
     /**
      * Constructor.
@@ -78,24 +77,14 @@ public class JUnit4StackTraceWriter
         return "";
     }
 
-    protected String getTestClassName()
-    {
-        return extractClassName( junitFailure.getDescription().getDisplayName() );
-    }
-
-    protected String getTestMethodName()
-    {
-        return extractMethodName( junitFailure.getDescription().getDisplayName() );
-    }
-
     @Override
-    @SuppressWarnings( "ThrowableResultOfMethodCallIgnored" )
     public String smartTrimmedStackTrace()
     {
         Throwable exception = junitFailure.getException();
+        ClassMethod classMethod = toClassMethod( junitFailure.getDescription() );
         return exception == null
             ? junitFailure.getMessage()
-            : new SmartStackTraceParser( getTestClassName(), exception, getTestMethodName() ).getString();
+            : new SmartStackTraceParser( classMethod.getClazz(), exception, classMethod.getMethod() ).getString();
     }
 
     /**
@@ -106,7 +95,7 @@ public class JUnit4StackTraceWriter
     @Override
     public String writeTrimmedTraceToString()
     {
-        String testClass = getTestClassName();
+        String testClass = toClassMethod( junitFailure.getDescription() ).getClazz();
         try
         {
             Throwable e = junitFailure.getException();
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java
index c2c6df8..de0ddc4 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java
@@ -32,7 +32,7 @@ import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.cutTestClassAndMethod;
+import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.toClassMethod;
 import static org.apache.maven.surefire.util.internal.ConcurrencyUtils.countDownToZero;
 
 /**
@@ -100,7 +100,7 @@ public class Notifier
         super.fireTestStarted( description );
         if ( !testClassNames.isEmpty() )
         {
-            testClassNames.remove( cutTestClassAndMethod( description ).getClazz() );
+            testClassNames.remove( toClassMethod( description ).getClazz() );
         }
     }
 
diff --git a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtilTest.java b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtilTest.java
index 163308c..15adc32 100644
--- a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtilTest.java
+++ b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtilTest.java
@@ -20,6 +20,7 @@ package org.apache.maven.surefire.common.junit4;
  */
 
 import junit.framework.TestCase;
+import org.apache.maven.surefire.util.internal.ClassMethod;
 import org.junit.runner.Description;
 import org.junit.runner.notification.Failure;
 
@@ -64,25 +65,25 @@ public class JUnit4ProviderUtilTest
     public void testIllegalTestDescription$NegativeTest()
     {
         Description test = Description.createSuiteDescription( "someTestMethod" );
-        ClassMethod classMethod = cutTestClassAndMethod( test );
-        assertFalse( classMethod.isValid() );
+        ClassMethod classMethod = JUnit4ProviderUtil.toClassMethod( test );
+        assertFalse( classMethod.isValidTest() );
     }
 
     public void testOldJUnitParameterizedDescriptionParser()
     {
         Description test = Description.createTestDescription( T1.class, " \n testMethod[5] " );
         assertEquals( " \n testMethod[5] (" + T1.class.getName() + ")", test.getDisplayName() );
-        ClassMethod classMethod = cutTestClassAndMethod( test );
-        assertTrue( classMethod.isValid() );
-        assertEquals( "testMethod[5]", classMethod.getMethod() );
+        ClassMethod classMethod = JUnit4ProviderUtil.toClassMethod( test );
+        assertTrue( classMethod.isValidTest() );
+        assertEquals( " \n testMethod[5] ", classMethod.getMethod() );
         assertEquals( T1.class.getName(), classMethod.getClazz() );
     }
 
     public void testNewJUnitParameterizedDescriptionParser()
     {
         Description test = Description.createTestDescription( T1.class, "flakyTest[3: (Test11); Test12; Test13;]" );
-        ClassMethod classMethod = cutTestClassAndMethod( test );
-        assertTrue( classMethod.isValid() );
+        ClassMethod classMethod = JUnit4ProviderUtil.toClassMethod( test );
+        assertTrue( classMethod.isValidTest() );
         assertEquals( "flakyTest[3: (Test11); Test12; Test13;]", classMethod.getMethod() );
         assertEquals( T1.class.getName(), classMethod.getClazz() );
     }
diff --git a/surefire-providers/common-junit48/src/main/java/org/apache/maven/surefire/common/junit48/JUnit46StackTraceWriter.java b/surefire-providers/common-junit48/src/main/java/org/apache/maven/surefire/common/junit48/JUnit46StackTraceWriter.java
deleted file mode 100644
index 4051e19..0000000
--- a/surefire-providers/common-junit48/src/main/java/org/apache/maven/surefire/common/junit48/JUnit46StackTraceWriter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package org.apache.maven.surefire.common.junit48;
-
-/*
- * 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.common.junit4.JUnit4StackTraceWriter;
-import org.junit.runner.notification.Failure;
-
-/**
- * A stacktrace writer that requires at least junit 4.6 to run. Note that we only use this for 4.8 and higher
- * <br>
- * Writes out a specific {@link org.junit.runner.notification.Failure} for
- * surefire as a stacktrace.
- *
- * @author Karl M. Davis
- * @author Kristian Rosenvold
- */
-public class JUnit46StackTraceWriter
-    extends JUnit4StackTraceWriter
-{
-
-    /**
-     * Constructor.
-     *
-     * @param junitFailure the {@link org.junit.runner.notification.Failure} that this will be operating on
-     */
-    public JUnit46StackTraceWriter( Failure junitFailure )
-    {
-        super( junitFailure );
-    }
-
-
-    @Override
-    protected final String getTestClassName()
-    {
-        return junitFailure.getDescription().getClassName();
-    }
-
-    @Override
-    protected String getTestMethodName()
-    {
-        return junitFailure.getDescription().getMethodName();
-    }
-}
diff --git a/surefire-providers/surefire-junit-platform/pom.xml b/surefire-providers/surefire-junit-platform/pom.xml
index 76bc92c..e551fef 100644
--- a/surefire-providers/surefire-junit-platform/pom.xml
+++ b/surefire-providers/surefire-junit-platform/pom.xml
@@ -139,6 +139,10 @@
                 <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
                 <configuration>
                     <jvm>${java.home}/bin/java</jvm>
+                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                    <includes>
+                        <include>**/JUnit47SuiteTest.java</include>
+                    </includes>
                 </configuration>
             </plugin>
         </plugins>
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 85227f3..ce80643 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
@@ -20,17 +20,15 @@ package org.apache.maven.surefire.junitplatform;
  */
 
 import static org.apache.maven.surefire.report.SimpleReportEntry.ignored;
-import static org.junit.platform.engine.TestExecutionResult.Status.ABORTED;
-import static org.junit.platform.engine.TestExecutionResult.Status.FAILED;
+import static org.apache.maven.surefire.report.SimpleReportEntry.withException;
 
 import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.maven.surefire.report.PojoStackTraceWriter;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.SimpleReportEntry;
 import org.apache.maven.surefire.report.StackTraceWriter;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 import org.junit.platform.engine.TestExecutionResult;
 import org.junit.platform.engine.TestSource;
 import org.junit.platform.engine.support.descriptor.ClassSource;
@@ -38,7 +36,6 @@ import org.junit.platform.engine.support.descriptor.MethodSource;
 import org.junit.platform.launcher.TestExecutionListener;
 import org.junit.platform.launcher.TestIdentifier;
 import org.junit.platform.launcher.TestPlan;
-import org.junit.platform.launcher.listeners.LegacyReportingUtils;
 
 /**
  * @since 2.22.0
@@ -46,12 +43,9 @@ import org.junit.platform.launcher.listeners.LegacyReportingUtils;
 final class RunListenerAdapter
     implements TestExecutionListener
 {
-
     private final RunListener runListener;
 
-    private TestPlan testPlan;
-
-    private Set<TestIdentifier> testSetNodes = ConcurrentHashMap.newKeySet();
+    private volatile TestPlan testPlan;
 
     RunListenerAdapter( RunListener runListener )
     {
@@ -61,13 +55,13 @@ final class RunListenerAdapter
     @Override
     public void testPlanExecutionStarted( TestPlan testPlan )
     {
-        updateTestPlan( testPlan );
+        this.testPlan = testPlan;
     }
 
     @Override
     public void testPlanExecutionFinished( TestPlan testPlan )
     {
-        updateTestPlan( null );
+        this.testPlan = null;
     }
 
     @Override
@@ -76,117 +70,82 @@ final class RunListenerAdapter
         if ( testIdentifier.isContainer()
                         && testIdentifier.getSource().filter( ClassSource.class::isInstance ).isPresent() )
         {
-            startTestSetIfPossible( testIdentifier );
+            runListener.testSetStarting( createTestSetReportEntry( testIdentifier ) );
         }
-        if ( testIdentifier.isTest() )
+        else if ( testIdentifier.isTest() )
         {
-            ensureTestSetStarted( testIdentifier );
             runListener.testStarting( createReportEntry( testIdentifier ) );
         }
     }
 
     @Override
-    public void executionSkipped( TestIdentifier testIdentifier, String reason )
-    {
-        ensureTestSetStarted( testIdentifier );
-        String source = getLegacyReportingClassName( testIdentifier );
-        runListener.testSkipped( ignored( source, getLegacyReportingName( testIdentifier ), reason ) );
-        completeTestSetIfNecessary( testIdentifier );
-    }
-
-    @Override
-    public void executionFinished(
-                    TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
-    {
-        if ( testExecutionResult.getStatus() == ABORTED )
-        {
-            runListener.testAssumptionFailure( createReportEntry( testIdentifier, testExecutionResult ) );
-        }
-        else if ( testExecutionResult.getStatus() == FAILED )
-        {
-            reportFailedTest( testIdentifier, testExecutionResult );
-        }
-        else if ( testIdentifier.isTest() )
-        {
-            runListener.testSucceeded( createReportEntry( testIdentifier ) );
-        }
-        completeTestSetIfNecessary( testIdentifier );
-    }
-
-    private void updateTestPlan( TestPlan testPlan )
-    {
-        this.testPlan = testPlan;
-        testSetNodes.clear();
-    }
-
-    private void ensureTestSetStarted( TestIdentifier testIdentifier )
-    {
-        if ( isTestSetStarted( testIdentifier ) )
-        {
-            return;
-        }
-        if ( testIdentifier.isTest() )
-        {
-            startTestSet( testPlan.getParent( testIdentifier ).orElse( testIdentifier ) );
-        }
-        else
-        {
-            startTestSet( testIdentifier );
+    public void executionFinished( TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
+    {
+        boolean isClass = testIdentifier.isContainer()
+                && testIdentifier.getSource().filter( ClassSource.class::isInstance ).isPresent();
+
+        boolean isTest = testIdentifier.isTest();
+
+        if ( isClass || isTest )
+        {
+            switch ( testExecutionResult.getStatus() )
+            {
+                case ABORTED:
+                    TestSetReportEntry reportEntry = createReportEntry( testIdentifier, testExecutionResult );
+                    if ( isTest )
+                    {
+                        runListener.testAssumptionFailure( reportEntry );
+                    }
+                    else
+                    {
+                        runListener.testSetCompleted( reportEntry );
+                    }
+                    break;
+                case FAILED:
+                    reportEntry = createReportEntry( testIdentifier, testExecutionResult );
+                    if ( !isTest )
+                    {
+                        runListener.testSetCompleted( reportEntry );
+                    }
+                    else if ( testExecutionResult.getThrowable()
+                            .filter( AssertionError.class::isInstance ).isPresent() )
+                    {
+                        runListener.testFailed( reportEntry );
+                    }
+                    else
+                    {
+                        runListener.testError( reportEntry );
+                    }
+                    break;
+                default:
+                    reportEntry = createReportEntry( testIdentifier );
+                    if ( isTest )
+                    {
+                        runListener.testSucceeded( reportEntry );
+                    }
+                    else
+                    {
+                        runListener.testSetCompleted( reportEntry );
+                    }
+            }
         }
     }
 
-    private boolean isTestSetStarted( TestIdentifier testIdentifier )
-    {
-        return testSetNodes.contains( testIdentifier )
-                        || testPlan.getParent( testIdentifier ).map( this::isTestSetStarted ).orElse( false );
-    }
-
-    private void startTestSetIfPossible( TestIdentifier testIdentifier )
-    {
-        if ( !isTestSetStarted( testIdentifier ) )
-        {
-            startTestSet( testIdentifier );
-        }
-    }
-
-    private void completeTestSetIfNecessary( TestIdentifier testIdentifier )
-    {
-        if ( testSetNodes.contains( testIdentifier ) )
-        {
-            completeTestSet( testIdentifier );
-        }
-    }
-
-    private void startTestSet( TestIdentifier testIdentifier )
-    {
-        runListener.testSetStarting( createTestSetReportEntry( testIdentifier ) );
-        testSetNodes.add( testIdentifier );
-    }
-
-    private void completeTestSet( TestIdentifier testIdentifier )
+    @Override
+    public void executionSkipped( TestIdentifier testIdentifier, String reason )
     {
-        runListener.testSetCompleted( createTestSetReportEntry( testIdentifier ) );
-        testSetNodes.remove( testIdentifier );
-    }
-
-    private void reportFailedTest(
-                    TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
-    {
-        SimpleReportEntry reportEntry = createReportEntry( testIdentifier, testExecutionResult );
-        if ( testExecutionResult.getThrowable().filter( AssertionError.class::isInstance ).isPresent() )
-        {
-            runListener.testFailed( reportEntry );
-        }
-        else
-        {
-            runListener.testError( reportEntry );
-        }
+        String[] classMethodName = toClassMethodName( testIdentifier );
+        String className = classMethodName[0];
+        String methodName = classMethodName[1];
+        runListener.testSkipped( ignored( className, methodName, reason ) );
     }
 
     private SimpleReportEntry createTestSetReportEntry( TestIdentifier testIdentifier )
     {
-        return new SimpleReportEntry(
-                        JUnitPlatformProvider.class.getName(), testIdentifier.getLegacyReportingName() );
+        String[] classMethodName = toClassMethodName( testIdentifier );
+        String className = classMethodName[0];
+        String methodName = classMethodName[1];
+        return new SimpleReportEntry( className, methodName );
     }
 
     private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier )
@@ -194,79 +153,88 @@ final class RunListenerAdapter
         return createReportEntry( testIdentifier, (StackTraceWriter) null );
     }
 
-    private SimpleReportEntry createReportEntry(
-                    TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
-    {
-        return createReportEntry(
-                        testIdentifier, getStackTraceWriter( testIdentifier, testExecutionResult ) );
-    }
-
-    private SimpleReportEntry createReportEntry(
-                    TestIdentifier testIdentifier, StackTraceWriter stackTraceWriter )
-    {
-        String source = getLegacyReportingClassName( testIdentifier );
-        String name = getLegacyReportingName( testIdentifier );
-
-        return SimpleReportEntry.withException( source, name, stackTraceWriter );
-    }
-
-    private String getLegacyReportingName( TestIdentifier testIdentifier )
+    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier,
+                                                 TestExecutionResult testExecutionResult )
     {
-        // Surefire cuts off the name at the first '(' character. Thus, we have to pick a different
-        // character to represent parentheses. "()" are removed entirely to maximize compatibility with
-        // existing reporting tools because in the old days test methods used to not have parameters.
-        return testIdentifier
-                        .getLegacyReportingName()
-                        .replace( "()", "" )
-                        .replace( '(', '{' )
-                        .replace( ')', '}' );
+        return createReportEntry( testIdentifier, toStackTraceWriter( testIdentifier, testExecutionResult ) );
     }
 
-    private String getLegacyReportingClassName( TestIdentifier testIdentifier )
+    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier, StackTraceWriter stackTraceWriter )
     {
-        return LegacyReportingUtils.getClassName( testPlan, testIdentifier );
+        String[] classMethodName = toClassMethodName( testIdentifier );
+        String className = classMethodName[0];
+        String methodName = classMethodName[1];
+        return withException( className, methodName, stackTraceWriter );
     }
 
-    private StackTraceWriter getStackTraceWriter(
-                    TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
+    private StackTraceWriter toStackTraceWriter( TestIdentifier testIdentifier,
+                                                 TestExecutionResult testExecutionResult )
     {
-        Optional<Throwable> throwable = testExecutionResult.getThrowable();
-        if ( testExecutionResult.getStatus() == FAILED )
+        switch ( testExecutionResult.getStatus() )
         {
-            // Failed tests must have a StackTraceWriter, otherwise Surefire will fail
-            return getStackTraceWriter( testIdentifier, throwable.orElse( null ) );
+            case ABORTED:
+            case FAILED:
+                // Failed tests must have a StackTraceWriter, otherwise Surefire will fail
+                return toStackTraceWriter( testIdentifier, testExecutionResult.getThrowable().orElse( null ) );
+            default:
+                return testExecutionResult.getThrowable().map( t -> toStackTraceWriter( testIdentifier, t ) )
+                        .orElse( null );
         }
-        return throwable.map( t -> getStackTraceWriter( testIdentifier, t ) ).orElse( null );
     }
 
-    private StackTraceWriter getStackTraceWriter( TestIdentifier testIdentifier, Throwable throwable )
+    private StackTraceWriter toStackTraceWriter( TestIdentifier testIdentifier, Throwable throwable )
     {
-        String className = getClassName( testIdentifier );
-        String methodName = getMethodName( testIdentifier ).orElse( "" );
+        String[] classMethodName = toClassMethodName( testIdentifier );
+        String className = classMethodName[0];
+        String methodName = classMethodName[1];
         return new PojoStackTraceWriter( className, methodName, throwable );
     }
 
-    private String getClassName( TestIdentifier testIdentifier )
+    /**
+     * <ul>
+     *     <li>[0] class name - used in stacktrace parser</li>
+     *     <li>[1] class display name</li>
+     *     <li>[2] method signature - used in stacktrace parser</li>
+     *     <li>[3] method display name</li>
+     * </ul>
+     *
+     * @param testIdentifier a class or method
+     * @return 4 elements string array
+     */
+    private String[] toClassMethodName( TestIdentifier testIdentifier )
     {
-        TestSource testSource = testIdentifier.getSource().orElse( null );
-        if ( testSource instanceof ClassSource )
+        Optional<TestSource> testSource = testIdentifier.getSource();
+        String display = testIdentifier.getDisplayName();
+
+        if ( testSource.filter( MethodSource.class::isInstance ).isPresent() )
         {
-            return ( (ClassSource) testSource ).getJavaClass().getName();
+            MethodSource methodSource = testSource.map( MethodSource.class::cast ).get();
+
+            String source = testPlan.getParent( testIdentifier )
+                    .map( this::toClassMethodName )
+                    .map( s -> s[0] )
+                    .orElse( methodSource.getClassName() );
+
+            String method = methodSource.getMethodName();
+            boolean useMethod = display.equals( method ) || display.equals( method + "()" );
+            String name = useMethod ? method : display;
+
+            return new String[] { source, name };
         }
-        if ( testSource instanceof MethodSource )
+        else if ( testSource.filter( ClassSource.class::isInstance ).isPresent() )
         {
-            return ( (MethodSource) testSource ).getClassName();
+            ClassSource classSource = testSource.map( ClassSource.class::cast ).get();
+            String className = classSource.getClassName();
+            String simpleClassName = className.substring( 1 + className.lastIndexOf( '.' ) );
+            String source = display.equals( simpleClassName ) ? className : display;
+            return new String[] { source, source };
         }
-        return testPlan.getParent( testIdentifier ).map( this::getClassName ).orElse( "" );
-    }
-
-    private Optional<String> getMethodName( TestIdentifier testIdentifier )
-    {
-        TestSource testSource = testIdentifier.getSource().orElse( null );
-        if ( testSource instanceof MethodSource )
+        else
         {
-            return Optional.of( ( (MethodSource) testSource ).getMethodName() );
+            String source = testPlan.getParent( testIdentifier )
+                    .map( TestIdentifier::getDisplayName )
+                    .orElse( display );
+            return new String[] { source, display };
         }
-        return Optional.empty();
     }
 }
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/ClassMethod.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnit47SuiteTest.java
similarity index 56%
rename from surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/ClassMethod.java
rename to surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnit47SuiteTest.java
index a3cccca..d0d4af9 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/ClassMethod.java
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnit47SuiteTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.common.junit4;
+package org.apache.maven.surefire.junitplatform;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,35 +19,28 @@ package org.apache.maven.surefire.common.junit4;
  * under the License.
  */
 
-import org.apache.maven.surefire.util.internal.StringUtils;
+import junit.framework.JUnit4TestAdapter;
+import junit.framework.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
 
 /**
- * Data transfer object of class and method literals.
+ * Adapt the JUnit4 tests which use only annotations to the JUnit3 test suite.
+ *
+ * @since 3.0.0-M4
  */
-public final class ClassMethod
+@SuiteClasses( {
+        JUnitPlatformProviderTest.class,
+        RunListenerAdapterTest.class,
+        TestMethodFilterTest.class,
+        TestPlanScannerFilterTest.class
+} )
+@RunWith( Suite.class )
+public class JUnit47SuiteTest
 {
-    private final String clazz;
-
-    private final String method;
-
-    public ClassMethod( String clazz, String method )
-    {
-        this.clazz = clazz;
-        this.method = method;
-    }
-
-    public boolean isValid()
-    {
-        return !StringUtils.isBlank( clazz ) && !StringUtils.isBlank( method );
-    }
-
-    public String getClazz()
-    {
-        return clazz;
-    }
-
-    public String getMethod()
+    public static Test suite()
     {
-        return method;
+        return new JUnit4TestAdapter( JUnit47SuiteTest.class );
     }
 }
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTest.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTest.java
index c162905..7aa8011 100644
--- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTest.java
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTest.java
@@ -167,25 +167,25 @@ public class JUnitPlatformProviderTest
                         .verify( runListener )
                         .testSetStarting(
                                         new SimpleReportEntry(
-                                                        JUnitPlatformProvider.class.getName(),
+                                                TestClass1.class.getName(),
                                                         TestClass1.class.getName() ) );
         inOrder
                         .verify( runListener )
                         .testSetCompleted(
                                         new SimpleReportEntry(
-                                                        JUnitPlatformProvider.class.getName(),
+                                                TestClass1.class.getName(),
                                                         TestClass1.class.getName() ) );
         inOrder
                         .verify( runListener )
                         .testSetStarting(
                                         new SimpleReportEntry(
-                                                        JUnitPlatformProvider.class.getName(),
+                                                TestClass2.class.getName(),
                                                         TestClass2.class.getName() ) );
         inOrder
                         .verify( runListener )
                         .testSetCompleted(
                                         new SimpleReportEntry(
-                                                        JUnitPlatformProvider.class.getName(),
+                                                TestClass2.class.getName(),
                                                         TestClass2.class.getName() ) );
 
         assertThat( executionListener.summaries ).hasSize( 1 );
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 0ccb4fe..73f108c 100644
--- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTest.java
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTest.java
@@ -22,15 +22,18 @@ package org.apache.maven.surefire.junitplatform;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singleton;
 import static java.util.Collections.singletonList;
+import static org.fest.assertions.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.platform.engine.TestDescriptor.Type.CONTAINER;
 import static org.junit.platform.engine.TestDescriptor.Type.TEST;
+import static org.junit.platform.engine.TestExecutionResult.aborted;
+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.inOrder;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -38,11 +41,15 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import java.lang.reflect.Method;
 import java.util.Optional;
 
+import org.apache.maven.surefire.report.PojoStackTraceWriter;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.SimpleReportEntry;
+import org.apache.maven.surefire.report.StackTraceWriter;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.jupiter.api.DisplayName;
@@ -51,16 +58,16 @@ 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.TestExecutionResult;
 import org.junit.platform.engine.TestSource;
 import org.junit.platform.engine.UniqueId;
 import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;
 import org.junit.platform.engine.support.descriptor.ClassSource;
 import org.junit.platform.engine.support.descriptor.EngineDescriptor;
+import org.junit.platform.engine.support.descriptor.MethodSource;
 import org.junit.platform.launcher.TestIdentifier;
 import org.junit.platform.launcher.TestPlan;
 import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
+import org.opentest4j.TestSkippedException;
 
 /**
  * Unit tests for {@link RunListenerAdapter}.
@@ -69,7 +76,7 @@ import org.mockito.InOrder;
  */
 public class RunListenerAdapterTest
 {
-    private static final ConfigurationParameters CONFIG_PARAMS = mock(ConfigurationParameters.class);
+    private static final ConfigurationParameters CONFIG_PARAMS = mock( ConfigurationParameters.class );
 
     private RunListener listener;
 
@@ -120,7 +127,7 @@ 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 + "(String)", entry.getName() );
         assertEquals( MyTestClass.class.getName(), entry.getSourceName() );
         assertNull( entry.getStackTraceWriter() );
     }
@@ -139,7 +146,7 @@ public class RunListenerAdapterTest
         adapter.testPlanExecutionStarted( plan );
         adapter.executionStarted( TestIdentifier.from( engine ) );
         adapter.executionStarted( TestIdentifier.from( parent ) );
-        verify( listener ).testSetStarting( new SimpleReportEntry( JUnitPlatformProvider.class.getName(),
+        verify( listener ).testSetStarting( new SimpleReportEntry( MyTestClass.class.getName(),
                                                         MyTestClass.class.getName() ) );
         verifyNoMoreInteractions( listener );
 
@@ -152,7 +159,7 @@ public class RunListenerAdapterTest
         verifyNoMoreInteractions( listener );
 
         adapter.executionFinished( TestIdentifier.from( parent ), successful() );
-        verify( listener ).testSetCompleted( new SimpleReportEntry( JUnitPlatformProvider.class.getName(),
+        verify( listener ).testSetCompleted( new SimpleReportEntry( MyTestClass.class.getName(),
                                                         MyTestClass.class.getName() ) );
         verifyNoMoreInteractions( listener );
 
@@ -161,11 +168,10 @@ public class RunListenerAdapterTest
     }
 
     @Test
-    public void notifiedLazilyForTestSetWhenFirstTestWithoutClassDescriptorParentStarted()
+    public void displayNamesInClassAndMethods()
     {
         EngineDescriptor engine = newEngineDescriptor();
-        TestDescriptor parent = newTestDescriptor( engine.getUniqueId().append( "container", "noClass" ), "parent",
-                                        CONTAINER );
+        TestDescriptor parent = newClassDescriptor( "parent" );
         engine.addChild( parent );
         TestDescriptor child1 = newTestDescriptor( parent.getUniqueId().append( "test", "child1" ), "child1", TEST );
         parent.addChild( child1 );
@@ -176,14 +182,12 @@ public class RunListenerAdapterTest
         adapter.testPlanExecutionStarted( plan );
         adapter.executionStarted( TestIdentifier.from( engine ) );
         adapter.executionStarted( TestIdentifier.from( parent ) );
+        verify( listener ).testSetStarting( new SimpleReportEntry( "parent", "parent" ) );
         verifyZeroInteractions( listener );
 
         adapter.executionStarted( TestIdentifier.from( child1 ) );
-        InOrder inOrder = inOrder( listener );
-        inOrder.verify( listener )
-                .testSetStarting( new SimpleReportEntry( JUnitPlatformProvider.class.getName(), "parent" ) );
-        inOrder.verify( listener ).testStarting( new SimpleReportEntry( "parent", "child1" ) );
-        inOrder.verifyNoMoreInteractions();
+        verify( listener ).testStarting( new SimpleReportEntry( "parent", "child1" ) );
+        verifyNoMoreInteractions( listener );
 
         adapter.executionFinished( TestIdentifier.from( child1 ), successful() );
         verify( listener ).testSucceeded( new SimpleReportEntry( "parent", "child1" ) );
@@ -198,8 +202,7 @@ public class RunListenerAdapterTest
         verifyNoMoreInteractions( listener );
 
         adapter.executionFinished( TestIdentifier.from( parent ), successful() );
-        verify( listener )
-                        .testSetCompleted( new SimpleReportEntry( JUnitPlatformProvider.class.getName(), "parent" ) );
+        verify( listener ).testSetCompleted( new SimpleReportEntry( "parent", "parent" ) );
         verifyNoMoreInteractions( listener );
 
         adapter.executionFinished( TestIdentifier.from( engine ), successful() );
@@ -207,7 +210,7 @@ public class RunListenerAdapterTest
     }
 
     @Test
-    public void notifiedForTestSetForSingleNodeEngine()
+    public void notifiedForUnclassifiedTestIdentifier()
     {
         EngineDescriptor engine = new EngineDescriptor( UniqueId.forEngine( "engine" ), "engine" )
         {
@@ -221,18 +224,12 @@ public class RunListenerAdapterTest
 
         adapter.testPlanExecutionStarted( plan );
         adapter.executionStarted( TestIdentifier.from( engine ) );
-        InOrder inOrder = inOrder( listener );
-        inOrder.verify( listener )
-                .testSetStarting( new SimpleReportEntry( JUnitPlatformProvider.class.getName(), "engine" ) );
-        inOrder.verify( listener ).testStarting( new SimpleReportEntry( "<unrooted>", "engine" ) );
-        inOrder.verifyNoMoreInteractions();
+        verify( listener ).testStarting( new SimpleReportEntry( "engine", "engine" ) );
+        verifyNoMoreInteractions( listener );
 
         adapter.executionFinished( TestIdentifier.from( engine ), successful() );
-        inOrder = inOrder( listener );
-        inOrder.verify( listener ).testSucceeded( new SimpleReportEntry( "<unrooted>", "engine" ) );
-        inOrder.verify( listener )
-                .testSetCompleted( new SimpleReportEntry( JUnitPlatformProvider.class.getName(), "engine" ) );
-        inOrder.verifyNoMoreInteractions();
+        verify( listener ).testSucceeded( new SimpleReportEntry( "engine", "engine" ) );
+        verifyNoMoreInteractions( listener );
     }
 
     @Test
@@ -279,48 +276,54 @@ public class RunListenerAdapterTest
     public void notifiedWhenMethodExecutionAborted()
                     throws Exception
     {
-        adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.aborted( null ) );
+        adapter.executionFinished( newMethodIdentifier(), aborted( null ) );
         verify( listener ).testAssumptionFailure( any() );
     }
 
     @Test
     public void notifiedWhenClassExecutionAborted()
     {
-        adapter.executionFinished( newClassIdentifier(), TestExecutionResult.aborted( null ) );
-        verify( listener ).testAssumptionFailure( any() );
+        TestSkippedException t = new TestSkippedException( "skipped" );
+        adapter.executionFinished( newClassIdentifier(), aborted( t ) );
+        String name = MyTestClass.class.getName();
+        StackTraceWriter stw = new PojoStackTraceWriter( name, name, t );
+        verify( listener ).testSetCompleted( eq( new SimpleReportEntry( name, name, stw, null ) ) );
     }
 
     @Test
-    public void notifiedWhenMethodExecutionFailedWithAnAssertionError()
+    public void notifiedWhenMethodExecutionFailed()
                     throws Exception
     {
-        adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.failed( new AssertionError() ) );
+        adapter.executionFinished( newMethodIdentifier(), failed( new AssertionError() ) );
         verify( listener ).testFailed( any() );
     }
 
     @Test
-    public void notifiedWhenMethodExecutionFailedWithANonAssertionError()
+    public void notifiedWhenMethodExecutionFailedWithError()
                     throws Exception
     {
-        adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.failed( new RuntimeException() ) );
+        adapter.executionFinished( newMethodIdentifier(), failed( new RuntimeException() ) );
         verify( listener ).testError( any() );
     }
 
     @Test
     public void notifiedWithCorrectNamesWhenClassExecutionFailed()
     {
-        ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+        ArgumentCaptor<TestSetReportEntry> entryCaptor = ArgumentCaptor.forClass( TestSetReportEntry.class );
         TestPlan testPlan = TestPlan.from( singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) );
         adapter.testPlanExecutionStarted( testPlan );
 
-        adapter.executionFinished(
-                        identifiersAsParentOnTestPlan( testPlan, newEngineDescriptor(), newClassDescriptor() ),
-                        TestExecutionResult.failed( new AssertionError() ) );
-        verify( listener ).testFailed( entryCaptor.capture() );
+        adapter.executionFinished( identifiersAsParentOnTestPlan( testPlan, newClassDescriptor() ),
+                failed( new AssertionError() ) );
+        verify( listener ).testSetCompleted( entryCaptor.capture() );
 
         ReportEntry entry = entryCaptor.getValue();
         assertEquals( MyTestClass.class.getTypeName(), entry.getSourceName() );
+        assertEquals( MyTestClass.class.getTypeName(), entry.getName() );
         assertNotNull( entry.getStackTraceWriter() );
+        assertNotNull( entry.getStackTraceWriter().getThrowable() );
+        assertThat( entry.getStackTraceWriter().getThrowable().getTarget() )
+                .isInstanceOf( AssertionError.class );
     }
 
     @Test
@@ -342,7 +345,7 @@ public class RunListenerAdapterTest
 
         adapter.executionFinished( TestIdentifier.from( classDescriptor ), successful() );
 
-        verify( listener ).testSetCompleted( new SimpleReportEntry( JUnitPlatformProvider.class.getName(),
+        verify( listener ).testSetCompleted( new SimpleReportEntry( MyTestClass.class.getName(),
                 MyTestClass.class.getName() ) );
 
         verify( listener, never() ).testSucceeded( any() );
@@ -375,7 +378,7 @@ public class RunListenerAdapterTest
 
         TestIdentifier child =
                 newSourcelessChildIdentifierWithParent( plan, "Parent", ClassSource.from( MyTestClass.class ) );
-        adapter.executionFinished( child, TestExecutionResult.failed( new RuntimeException() ) );
+        adapter.executionFinished( child, failed( new RuntimeException() ) );
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
         verify( listener ).testError( entryCaptor.capture() );
         assertNotNull( entryCaptor.getValue().getStackTraceWriter() );
@@ -388,7 +391,7 @@ public class RunListenerAdapterTest
         adapter.testPlanExecutionStarted( plan );
 
         TestIdentifier child = newSourcelessChildIdentifierWithParent( plan, "Parent", null );
-        adapter.executionFinished( child, TestExecutionResult.failed( new RuntimeException( "message" ) ) );
+        adapter.executionFinished( child, failed( new RuntimeException( "message" ) ) );
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
         verify( listener ).testError( entryCaptor.capture() );
         assertNotNull( entryCaptor.getValue().getStackTraceWriter() );
@@ -401,7 +404,7 @@ public class RunListenerAdapterTest
     public void stackTraceWriterPresentEvenWithoutException()
                     throws Exception
     {
-        adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.failed( null ) );
+        adapter.executionFinished( newMethodIdentifier(), failed( null ) );
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
         verify( listener ).testError( entryCaptor.capture() );
         assertNotNull( entryCaptor.getValue().getStackTraceWriter() );
@@ -411,8 +414,22 @@ public class RunListenerAdapterTest
     public void displayNamesIgnoredInReport()
                     throws NoSuchMethodException
     {
-        TestMethodTestDescriptor descriptor = new TestMethodTestDescriptor( newId(), MyTestClass.class,
-                MyTestClass.class.getDeclaredMethod( "myNamedTestMethod" ) );
+        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" ) );
 
         TestIdentifier factoryIdentifier = TestIdentifier.from( descriptor );
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
@@ -422,7 +439,7 @@ public class RunListenerAdapterTest
 
         ReportEntry value = entryCaptor.getValue();
 
-        assertEquals( "myNamedTestMethod", value.getName() );
+        assertEquals( "some display name", value.getName() );
     }
 
     private static TestIdentifier newMethodIdentifier()
@@ -445,6 +462,12 @@ public class RunListenerAdapterTest
         return TestIdentifier.from( newClassDescriptor() );
     }
 
+    private static TestDescriptor newClassDescriptor( String displayName )
+    {
+        return new ClassTestDescriptor( UniqueId.root( "class", MyTestClass.class.getName() ),
+                c -> displayName, MyTestClass.class, CONFIG_PARAMS ) {};
+    }
+
     private static TestDescriptor newClassDescriptor()
     {
         return new ClassTestDescriptor( UniqueId.root( "class", MyTestClass.class.getName() ), MyTestClass.class, CONFIG_PARAMS );
@@ -516,6 +539,13 @@ public class RunListenerAdapterTest
         return childIdentifier;
     }
 
+    private static TestIdentifier identifiersAsParentOnTestPlan( TestPlan plan, TestDescriptor root )
+    {
+        TestIdentifier rootIdentifier = TestIdentifier.from( root );
+        plan.add( rootIdentifier );
+        return rootIdentifier;
+    }
+
     private static UniqueId newId()
     {
         return UniqueId.forEngine( "engine" );
diff --git a/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java b/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java
index 977ac9e..fad855e 100644
--- a/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java
+++ b/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java
@@ -42,10 +42,10 @@ public class JUnitTestSetTest
         JUnitTestSet testSet = new JUnitTestSet( Suite.class, reflector );
         SuccessListener listener = new SuccessListener();
         testSet.execute( listener, testClassLoader );
-        List succeededTests = listener.getSucceededTests();
+        List<ReportEntry> succeededTests = listener.getSucceededTests();
         assertEquals( 1, succeededTests.size() );
         assertEquals( "testSuccess(org.apache.maven.surefire.junit.JUnitTestSetTest$AlwaysSucceeds)",
-                      ( (ReportEntry) succeededTests.get( 0 ) ).getName() );
+                      succeededTests.get( 0 ).getName() );
     }
 
     public static final class AlwaysSucceeds
@@ -81,7 +81,7 @@ public class JUnitTestSetTest
         @Override
         public void testSucceeded( ReportEntry report )
         {
-            this.succeededTests.add( report );
+            succeededTests.add( report );
         }
 
         @Override
@@ -118,7 +118,7 @@ public class JUnitTestSetTest
             testSkipped( report );
         }
 
-        public List getSucceededTests()
+        List<ReportEntry> getSucceededTests()
         {
             return succeededTests;
         }
diff --git a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/TestResolverFilter.java b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/TestResolverFilter.java
index 4205b14..72669a8 100644
--- a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/TestResolverFilter.java
+++ b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/TestResolverFilter.java
@@ -1 +1 @@
-package org.apache.maven.surefire.junit4;

/*
 * 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.common.junit4.ClassMethod;
import org.apache.maven.surefire.testset.TestListResolver;
import org.junit.runner.
 Description;
import org.junit.runner.manipulation.Filter;

import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.cutTestClassAndMethod;
import static org.apache.maven.surefire.testset.TestListResolver.toClassFileName;

/**
 * Method filter used in {@link JUnit4Provider}.
 */
final class TestResolverFilter
    extends Filter
{
    private final TestListResolver methodFilter;

    TestResolverFilter( TestListResolver methodFilter )
    {
        this.methodFilter = methodFilter;
    }

    @Override
    public boolean shouldRun( Description description )
    {
        // class: Java class name; method: 1. "testMethod" or 2. "testMethod[5+whatever]" in @Parameterized
        final ClassMethod cm = cutTestClassAndMethod( description );
        final boolean isSuite = description.isSuite();
        final boolean isValidTest = description.isTest() && cm.isValid();
        final String clazz = cm.getClazz();
        final String method = cm.getMethod();
        return is
 Suite || isValidTest && methodFilter.shouldRun( toClassFileName( clazz ), method );
    }

    @Override
    public String describe()
    {
        return methodFilter.toString();
    }
}
\ No newline at end of file
+package org.apache.maven.surefire.junit4;

/*
 * 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.util.internal.ClassMethod;
import org.apache.maven.surefire.testset.TestListResolver;
import org.junit.runner.
 Description;
import org.junit.runner.manipulation.Filter;

import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.toClassMethod;
import static org.apache.maven.surefire.testset.TestListResolver.toClassFileName;

/**
 * Method filter used in {@link JUnit4Provider}.
 */
final class TestResolverFilter
    extends Filter
{
    private final TestListResolver methodFilter;

    TestResolverFilter( TestListResolver methodFilter )
    {
        this.methodFilter = methodFilter;
    }

    @Override
    public boolean shouldRun( Description description )
    {
        // class: Java class name; method: 1. "testMethod" or 2. "testMethod[5+whatever]" in @Parameterized
        final ClassMethod cm = toClassMethod( description );
        final boolean isSuite = description.isSuite();
        final boolean isValidTest = description.isTest() && cm.isValidTest();
        final String clazz = cm.getClazz();
        final String method = cm.getMethod();
        return isSuite || isV
 alidTest && methodFilter.shouldRun( toClassFileName( clazz ), method );
    }

    @Override
    public String describe()
    {
        return methodFilter.toString();
    }
}
\ No newline at end of file
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListener.java
index ae00fdb..03e314e 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListener.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListener.java
@@ -20,7 +20,7 @@ package org.apache.maven.surefire.junitcore;
  */
 
 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
-import org.apache.maven.surefire.common.junit48.JUnit46StackTraceWriter;
+import org.apache.maven.surefire.common.junit4.JUnit4StackTraceWriter;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.StackTraceWriter;
 import org.junit.runner.Description;
@@ -29,6 +29,8 @@ import org.junit.runner.notification.Failure;
 
 import java.util.Map;
 
+import static org.apache.maven.surefire.util.internal.TestClassMethodNameUtils.extractClassName;
+
 /**
  * Noteworthy things about JUnit4 listening:
  * <br>
@@ -95,7 +97,7 @@ public class JUnitCoreRunListener
     {
         if ( description.isTest() )
         {
-            final String testClassName = extractDescriptionClassName( description );
+            final String testClassName = extractClassName( description.getDisplayName() );
             if ( testClassName != null )
             {
                 final TestSet testSet;
@@ -121,18 +123,6 @@ public class JUnitCoreRunListener
     @Override
     protected StackTraceWriter createStackTraceWriter( Failure failure )
     {
-        return new JUnit46StackTraceWriter( failure );
-    }
-
-    @Override
-    protected String extractDescriptionClassName( Description description )
-    {
-        return description.getClassName();
-    }
-
-    @Override
-    protected String extractDescriptionMethodName( Description description )
-    {
-        return description.getMethodName();
+        return new JUnit4StackTraceWriter( failure );
     }
 }
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java
index f8d2fb7..71ec93d 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java
@@ -19,6 +19,7 @@ package org.apache.maven.surefire.junitcore;
  * under the License.
  */
 
+import org.apache.maven.surefire.util.internal.ClassMethod;
 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.RunListener;
@@ -32,6 +33,7 @@ import org.junit.runner.notification.Failure;
 import java.util.Collections;
 import java.util.Map;
 
+import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.toClassMethod;
 import static org.apache.maven.surefire.util.internal.ObjectUtils.systemProps;
 
 /**
@@ -63,13 +65,14 @@ public class NonConcurrentRunListener
     @Override
     protected SimpleReportEntry createReportEntry( Description description )
     {
-        return new SimpleReportEntry( extractDescriptionClassName( description ), description.getDisplayName() );
+        ClassMethod classMethod = toClassMethod( description );
+        return new SimpleReportEntry( classMethod.getClazz(), classMethod.getMethod() );
     }
 
     private TestSetReportEntry createReportEntryForTestSet( Description description, Map<String, String> systemProps )
     {
-        String testClassName = extractDescriptionClassName( description );
-        return new SimpleReportEntry( testClassName, testClassName, systemProps );
+        ClassMethod classMethod = toClassMethod( description );
+        return new SimpleReportEntry( classMethod.getClazz(), classMethod.getClazz(), systemProps );
     }
 
     private TestSetReportEntry createTestSetReportEntryStarted( Description description )
@@ -83,18 +86,6 @@ public class NonConcurrentRunListener
     }
 
     @Override
-    protected String extractDescriptionClassName( Description description )
-    {
-        return description.getClassName();
-    }
-
-    @Override
-    protected String extractDescriptionMethodName( Description description )
-    {
-        return description.getMethodName();
-    }
-
-    @Override
     public void testStarted( Description description )
         throws Exception
     {
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListenerTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListenerTest.java
index b467f63..08bbd2a 100644
--- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListenerTest.java
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreRunListenerTest.java
@@ -43,7 +43,6 @@ public class JUnitCoreRunListenerTest
     extends TestCase
 {
     public void testTestRunStarted()
-        throws Exception
     {
         RunListener jUnit4TestSetReporter =
             new JUnitCoreRunListener( new MockReporter(), new HashMap<String, TestSet>() );
@@ -55,7 +54,6 @@ public class JUnitCoreRunListenerTest
     }
 
     public void testFailedAssumption()
-        throws Exception
     {
         RunListener jUnit4TestSetReporter =
             new JUnitCoreRunListener( new MockReporter(), new HashMap<String, TestSet>() );
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGReporter.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGReporter.java
index 19cd22d..8cb7641 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGReporter.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGReporter.java
@@ -25,6 +25,7 @@ import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.SimpleReportEntry;
 
+import org.testng.IClass;
 import org.testng.ISuite;
 import org.testng.ISuiteListener;
 import org.testng.ITestContext;
@@ -64,54 +65,45 @@ public class TestNGReporter
     @Override
     public void onTestStart( ITestResult result )
     {
-        String group = groupString( result.getMethod().getGroups(), result.getTestClass().getName() );
-        String userFriendlyTestName = getUserFriendlyTestName( result );
-        reporter.testStarting( new CategorizedReportEntry( getSource( result ), userFriendlyTestName, group ) );
-    }
-
-    private String getSource( ITestResult result )
-    {
-        return result.getTestClass().getName();
+        String clazz = result.getTestClass().getName();
+        String group = groupString( result.getMethod().getGroups(), clazz );
+        reporter.testStarting( new CategorizedReportEntry( clazz, result.getName(), group ) );
     }
 
     @Override
     public void onTestSuccess( ITestResult result )
     {
-        ReportEntry report = new SimpleReportEntry( getSource( result ), getUserFriendlyTestName( result ) );
+        ReportEntry report = new SimpleReportEntry( result.getTestClass().getName(), result.getName() );
         reporter.testSucceeded( report );
     }
 
     @Override
     public void onTestFailure( ITestResult result )
     {
-        ReportEntry report = withException( getSource( result ), getUserFriendlyTestName( result ),
-                new PojoStackTraceWriter( result.getTestClass().getRealClass().getName(),
+        IClass clazz = result.getTestClass();
+        ReportEntry report = withException( clazz.getName(), result.getName(),
+                new PojoStackTraceWriter( clazz.getRealClass().getName(),
                         result.getMethod().getMethodName(),
                         result.getThrowable() ) );
 
         reporter.testFailed( report );
     }
 
-    private static String getUserFriendlyTestName( ITestResult result )
-    {
-        // This is consistent with the JUnit output
-        return result.getName() + "(" + result.getTestClass().getName() + ")";
-    }
-
     @Override
     public void onTestSkipped( ITestResult result )
     {
         Throwable t = result.getThrowable();
         String reason = t == null ? null : t.getMessage();
-        ReportEntry report = ignored( getSource( result ), getUserFriendlyTestName( result ), reason );
+        ReportEntry report = ignored( result.getTestClass().getName(), result.getName(), reason );
         reporter.testSkipped( report );
     }
 
     @Override
     public void onTestFailedButWithinSuccessPercentage( ITestResult result )
     {
-        ReportEntry report = withException( getSource( result ), getUserFriendlyTestName( result ),
-                new PojoStackTraceWriter( result.getTestClass().getRealClass().getName(),
+        IClass clazz = result.getTestClass();
+        ReportEntry report = withException( clazz.getName(), result.getName(),
+                new PojoStackTraceWriter( clazz.getRealClass().getName(),
                         result.getMethod().getMethodName(),
                         result.getThrowable() ) );
 
diff --git a/surefire-report-parser/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportParser.java b/surefire-report-parser/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportParser.java
index da1b255..f6e644f 100644
--- a/surefire-report-parser/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportParser.java
+++ b/surefire-report-parser/src/main/java/org/apache/maven/plugins/surefire/report/SurefireReportParser.java
@@ -33,11 +33,10 @@ import javax.xml.parsers.ParserConfigurationException;
 
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.reporting.MavenReportException;
-import org.apache.maven.shared.utils.StringUtils;
 import org.apache.maven.shared.utils.io.DirectoryScanner;
 import org.xml.sax.SAXException;
 
-import static java.util.Collections.singletonList;
+import static org.apache.maven.shared.utils.StringUtils.split;
 
 /**
  *
@@ -57,7 +56,7 @@ public final class SurefireReportParser
 
     private final ConsoleLogger consoleLogger;
 
-    private List<File> reportsDirectories;
+    private final List<File> reportsDirectories;
 
     public SurefireReportParser( List<File> reportsDirectories, Locale locale, ConsoleLogger consoleLogger )
     {
@@ -104,21 +103,6 @@ public final class SurefireReportParser
         return testSuites;
     }
 
-    protected String parseTestSuiteName( String lineString )
-    {
-        return lineString.substring( lineString.lastIndexOf( "." ) + 1, lineString.length() );
-    }
-
-    protected String parseTestSuitePackageName( String lineString )
-    {
-        return lineString.substring( lineString.indexOf( ":" ) + 2, lineString.lastIndexOf( "." ) );
-    }
-
-    protected String parseTestCaseName( String lineString )
-    {
-        return lineString.substring( 0, lineString.indexOf( "(" ) );
-    }
-
     public Map<String, String> getSummary( List<ReportTestSuite> suites )
     {
         Map<String, String> totalSummary = new HashMap<>();
@@ -164,11 +148,6 @@ public final class SurefireReportParser
         return totalSummary;
     }
 
-    public void setReportsDirectory( File reportsDirectory )
-    {
-        reportsDirectories = singletonList( reportsDirectory );
-    }
-
     public NumberFormat getNumberFormat()
     {
         return numberFormat;
@@ -238,9 +217,9 @@ public final class SurefireReportParser
 
         scanner.setBasedir( directory );
 
-        scanner.setIncludes( StringUtils.split( includes, "," ) );
+        scanner.setIncludes( split( includes, "," ) );
 
-        scanner.setExcludes( StringUtils.split( excludes, "," ) );
+        scanner.setExcludes( split( excludes, "," ) );
 
         scanner.scan();
 
diff --git a/surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportParserTest.java b/surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportParserTest.java
index 870cd92..7176c4d 100644
--- a/surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportParserTest.java
+++ b/surefire-report-parser/src/test/java/org/apache/maven/plugins/surefire/report/SurefireReportParserTest.java
@@ -29,10 +29,10 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
-import org.apache.maven.reporting.MavenReportException;
 
 import junit.framework.TestCase;
 
+import static java.util.Collections.singletonList;
 import static java.util.Locale.ENGLISH;
 
 /**
@@ -41,23 +41,11 @@ import static java.util.Locale.ENGLISH;
 public class SurefireReportParserTest
     extends TestCase
 {
-    private SurefireReportParser report;
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void setUp()
-        throws Exception
-    {
-        super.setUp();
-        report = new SurefireReportParser( null, ENGLISH, new NullConsoleLogger() );
-    }
-
     public void testParseXMLReportFiles()
-        throws MavenReportException, UnsupportedEncodingException
+        throws Exception
     {
-        report.setReportsDirectory( getTestDir() );
+        SurefireReportParser report =
+                new SurefireReportParser( singletonList( getTestDir() ), ENGLISH, new NullConsoleLogger() );
 
         List<ReportTestSuite> suites = report.parseXMLReportFiles();
 
@@ -79,21 +67,6 @@ public class SurefireReportParserTest
         return new File( URLDecoder.decode( resource.getPath(), "UTF-8" ) ).getAbsoluteFile();
     }
 
-    public void testParseTestSuiteName()
-    {
-        assertEquals( "CircleTest", report.parseTestSuiteName( "Battery: com.shape.CircleTest" ) );
-    }
-
-    public void testParseTestSuitePackageName()
-    {
-        assertEquals( "com.shape", report.parseTestSuitePackageName( "Battery: com.shape.CircleTest" ) );
-    }
-
-    public void testParseTestCaseName()
-    {
-        assertEquals( "testCase", report.parseTestCaseName( "testCase(com.shape.CircleTest)" ) );
-    }
-
     public void testGetSummary()
         throws Exception
     {
@@ -117,6 +90,8 @@ public class SurefireReportParserTest
 
         suites.add( tSuite2 );
 
+        SurefireReportParser report = new SurefireReportParser( null, ENGLISH, new NullConsoleLogger() );
+
         Map<String, String> testMap = report.getSummary( suites );
 
         assertEquals( 20, Integer.parseInt( testMap.get( "totalErrors" ) ) );
@@ -156,6 +131,8 @@ public class SurefireReportParserTest
 
         suites.add( tSuite3 );
 
+        SurefireReportParser report = new SurefireReportParser( null, ENGLISH, new NullConsoleLogger() );
+
         Map<String, List<ReportTestSuite>> groupMap = report.getSuitesGroupByPackage( suites );
 
         assertEquals( 2, groupMap.size() );
@@ -170,6 +147,7 @@ public class SurefireReportParserTest
     public void testComputePercentage()
         throws Exception
     {
+        SurefireReportParser report = new SurefireReportParser( null, ENGLISH, new NullConsoleLogger() );
         NumberFormat numberFormat = report.getNumberFormat();
 
         assertEquals( 70.00f, numberFormat.parse( report.computePercentage( 100, 20, 10, 0 ) ).floatValue(), 0 );
@@ -211,6 +189,8 @@ public class SurefireReportParserTest
 
         suites.add( tSuite2 );
 
+        SurefireReportParser report = new SurefireReportParser( null, ENGLISH, new NullConsoleLogger() );
+
         List<ReportTestCase> failures = report.getFailureDetails( suites );
 
         assertEquals( 2, failures.size() );