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 2018/05/03 11:50:32 UTC

[maven-surefire] branch 1330 created (now 41e80e5)

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

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


      at 41e80e5  Copy current sources from junit-platform-surefire-provider

This branch includes the following new commits:

     new 07778b7  junit5
     new f60dffe  Rename JUnitPlattformProviderInfo to JUnitPlatformProviderInfo
     new 82182d5  Bump JUnit versions and remove unnecessary dependency
     new 41e80e5  Copy current sources from junit-platform-surefire-provider

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


-- 
To stop receiving notification emails like this one, please contact
tibordigana@apache.org.

[maven-surefire] 02/04: Rename JUnitPlattformProviderInfo to JUnitPlatformProviderInfo

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

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

commit f60dffee88fde0eced0f567a122e527080090ab4
Author: Christian Stein <so...@gmail.com>
AuthorDate: Wed May 2 23:17:53 2018 +0200

    Rename JUnitPlattformProviderInfo to JUnitPlatformProviderInfo
---
 .../java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index dcfabae..414e084 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
@@ -1024,7 +1024,7 @@ public abstract class AbstractSurefireMojo
                               new TestNgProviderInfo( getTestNgArtifact() ),
                               new JUnitCoreProviderInfo( getJunitArtifact(), junitDepArtifact ),
                               new JUnit4ProviderInfo( getJunitArtifact(), junitDepArtifact ),
-                              new JUnitPlattformProviderInfo( getJunitArtifact() ),
+                              new JUnitPlatformProviderInfo( getJunitArtifact() ),
                               new JUnit3ProviderInfo() )
             .resolve();
     }
@@ -2884,12 +2884,12 @@ public abstract class AbstractSurefireMojo
 
     }
 
-    final class JUnitPlattformProviderInfo
+    final class JUnitPlatformProviderInfo
         implements ProviderInfo
     {
         private final Artifact junitArtifact;
 
-        JUnitPlattformProviderInfo( Artifact junitArtifact )
+        JUnitPlatformProviderInfo( Artifact junitArtifact )
         {
             this.junitArtifact = junitArtifact;
         }

-- 
To stop receiving notification emails like this one, please contact
tibordigana@apache.org.

[maven-surefire] 01/04: junit5

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

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

commit 07778b7e342a87ac9810494c9f6342b3135178ec
Author: Tibor17 <ti...@apache.org>
AuthorDate: Fri Apr 13 23:51:33 2018 +0200

    junit5
---
 .../plugin/surefire/AbstractSurefireMojo.java      |  46 ++-
 pom.xml                                            |   7 +-
 surefire-its/pom.xml                               |  23 +-
 .../maven/surefire/its/JUnit4VersionsIT.java       |  74 +++--
 .../org/apache/maven/surefire/its/JUnit5IT.java}   |  50 +---
 .../maven/surefire/its/JUnitPlatformIT.java}       |  48 +--
 .../apache/maven/surefire/its/JUnitVersion.java    |  80 +++++
 .../resources/{junit4 => junit-platform}/pom.xml   |  55 ++--
 .../src/test/java/junitplatform}/BasicTest.java    |  30 +-
 surefire-its/src/test/resources/junit4/pom.xml     | 141 +++++++--
 .../junit4/src/test/java/junit4/BasicTest.java     |   4 +-
 .../BasicTest.java => junit5/JUnit5Test.java}      |  35 ++-
 surefire-its/src/test/resources/junit5/pom.xml     |  77 +++++
 .../src/test/java/junit5/JUnit4Test.java}          |  19 +-
 .../src/test/java/junit5/JUnit5Test.java}          |  36 +--
 surefire-providers/pom.xml                         |   1 +
 surefire-providers/surefire-junit-platform/pom.xml | 167 +++++++++++
 .../junitplatform/JUnitPlatformProvider.java       | 202 +++++++++++++
 .../surefire/junitplatform/RunListenerAdapter.java | 150 ++++++++++
 .../junitplatform/TestPlanScannerFilter.java       |  64 ++++
 ...che.maven.surefire.providerapi.SurefireProvider |   1 +
 .../junitplatform/JUnitPlatformProviderTests.java  | 325 +++++++++++++++++++++
 .../junitplatform/RunListenerAdapterTests.java     | 252 ++++++++++++++++
 .../junitplatform/TestPlanScannerFilterTests.java  | 190 ++++++++++++
 24 files changed, 1837 insertions(+), 240 deletions(-)

diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index 34f805a..dcfabae 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
@@ -1024,6 +1024,7 @@ public abstract class AbstractSurefireMojo
                               new TestNgProviderInfo( getTestNgArtifact() ),
                               new JUnitCoreProviderInfo( getJunitArtifact(), junitDepArtifact ),
                               new JUnit4ProviderInfo( getJunitArtifact(), junitDepArtifact ),
+                              new JUnitPlattformProviderInfo( getJunitArtifact() ),
                               new JUnit3ProviderInfo() )
             .resolve();
     }
@@ -1540,7 +1541,17 @@ public abstract class AbstractSurefireMojo
         return dependencyResolver.isWithinVersionSpec( artifact, "[4.0,)" );
     }
 
-    static boolean isForkModeNever( String forkMode )
+    private boolean isJunitJupiter( Artifact artifact )
+    {
+        return dependencyResolver.isWithinVersionSpec( artifact, "[5.0.0,)" );
+    }
+
+    private boolean isJunitVintage( Artifact artifact )
+    {
+        return dependencyResolver.isWithinVersionSpec( artifact, "[4.12.0,5.0.0)" );
+    }
+
+    private static boolean isForkModeNever( String forkMode )
     {
         return FORK_NEVER.equals( forkMode );
     }
@@ -2873,6 +2884,39 @@ public abstract class AbstractSurefireMojo
 
     }
 
+    final class JUnitPlattformProviderInfo
+        implements ProviderInfo
+    {
+        private final Artifact junitArtifact;
+
+        JUnitPlattformProviderInfo( Artifact junitArtifact )
+        {
+            this.junitArtifact = junitArtifact;
+        }
+
+        @Nonnull public String getProviderName()
+        {
+            return "org.apache.maven.surefire.junitplatform.JUnitPlatformProvider";
+        }
+
+        public boolean isApplicable()
+        {
+            return isJunitJupiter( junitArtifact ) || isJunitVintage( junitArtifact );
+        }
+
+        public void addProviderProperties() throws MojoExecutionException
+        {
+        }
+
+        public Classpath getProviderClasspath()
+            throws ArtifactResolutionException, ArtifactNotFoundException
+        {
+            return dependencyResolver.getProviderClasspath( "surefire-junit-platform",
+                                                            surefireBooterArtifact.getBaseVersion(),
+                                                            null );
+        }
+    }
+
     final class JUnitCoreProviderInfo
         implements ProviderInfo
     {
diff --git a/pom.xml b/pom.xml
index 5fdefd4..96a75a4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -97,8 +97,9 @@
     <maven.site.path>surefire-archives/surefire-LATEST</maven.site.path>
     <!-- Override with Jigsaw JRE 9 -->
     <jdk.home>${java.home}/..</jdk.home>
-    <maven.compiler.testSource>1.6</maven.compiler.testSource>
-    <maven.compiler.testTarget>1.6</maven.compiler.testTarget>
+    <javaVersion>6</javaVersion>
+    <maven.compiler.testSource>1.${javaVersion}</maven.compiler.testSource>
+    <maven.compiler.testTarget>1.${javaVersion}</maven.compiler.testTarget>
     <jvm.args.tests>-server -XX:+UseG1GC -Xms128m -Xmx144m -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:SoftRefLRUPolicyMSPerMB=50 -Djava.awt.headless=true</jvm.args.tests>
   </properties>
 
@@ -419,6 +420,7 @@
                 <excludeDependencies>
                   <param>org.codehaus.plexus:plexus-java</param>
                   <param>org.ow2.asm:asm</param>
+                  <param>org.junit.platform:junit-platform-commons</param>
                 </excludeDependencies>
                 <ignores>
                   <param>org.objectweb.asm.*</param>
@@ -550,6 +552,7 @@
                   <excludes>
                     <exclude>org.codehaus.plexus:plexus-java</exclude>
                     <exclude>org.ow2.asm:asm</exclude>
+                    <exclude>org.junit.platform:junit-platform-commons</exclude>
                   </excludes>
                 </enforceBytecodeVersion>
               </rules>
diff --git a/surefire-its/pom.xml b/surefire-its/pom.xml
index 88a4870..d4c7df4 100644
--- a/surefire-its/pom.xml
+++ b/surefire-its/pom.xml
@@ -104,7 +104,9 @@
           <forkMode>once</forkMode>
           <argLine>-server -Xmx64m -XX:+UseG1GC -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Djava.awt.headless=true</argLine>
           <includes>
-            <include>org/apache/**/*IT*.java</include>
+            <include>org/apache/**/JUnit4VersionsIT.java</include>
+            <include>org/apache/**/JUnit5IT.java</include>
+            <include>org/apache/**/JUnitPlatformIT.java</include>
           </includes>
           <!-- Pass current surefire version to the main suite so that it -->
           <!-- can forward to all integration test projects. SUREFIRE-513 -->
@@ -143,25 +145,6 @@
         </executions>
       </plugin>
       <plugin>
-        <artifactId>maven-enforcer-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>require-maven-2.1.0</id>
-            <goals>
-              <goal>enforce</goal>
-            </goals>
-            <configuration>
-              <rules>
-                <requireMavenVersion>
-                  <!-- Some plugin features require a recent Maven runtime to work (e.g. SystemPropertiesTest) -->
-                  <version>[2.1.0,)</version>
-                </requireMavenVersion>
-              </rules>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
         <artifactId>maven-jar-plugin</artifactId>
         <!-- todo dont skip since of failsafe:2.19 internal use if having src/main/java/... -->
         <configuration>
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit4VersionsIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit4VersionsIT.java
index 8dd8f0c..c391964 100644
--- a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit4VersionsIT.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit4VersionsIT.java
@@ -19,7 +19,6 @@ package org.apache.maven.surefire.its;
  * under the License.
  */
 
-import java.util.Arrays;
 import java.util.Collection;
 
 import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
@@ -29,8 +28,28 @@ import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameter;
 
+import static java.util.Arrays.asList;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_10;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_11;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_12;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_8;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_8_1;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_8_2;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_9;
 import static org.junit.runners.Parameterized.*;
 
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_0;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_1;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_2;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_3;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_3_1;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_4;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_5;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_6;
+import static org.apache.maven.surefire.its.JUnitVersion.JUNIT_4_7;
+import static org.apache.maven.surefire.its.JUnitVersion.JUPITER_5_1_1;
+import static org.apache.maven.surefire.its.JUnitVersion.VINTAGE_5_1_1;
+
 /**
  * Basic suite test using all known versions of JUnit 4.x
  *
@@ -44,44 +63,41 @@ public class JUnit4VersionsIT
     @Parameters( name = "{index}: JUnit {0}" )
     public static Collection<Object[]> junitVersions()
     {
-        return Arrays.asList( new Object[][] {
-                { "4.0" },
-                { "4.1" },
-                { "4.2" },
-                { "4.3" },
-                { "4.3.1" },
-                { "4.4" },
-                { "4.5" },
-                { "4.6" },
-                { "4.7" },
-                { "4.8" },
-                { "4.8.1" },
-                { "4.8.2" },
-                { "4.9" },
-                { "4.10" },
-                { "4.11" },
-                { "4.12" }
+        return asList( new Object[][] {
+                { JUNIT_4_0 },
+                { JUNIT_4_1 },
+                { JUNIT_4_2 },
+                { JUNIT_4_3 },
+                { JUNIT_4_3_1 },
+                { JUNIT_4_4 },
+                { JUNIT_4_5 },
+                { JUNIT_4_6 },
+                { JUNIT_4_7 },
+                { JUNIT_4_8 },
+                { JUNIT_4_8_1 },
+                { JUNIT_4_8_2 },
+                { JUNIT_4_9 },
+                { JUNIT_4_10 },
+                { JUNIT_4_11 },
+                { JUNIT_4_12 },
+                { VINTAGE_5_1_1 },
+                { JUPITER_5_1_1 }
         } );
     }
 
     @Parameter
-    public String version;
-
-    private SurefireLauncher unpack()
-    {
-        return unpack( "/junit4", version );
-    }
+    public JUnitVersion version;
 
     @Test
     public void testJunit()
-        throws Exception
     {
-        runJUnitTest( version );
+        version.configure( unpack() )
+                .executeTest()
+                .verifyErrorFree( 1 );
     }
 
-    public void runJUnitTest( String version )
-        throws Exception
+    private SurefireLauncher unpack()
     {
-        unpack().setJUnitVersion( version ).executeTest().verifyErrorFree( 1 );
+        return unpack( "/junit4", version.toString() );
     }
 }
diff --git a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit5IT.java
similarity index 54%
copy from surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
copy to surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit5IT.java
index e9234f2..8c5eb3a 100644
--- a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit5IT.java
@@ -1,4 +1,4 @@
-package junit4;
+package org.apache.maven.surefire.its;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,47 +19,27 @@ package junit4;
  * under the License.
  */
 
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
+import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
 import org.junit.Test;
 
+import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaVersion;
 
-public class BasicTest
+/**
+ * Basic suite test currently only running against JUnit 5 M2
+ *
+ * @author <a href="mailto:britter@apache.org">Benedikt Ritter</a>
+ */
+public class JUnit5IT
+    extends SurefireJUnit4IntegrationTestCase
 {
 
-    private boolean setUpCalled = false;
-
-    private static boolean tearDownCalled = false;
-    
-    @Before
-    public void setUp()
-    {
-        setUpCalled = true;
-        tearDownCalled = false;
-        System.out.println( "Called setUp" );
-    }
-
-    @After
-    public void tearDown()
-    {
-        setUpCalled = false;
-        tearDownCalled = true;
-        System.out.println( "Called tearDown" );
-    }
-
     @Test
-    public void testSetUp()
+    public void test()
     {
-        Assert.assertTrue( "setUp was not called", setUpCalled );
-    }
-  
+        assumeJavaVersion( 1.8d );
 
-    @AfterClass
-    public static void oneTimeTearDown()
-    {
-        
+        unpack( "/junit5" )
+                .executeTest()
+                .verifyErrorFree( 2 );
     }
-
 }
diff --git a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformIT.java
similarity index 53%
copy from surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
copy to surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformIT.java
index e9234f2..f82206c 100644
--- a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformIT.java
@@ -1,4 +1,4 @@
-package junit4;
+package org.apache.maven.surefire.its;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,47 +19,29 @@ package junit4;
  * under the License.
  */
 
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
+import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
+import org.apache.maven.surefire.its.fixture.SurefireLauncher;
 import org.junit.Test;
 
+import static java.lang.System.getProperty;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assume.assumeThat;
 
-public class BasicTest
+public class JUnitPlatformIT
+    extends SurefireJUnit4IntegrationTestCase
 {
-
-    private boolean setUpCalled = false;
-
-    private static boolean tearDownCalled = false;
-    
-    @Before
-    public void setUp()
-    {
-        setUpCalled = true;
-        tearDownCalled = false;
-        System.out.println( "Called setUp" );
-    }
-
-    @After
-    public void tearDown()
+    private SurefireLauncher unpack()
     {
-        setUpCalled = false;
-        tearDownCalled = true;
-        System.out.println( "Called tearDown" );
+        return unpack( "/junit-platform" );
     }
 
     @Test
-    public void testSetUp()
+    public void test40()
     {
-        Assert.assertTrue( "setUp was not called", setUpCalled );
-    }
-  
+        assumeThat( "java.specification.version: ",
+                getProperty( "java.specification.version" ), is( greaterThanOrEqualTo( "1.8" ) ) );
 
-    @AfterClass
-    public static void oneTimeTearDown()
-    {
-        
+        unpack().executeTest().verifyErrorFree( 1 );
     }
-
 }
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitVersion.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitVersion.java
new file mode 100644
index 0000000..8e7f606
--- /dev/null
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitVersion.java
@@ -0,0 +1,80 @@
+package org.apache.maven.surefire.its;
+
+/*
+ * 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.its.fixture.SurefireLauncher;
+
+/**
+ * Enum listing all the JUnit version.
+ */
+public enum JUnitVersion {
+
+    JUNIT_4_0( "4.0" ),
+    JUNIT_4_1( "4.1" ),
+    JUNIT_4_2( "4.2" ),
+    JUNIT_4_3( "4.3" ),
+    JUNIT_4_3_1( "4.3.1" ),
+    JUNIT_4_4( "4.4" ),
+    JUNIT_4_5( "4.5" ),
+    JUNIT_4_6( "4.6" ),
+    JUNIT_4_7( "4.7" ),
+    JUNIT_4_8( "4.8" ),
+    JUNIT_4_8_1( "4.8.1" ),
+    JUNIT_4_8_2( "4.8.2" ),
+    JUNIT_4_9( "4.9" ),
+    JUNIT_4_10( "4.10" ),
+    JUNIT_4_11( "4.11" ),
+    JUNIT_4_12( "4.12" ),
+    VINTAGE_5_1_1( "5.1.1" )
+    {
+        @Override
+        public SurefireLauncher configure( SurefireLauncher launcher )
+        {
+            return super.configure( launcher )
+                    .activateProfile( "junit5-vintage" );
+        }
+    },
+    JUPITER_5_1_1( "5.1.1" )
+    {
+        @Override
+        public SurefireLauncher configure( SurefireLauncher launcher )
+        {
+            return super.configure( launcher )
+                    .activateProfile( "junit5-jupiter" );
+        }
+    };
+
+    private final String version;
+
+    JUnitVersion( String version )
+    {
+        this.version = version;
+    }
+
+    public SurefireLauncher configure( SurefireLauncher launcher )
+    {
+        return launcher.setJUnitVersion( version );
+    }
+
+    public String toString()
+    {
+        return version;
+    }
+}
diff --git a/surefire-its/src/test/resources/junit4/pom.xml b/surefire-its/src/test/resources/junit-platform/pom.xml
similarity index 55%
copy from surefire-its/src/test/resources/junit4/pom.xml
copy to surefire-its/src/test/resources/junit-platform/pom.xml
index 751a0e6..40a4f61 100644
--- a/surefire-its/src/test/resources/junit4/pom.xml
+++ b/surefire-its/src/test/resources/junit-platform/pom.xml
@@ -17,39 +17,38 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
+    <modelVersion>4.0.0</modelVersion>
 
-  <groupId>org.apache.maven.plugins.surefire</groupId>
-  <artifactId>junit4</artifactId>
-  <version>1.0-SNAPSHOT</version>
-  <name>Test for JUnit 4</name>
+    <groupId>org.apache.maven.plugins.surefire</groupId>
+    <artifactId>junit-platform</artifactId>
+    <version>1.0</version>
+    <name>Test for JUnit 5 Platform</name>
 
-  <properties>
-    <junitVersion>4.4</junitVersion>
-    <maven.compiler.source>1.6</maven.compiler.source>
-    <maven.compiler.target>1.6</maven.compiler.target>
-  </properties>
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+    </properties>
 
-  <dependencies>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>${junit.version}</version>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-  
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-surefire-plugin</artifactId>
-        <version>${surefire.version}</version>
-      </plugin>
-    </plugins>
-  </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>5.1.1</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${surefire.version}</version>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java b/surefire-its/src/test/resources/junit-platform/src/test/java/junitplatform/BasicTest.java
similarity index 73%
copy from surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
copy to surefire-its/src/test/resources/junit-platform/src/test/java/junitplatform/BasicTest.java
index e9234f2..2c9d119 100644
--- a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
+++ b/surefire-its/src/test/resources/junit-platform/src/test/java/junitplatform/BasicTest.java
@@ -1,4 +1,4 @@
-package junit4;
+package junitplatform;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,21 +19,21 @@ package junit4;
  * under the License.
  */
 
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 public class BasicTest
 {
 
-    private boolean setUpCalled = false;
+    private boolean setUpCalled;
 
-    private static boolean tearDownCalled = false;
-    
-    @Before
+    private static boolean tearDownCalled;
+
+    @BeforeEach
     public void setUp()
     {
         setUpCalled = true;
@@ -41,7 +41,7 @@ public class BasicTest
         System.out.println( "Called setUp" );
     }
 
-    @After
+    @AfterEach
     public void tearDown()
     {
         setUpCalled = false;
@@ -52,14 +52,14 @@ public class BasicTest
     @Test
     public void testSetUp()
     {
-        Assert.assertTrue( "setUp was not called", setUpCalled );
+        assertTrue( setUpCalled, "setUp was not called" );
     }
-  
 
-    @AfterClass
+
+    @AfterAll
     public static void oneTimeTearDown()
     {
-        
+        assertTrue( tearDownCalled );
     }
 
 }
diff --git a/surefire-its/src/test/resources/junit4/pom.xml b/surefire-its/src/test/resources/junit4/pom.xml
index 751a0e6..deddd56 100644
--- a/surefire-its/src/test/resources/junit4/pom.xml
+++ b/surefire-its/src/test/resources/junit4/pom.xml
@@ -20,36 +20,121 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
+    <modelVersion>4.0.0</modelVersion>
 
-  <groupId>org.apache.maven.plugins.surefire</groupId>
-  <artifactId>junit4</artifactId>
-  <version>1.0-SNAPSHOT</version>
-  <name>Test for JUnit 4</name>
+    <groupId>org.apache.maven.plugins.surefire</groupId>
+    <artifactId>junit4</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <name>Test for JUnit 4</name>
 
-  <properties>
-    <junitVersion>4.4</junitVersion>
-    <maven.compiler.source>1.6</maven.compiler.source>
-    <maven.compiler.target>1.6</maven.compiler.target>
-  </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>5.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
 
-  <dependencies>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>${junit.version}</version>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-  
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-surefire-plugin</artifactId>
-        <version>${surefire.version}</version>
-      </plugin>
-    </plugins>
-  </build>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>junit4</id>
+            <dependencies>
+                <dependency>
+                    <groupId>junit</groupId>
+                    <artifactId>junit</artifactId>
+                    <version>${junit.version}</version>
+                    <scope>test</scope>
+                </dependency>
+            </dependencies>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <version>${surefire.version}</version>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <profile>
+            <id>junit5-jupiter</id>
+            <properties>
+                <maven.compiler.source>1.8</maven.compiler.source>
+                <maven.compiler.target>1.8</maven.compiler.target>
+                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+                <junit.jupiter.version>5.1.1</junit.jupiter.version>
+                <junit.vintage.version>5.1.1</junit.vintage.version>
+                <junit.platform.version>1.1.1</junit.platform.version>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <version>${surefire.version}</version>
+                        <dependencies>
+                            <dependency>
+                                <groupId>org.junit.platform</groupId>
+                                <artifactId>junit-platform-surefire-provider</artifactId>
+                                <version>${junit.platform.version}</version>
+                            </dependency>
+                            <dependency>
+                                <groupId>org.junit.jupiter</groupId>
+                                <artifactId>junit-jupiter-engine</artifactId>
+                                <version>${junit.jupiter.version}</version>
+                            </dependency>
+                        </dependencies>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <profile>
+            <id>junit5-vintage</id>
+            <properties>
+                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+                <junit.platform.version>1.1.1</junit.platform.version>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <version>${surefire.version}</version>
+                        <dependencies>
+                            <dependency>
+                                <groupId>org.junit.platform</groupId>
+                                <artifactId>junit-platform-surefire-provider</artifactId>
+                                <version>${junit.platform.version}</version>
+                            </dependency>
+                            <dependency>
+                                <groupId>org.junit.vintage</groupId>
+                                <artifactId>junit-vintage-engine</artifactId>
+                                <version>${junit.version}</version>
+                            </dependency>
+                        </dependencies>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
 
 </project>
diff --git a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java b/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
index e9234f2..144df74 100644
--- a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
+++ b/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
@@ -25,14 +25,12 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
-
 public class BasicTest
 {
+    private static boolean tearDownCalled = false;
 
     private boolean setUpCalled = false;
 
-    private static boolean tearDownCalled = false;
-    
     @Before
     public void setUp()
     {
diff --git a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java b/surefire-its/src/test/resources/junit4/src/test/java/junit5/JUnit5Test.java
similarity index 66%
copy from surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
copy to surefire-its/src/test/resources/junit4/src/test/java/junit5/JUnit5Test.java
index e9234f2..0dd000c 100644
--- a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
+++ b/surefire-its/src/test/resources/junit4/src/test/java/junit5/JUnit5Test.java
@@ -1,4 +1,4 @@
-package junit4;
+package junit5;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,32 +19,32 @@ package junit4;
  * under the License.
  */
 
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class BasicTest
+/**
+ * A test using the JUnit 5 API, which should be executed by JUnit jupiter enigne
+ */
+public class JUnit5Test
 {
+    private static boolean tearDownCalled;
 
-    private boolean setUpCalled = false;
+    private boolean setUpCalled;
 
-    private static boolean tearDownCalled = false;
-    
-    @Before
+    @BeforeEach
     public void setUp()
     {
         setUpCalled = true;
-        tearDownCalled = false;
         System.out.println( "Called setUp" );
     }
 
-    @After
+    @AfterEach
     public void tearDown()
     {
-        setUpCalled = false;
         tearDownCalled = true;
         System.out.println( "Called tearDown" );
     }
@@ -52,14 +52,13 @@ public class BasicTest
     @Test
     public void testSetUp()
     {
-        Assert.assertTrue( "setUp was not called", setUpCalled );
+        assertTrue( setUpCalled, "setUp was not called" );
     }
-  
 
-    @AfterClass
+    @AfterAll
     public static void oneTimeTearDown()
     {
-        
+        assertTrue( tearDownCalled, "tearDown was not called" );
     }
 
 }
diff --git a/surefire-its/src/test/resources/junit5/pom.xml b/surefire-its/src/test/resources/junit5/pom.xml
new file mode 100644
index 0000000..b44c2cc
--- /dev/null
+++ b/surefire-its/src/test/resources/junit5/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.maven.plugins.surefire</groupId>
+    <artifactId>junit5</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <name>Test for JUnit 5</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <java.version>1.8</java.version>
+        <junit.jupiter.version>5.1.1</junit.jupiter.version>
+        <junit.vintage.version>5.1.1</junit.vintage.version>
+        <junit.platform.version>1.1.1</junit.platform.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit.jupiter.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.vintage</groupId>
+            <artifactId>junit-vintage-engine</artifactId>
+            <version>${junit.vintage.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${surefire.version}</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.junit.platform</groupId>
+                        <artifactId>junit-platform-surefire-provider</artifactId>
+                        <version>${junit.platform.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java b/surefire-its/src/test/resources/junit5/src/test/java/junit5/JUnit4Test.java
similarity index 83%
copy from surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
copy to surefire-its/src/test/resources/junit5/src/test/java/junit5/JUnit4Test.java
index e9234f2..0a94b1a 100644
--- a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
+++ b/surefire-its/src/test/resources/junit5/src/test/java/junit5/JUnit4Test.java
@@ -1,4 +1,4 @@
-package junit4;
+package junit5;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -25,26 +25,26 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
-
-public class BasicTest
+/**
+ * A test using the JUnit 4 API, which should be executed by JUnit vintage enigne
+ */
+public class JUnit4Test
 {
 
-    private boolean setUpCalled = false;
+    private boolean setUpCalled;
+
+    private static boolean tearDownCalled;
 
-    private static boolean tearDownCalled = false;
-    
     @Before
     public void setUp()
     {
         setUpCalled = true;
-        tearDownCalled = false;
         System.out.println( "Called setUp" );
     }
 
     @After
     public void tearDown()
     {
-        setUpCalled = false;
         tearDownCalled = true;
         System.out.println( "Called tearDown" );
     }
@@ -54,12 +54,11 @@ public class BasicTest
     {
         Assert.assertTrue( "setUp was not called", setUpCalled );
     }
-  
 
     @AfterClass
     public static void oneTimeTearDown()
     {
-        
+        Assert.assertTrue( "tearDown was not called", tearDownCalled );
     }
 
 }
diff --git a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java b/surefire-its/src/test/resources/junit5/src/test/java/junit5/JUnit5Test.java
similarity index 66%
copy from surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
copy to surefire-its/src/test/resources/junit5/src/test/java/junit5/JUnit5Test.java
index e9234f2..558546d 100644
--- a/surefire-its/src/test/resources/junit4/src/test/java/junit4/BasicTest.java
+++ b/surefire-its/src/test/resources/junit5/src/test/java/junit5/JUnit5Test.java
@@ -1,4 +1,4 @@
-package junit4;
+package junit5;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,32 +19,33 @@ package junit4;
  * under the License.
  */
 
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 
-public class BasicTest
+/**
+ * A test using the JUnit 5 API, which should be executed by JUnit jupiter enigne
+ */
+public class JUnit5Test
 {
 
-    private boolean setUpCalled = false;
+    private boolean setUpCalled;
+
+    private static boolean tearDownCalled;
 
-    private static boolean tearDownCalled = false;
-    
-    @Before
+    @BeforeEach
     public void setUp()
     {
         setUpCalled = true;
-        tearDownCalled = false;
         System.out.println( "Called setUp" );
     }
 
-    @After
+    @AfterEach
     public void tearDown()
     {
-        setUpCalled = false;
         tearDownCalled = true;
         System.out.println( "Called tearDown" );
     }
@@ -52,14 +53,13 @@ public class BasicTest
     @Test
     public void testSetUp()
     {
-        Assert.assertTrue( "setUp was not called", setUpCalled );
+        Assertions.assertTrue( setUpCalled, "setUp was not called" );
     }
-  
 
-    @AfterClass
+    @AfterAll
     public static void oneTimeTearDown()
     {
-        
+        Assertions.assertTrue( tearDownCalled, "tearDown was not called" );
     }
 
 }
diff --git a/surefire-providers/pom.xml b/surefire-providers/pom.xml
index 0322bcc..47b0f40 100644
--- a/surefire-providers/pom.xml
+++ b/surefire-providers/pom.xml
@@ -41,6 +41,7 @@
     <module>surefire-junit3</module>
     <module>surefire-junit4</module>
     <module>surefire-junit47</module>
+    <module>surefire-junit-platform</module>
     <module>surefire-testng-utils</module>
     <module>surefire-testng</module>
   </modules>
diff --git a/surefire-providers/surefire-junit-platform/pom.xml b/surefire-providers/surefire-junit-platform/pom.xml
new file mode 100644
index 0000000..07fa180
--- /dev/null
+++ b/surefire-providers/surefire-junit-platform/pom.xml
@@ -0,0 +1,167 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.maven.surefire</groupId>
+        <artifactId>surefire-providers</artifactId>
+        <version>3.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>surefire-junit-platform</artifactId>
+    <name>SureFire JUnit Platform Runner</name>
+    <description>SureFire JUnit Platform Runner</description>
+    <properties>
+        <javaVersion>8</javaVersion>
+    </properties>
+    <contributors>
+        <contributor>
+            <name>Konstantin Lutovich</name>
+            <roles>
+                <role>Contributed to the original provider implementation</role>
+            </roles>
+        </contributor>
+        <contributor>
+            <name>Shintaro Katafuchi</name>
+            <roles>
+                <role>Contributed to the original provider implementation</role>
+            </roles>
+        </contributor>
+        <contributor>
+            <name>Sam Brannen</name>
+            <roles>
+                <role>Contributed to the original provider implementation</role>
+            </roles>
+        </contributor>
+        <contributor>
+            <name>Stefan Bechtold</name>
+            <roles>
+                <role>Contributed to the original provider implementation</role>
+            </roles>
+        </contributor>
+        <contributor>
+            <name>Marc Philipp</name>
+            <roles>
+                <role>Contributed to the original provider implementation</role>
+            </roles>
+        </contributor>
+        <contributor>
+            <name>Matthias Merdes</name>
+            <roles>
+                <role>Contributed to the original provider implementation</role>
+            </roles>
+        </contributor>
+        <contributor>
+            <name>Johannes Link</name>
+            <roles>
+                <role>Contributed to the original provider implementation</role>
+            </roles>
+        </contributor>
+    </contributors>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.maven.surefire</groupId>
+            <artifactId>common-java5</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>5.1.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>5.1.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>3.6.0</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>animal-sniffer-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>signature-check</id>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                        <configuration>
+                            <signature combine.self="override">
+                                <groupId>org.codehaus.mojo.signature</groupId>
+                                <artifactId>java18</artifactId>
+                                <version>1.0</version>
+                            </signature>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <jvm>${java.home}/bin/java</jvm>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <artifactSet>
+                                <includes>
+                                    <include>org.apache.maven.surefire:common-java5</include>
+                                </includes>
+                            </artifactSet>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
new file mode 100644
index 0000000..7f58921
--- /dev/null
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
@@ -0,0 +1,202 @@
+package org.apache.maven.surefire.junitplatform;
+
+/*
+ * 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.providerapi.AbstractProvider;
+import org.apache.maven.surefire.providerapi.ProviderParameters;
+import org.apache.maven.surefire.report.ReporterException;
+import org.apache.maven.surefire.report.ReporterFactory;
+import org.apache.maven.surefire.report.RunListener;
+import org.apache.maven.surefire.report.SimpleReportEntry;
+import org.apache.maven.surefire.suite.RunResult;
+import org.apache.maven.surefire.testset.TestSetFailedException;
+import org.apache.maven.surefire.util.TestsToRun;
+import org.junit.platform.engine.Filter;
+import org.junit.platform.launcher.Launcher;
+import org.junit.platform.launcher.LauncherDiscoveryRequest;
+import org.junit.platform.launcher.TagFilter;
+import org.junit.platform.launcher.core.LauncherFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
+import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
+
+/**
+ * @since 1.0
+ */
+public class JUnitPlatformProvider
+    extends AbstractProvider
+{
+
+    // Parameter names processed to determine which @Tags should be executed.
+    static final String EXCLUDE_GROUPS = "excludedGroups";
+
+    static final String EXCLUDE_TAGS = "excludeTags";
+
+    static final String INCLUDE_GROUPS = "groups";
+
+    static final String INCLUDE_TAGS = "includeTags";
+
+    static final String EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED = "The " + INCLUDE_GROUPS + " and " + INCLUDE_TAGS
+            + " parameters (or the " + EXCLUDE_GROUPS + " and " + EXCLUDE_TAGS + " parameters) are synonyms - "
+            + "only one of each is allowed (though neither is required).";
+
+    private final ProviderParameters parameters;
+
+    private final Launcher launcher;
+
+    final Filter<?>[] includeAndExcludeFilters;
+
+    public JUnitPlatformProvider( ProviderParameters parameters )
+    {
+        this( parameters, LauncherFactory.create() );
+    }
+
+    JUnitPlatformProvider( ProviderParameters parameters, Launcher launcher )
+    {
+        this.parameters = parameters;
+        this.launcher = launcher;
+        this.includeAndExcludeFilters = getIncludeAndExcludeFilters();
+        Logger.getLogger( "org.junit" ).setLevel( Level.WARNING );
+    }
+
+    @Override
+    public Iterable<Class<?>> getSuites()
+    {
+        return scanClasspath();
+    }
+
+    @Override
+    public RunResult invoke( Object forkTestSet )
+            throws TestSetFailedException, ReporterException, InvocationTargetException
+    {
+        if ( forkTestSet instanceof TestsToRun )
+        {
+            return invokeAllTests( (TestsToRun) forkTestSet );
+        }
+        else if ( forkTestSet instanceof Class )
+        {
+            return invokeAllTests( TestsToRun.fromClass( (Class<?>) forkTestSet ) );
+        }
+        else if ( forkTestSet == null )
+        {
+            return invokeAllTests( scanClasspath() );
+        }
+        else
+        {
+            throw new IllegalArgumentException( "Unexpected value of forkTestSet: " + forkTestSet );
+        }
+    }
+
+    private TestsToRun scanClasspath()
+    {
+        TestsToRun scannedClasses = parameters.getScanResult().applyFilter(
+            new TestPlanScannerFilter( launcher, includeAndExcludeFilters ), parameters.getTestClassLoader() );
+        return parameters.getRunOrderCalculator().orderTestClasses( scannedClasses );
+    }
+
+    private RunResult invokeAllTests( TestsToRun testsToRun )
+    {
+        RunResult runResult;
+        ReporterFactory reporterFactory = parameters.getReporterFactory();
+        try
+        {
+            RunListener runListener = reporterFactory.createReporter();
+            launcher.registerTestExecutionListeners( new RunListenerAdapter( runListener ) );
+
+            for ( Class<?> testClass : testsToRun )
+            {
+                invokeSingleClass( testClass, runListener );
+            }
+        }
+        finally
+        {
+            runResult = reporterFactory.close();
+        }
+        return runResult;
+    }
+
+    private void invokeSingleClass( Class<?> testClass, RunListener runListener )
+    {
+        SimpleReportEntry classEntry = new SimpleReportEntry( getClass().getName(), testClass.getName() );
+        runListener.testSetStarting( classEntry );
+
+        LauncherDiscoveryRequest discoveryRequest = request().selectors( selectClass( testClass ) ).filters(
+            includeAndExcludeFilters ).build();
+        launcher.execute( discoveryRequest );
+
+        runListener.testSetCompleted( classEntry );
+    }
+
+    private Filter<?>[] getIncludeAndExcludeFilters()
+    {
+        List<Filter<?>> filters = new ArrayList<>();
+
+        Optional<List<String>> includes = getGroupsOrTags( getPropertiesList( INCLUDE_GROUPS ),
+            getPropertiesList( INCLUDE_TAGS ) );
+        includes.map( TagFilter::includeTags ).ifPresent( filters::add );
+
+        Optional<List<String>> excludes = getGroupsOrTags( getPropertiesList( EXCLUDE_GROUPS ),
+            getPropertiesList( EXCLUDE_TAGS ) );
+        excludes.map( TagFilter::excludeTags ).ifPresent( filters::add );
+
+        return filters.toArray( new Filter<?>[filters.size()] );
+    }
+
+    private Optional<List<String>> getPropertiesList( String key )
+    {
+        List<String> compoundProperties = null;
+        String property = parameters.getProviderProperties().get( key );
+        if ( property != null )
+        {
+            compoundProperties = Arrays.asList( property.split( "[, ]+" ) );
+        }
+        return Optional.ofNullable( compoundProperties );
+    }
+
+    private Optional<List<String>> getGroupsOrTags( Optional<List<String>> groups, Optional<List<String>> tags )
+    {
+        Optional<List<String>> elements = Optional.empty();
+
+        if ( groups.isPresent() && tags.isPresent() )
+        {
+            throw new IllegalStateException( EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED );
+        }
+
+        if ( groups.isPresent() )
+        {
+            elements = groups;
+        }
+        else if ( tags.isPresent() )
+        {
+            elements = tags;
+        }
+
+        return elements;
+    }
+
+}
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
new file mode 100644
index 0000000..9191864
--- /dev/null
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java
@@ -0,0 +1,150 @@
+package org.apache.maven.surefire.junitplatform;
+
+/*
+ * 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 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 java.util.Optional;
+
+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.junit.platform.engine.TestExecutionResult;
+import org.junit.platform.engine.TestSource;
+import org.junit.platform.engine.support.descriptor.ClassSource;
+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;
+
+/**
+ * @since 1.0
+ */
+final class RunListenerAdapter
+    implements TestExecutionListener
+{
+
+    private final RunListener runListener;
+
+    private Optional<TestPlan> testPlan = Optional.empty();
+
+    public RunListenerAdapter( RunListener runListener )
+    {
+        this.runListener = runListener;
+    }
+
+    @Override
+    public void testPlanExecutionStarted( TestPlan testPlan )
+    {
+        this.testPlan = Optional.of( testPlan );
+    }
+
+    @Override
+    public void testPlanExecutionFinished( TestPlan testPlan )
+    {
+        this.testPlan = Optional.empty();
+    }
+
+    @Override
+    public void executionStarted( TestIdentifier testIdentifier )
+    {
+        if ( testIdentifier.isTest() )
+        {
+            runListener.testStarting( createReportEntry( testIdentifier, Optional.empty() ) );
+        }
+    }
+
+    @Override
+    public void executionSkipped( TestIdentifier testIdentifier, String reason )
+    {
+        String source = getClassName( testIdentifier ).orElseGet( () -> parentDisplayName( testIdentifier ) );
+        runListener.testSkipped( ignored( source, testIdentifier.getDisplayName(), reason ) );
+    }
+
+    @Override
+    public void executionFinished( TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
+    {
+        if ( testExecutionResult.getStatus() == ABORTED )
+        {
+            runListener.testAssumptionFailure( createReportEntry( testIdentifier,
+                    testExecutionResult.getThrowable() ) );
+        }
+        else if ( testExecutionResult.getStatus() == FAILED )
+        {
+            runListener.testFailed( createReportEntry( testIdentifier, testExecutionResult.getThrowable() ) );
+        }
+        else if ( testIdentifier.isTest() )
+        {
+            runListener.testSucceeded( createReportEntry( testIdentifier, Optional.empty() ) );
+        }
+    }
+
+    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier, Optional<Throwable> throwable )
+    {
+        Optional<String> className = getClassName( testIdentifier );
+        if ( className.isPresent() )
+        {
+            StackTraceWriter traceWriter = new PojoStackTraceWriter( className.get(),
+                getMethodName( testIdentifier ).orElse( "" ), throwable.orElse( null ) );
+            return new SimpleReportEntry( className.get(), testIdentifier.getDisplayName(), traceWriter,
+                    (Integer) null );
+        }
+        else
+        {
+            return new SimpleReportEntry( parentDisplayName( testIdentifier ), testIdentifier.getDisplayName(),
+                    (Integer) null );
+        }
+    }
+
+    private Optional<String> getClassName( TestIdentifier testIdentifier )
+    {
+        TestSource testSource = testIdentifier.getSource().orElse( null );
+        if ( testSource instanceof ClassSource )
+        {
+            return Optional.of( ( (ClassSource) testSource ).getJavaClass().getName() );
+        }
+        if ( testSource instanceof MethodSource )
+        {
+            return Optional.of( ( (MethodSource) testSource ).getClassName() );
+        }
+        return Optional.empty();
+    }
+
+    private Optional<String> getMethodName( TestIdentifier testIdentifier )
+    {
+        TestSource testSource = testIdentifier.getSource().orElse( null );
+        if ( testSource instanceof MethodSource )
+        {
+            return Optional.of( ( (MethodSource) testSource ).getMethodName() );
+        }
+        return Optional.empty();
+    }
+
+    private String parentDisplayName( TestIdentifier testIdentifier )
+    {
+        return testPlan
+            .flatMap( plan -> plan.getParent( testIdentifier ) )
+            .map( TestIdentifier::getDisplayName )
+            .orElseGet( testIdentifier::getUniqueId );
+    }
+}
diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java
new file mode 100644
index 0000000..4a95d6e
--- /dev/null
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java
@@ -0,0 +1,64 @@
+package org.apache.maven.surefire.junitplatform;
+
+/*
+ * 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 static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
+import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
+
+import java.util.function.Predicate;
+
+import org.apache.maven.surefire.util.ScannerFilter;
+import org.junit.platform.engine.Filter;
+import org.junit.platform.launcher.Launcher;
+import org.junit.platform.launcher.LauncherDiscoveryRequest;
+import org.junit.platform.launcher.TestIdentifier;
+import org.junit.platform.launcher.TestPlan;
+
+/**
+ * @since 1.0
+ */
+final class TestPlanScannerFilter
+    implements ScannerFilter
+{
+
+    private static final Predicate<TestIdentifier> HAS_TESTS = testIdentifier -> testIdentifier.isTest()
+            || testIdentifier.isContainer();
+
+    private final Launcher launcher;
+
+    private final Filter<?>[] includeAndExcludeFilters;
+
+    public TestPlanScannerFilter( Launcher launcher, Filter<?>[] includeAndExcludeFilters )
+    {
+        this.launcher = launcher;
+        this.includeAndExcludeFilters = includeAndExcludeFilters;
+    }
+
+    @Override
+    @SuppressWarnings( "rawtypes" )
+    public boolean accept( Class testClass )
+    {
+        LauncherDiscoveryRequest discoveryRequest = request().selectors( selectClass( testClass ) ).filters(
+            includeAndExcludeFilters ).build();
+        TestPlan testPlan = launcher.discover( discoveryRequest );
+        return testPlan.countTestIdentifiers( HAS_TESTS ) > 0;
+    }
+
+}
diff --git a/surefire-providers/surefire-junit-platform/src/main/resources/META-INF/services/org.apache.maven.surefire.providerapi.SurefireProvider b/surefire-providers/surefire-junit-platform/src/main/resources/META-INF/services/org.apache.maven.surefire.providerapi.SurefireProvider
new file mode 100644
index 0000000..dbe73cf
--- /dev/null
+++ b/surefire-providers/surefire-junit-platform/src/main/resources/META-INF/services/org.apache.maven.surefire.providerapi.SurefireProvider
@@ -0,0 +1 @@
+org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java
new file mode 100644
index 0000000..a32aade
--- /dev/null
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java
@@ -0,0 +1,325 @@
+package org.apache.maven.surefire.junitplatform;
+
+/*
+ * 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 static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.surefire.providerapi.ProviderParameters;
+import org.apache.maven.surefire.report.ReporterFactory;
+import org.apache.maven.surefire.report.RunListener;
+import org.apache.maven.surefire.util.RunOrderCalculator;
+import org.apache.maven.surefire.util.ScanResult;
+import org.apache.maven.surefire.util.TestsToRun;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.commons.util.PreconditionViolationException;
+import org.junit.platform.launcher.Launcher;
+import org.junit.platform.launcher.TestPlan;
+import org.junit.platform.launcher.core.LauncherFactory;
+import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
+import org.junit.platform.launcher.listeners.TestExecutionSummary;
+
+/**
+ * Unit tests for {@link JUnitPlatformProvider}.
+ *
+ * @since 1.0
+ */
+class JUnitPlatformProviderTests
+{
+
+    @Test
+    void getSuitesReturnsScannedClasses()
+        throws Exception
+    {
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class, TestClass2.class );
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+
+        assertThat( provider.getSuites() ).containsOnly( TestClass1.class, TestClass2.class );
+    }
+
+    @Test
+    void invokeThrowsForWrongForkTestSet()
+        throws Exception
+    {
+        ProviderParameters providerParameters = providerParametersMock( Integer.class );
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+
+        assertThrows( IllegalArgumentException.class, () -> provider.invoke( "wrong forkTestSet" ) );
+    }
+
+    @Test
+    void allGivenTestsToRunAreInvoked()
+        throws Exception
+    {
+        Launcher launcher = LauncherFactory.create();
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParametersMock(), launcher );
+
+        TestPlanSummaryListener executionListener = new TestPlanSummaryListener();
+        launcher.registerTestExecutionListeners( executionListener );
+
+        TestsToRun testsToRun = newTestsToRun( TestClass1.class, TestClass2.class );
+        provider.invoke( testsToRun );
+
+        assertThat( executionListener.summaries ).hasSize( 2 );
+        TestClass1.verifyExecutionSummary( executionListener.summaries.get( 0 ) );
+        TestClass2.verifyExecutionSummary( executionListener.summaries.get( 1 ) );
+    }
+
+    @Test
+    void singleTestClassIsInvoked()
+        throws Exception
+    {
+        Launcher launcher = LauncherFactory.create();
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParametersMock(), launcher );
+
+        TestPlanSummaryListener executionListener = new TestPlanSummaryListener();
+        launcher.registerTestExecutionListeners( executionListener );
+
+        provider.invoke( TestClass1.class );
+
+        assertThat( executionListener.summaries ).hasSize( 1 );
+        TestClass1.verifyExecutionSummary( executionListener.summaries.get( 0 ) );
+    }
+
+    @Test
+    void allDiscoveredTestsAreInvokedForNullArgument()
+        throws Exception
+    {
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class, TestClass2.class );
+        Launcher launcher = LauncherFactory.create();
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters, launcher );
+
+        TestPlanSummaryListener executionListener = new TestPlanSummaryListener();
+        launcher.registerTestExecutionListeners( executionListener );
+
+        provider.invoke( null );
+
+        assertThat( executionListener.summaries ).hasSize( 2 );
+        TestClass1.verifyExecutionSummary( executionListener.summaries.get( 0 ) );
+        TestClass2.verifyExecutionSummary( executionListener.summaries.get( 1 ) );
+    }
+
+    @Test
+    void bothGroupsAndIncludeTagsThrowsException()
+    {
+        Map<String, String> properties = new HashMap<>();
+        properties.put( JUnitPlatformProvider.INCLUDE_GROUPS, "groupOne, groupTwo" );
+        properties.put( JUnitPlatformProvider.INCLUDE_TAGS, "tagOne, tagTwo" );
+        verifyPreconditionViolationException( properties );
+    }
+
+    @Test
+    void bothExcludedGroupsAndExcludeTagsThrowsException() {
+        Map<String, String> properties = new HashMap<>();
+        properties.put(JUnitPlatformProvider.EXCLUDE_GROUPS, "groupOne, groupTwo");
+        properties.put(JUnitPlatformProvider.EXCLUDE_TAGS, "tagOne, tagTwo");
+        verifyPreconditionViolationException(properties);
+    }
+
+    @Test
+    void onlyGroupsIsDeclared()
+        throws Exception
+    {
+        Map<String, String> properties = new HashMap<>();
+        properties.put( JUnitPlatformProvider.INCLUDE_GROUPS, "groupOne, groupTwo" );
+
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+        when( providerParameters.getProviderProperties() ).thenReturn( properties );
+
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+
+        assertEquals( 1, provider.includeAndExcludeFilters.length );
+    }
+
+    @Test
+    void onlyExcludeTagsIsDeclared()
+        throws Exception
+    {
+        Map<String, String> properties = new HashMap<>();
+        properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "tagOne, tagTwo" );
+
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+        when( providerParameters.getProviderProperties() ).thenReturn( properties );
+
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+
+        assertEquals( 1, provider.includeAndExcludeFilters.length );
+    }
+
+    @Test
+    void bothIncludeAndExcludeAreAllowed()
+        throws Exception
+    {
+        Map<String, String> properties = new HashMap<>();
+        properties.put( JUnitPlatformProvider.INCLUDE_TAGS, "tagOne, tagTwo" );
+        properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "tagThree, tagFour" );
+
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+        when( providerParameters.getProviderProperties() ).thenReturn( properties );
+
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+
+        assertEquals( 2, provider.includeAndExcludeFilters.length );
+    }
+
+    @Test
+    void noFiltersAreCreatedIfNoPropertiesAreDeclared()
+        throws Exception
+    {
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+
+        assertEquals( 0, provider.includeAndExcludeFilters.length );
+    }
+
+    private void verifyPreconditionViolationException( Map<String, String> properties )
+    {
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+        when( providerParameters.getProviderProperties() ).thenReturn( properties );
+
+        Throwable throwable = assertThrows( PreconditionViolationException.class, () ->
+                new JUnitPlatformProvider(providerParameters) );
+
+        assertEquals( JUnitPlatformProvider.EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED, throwable.getMessage() );
+    }
+
+    private static ProviderParameters providerParametersMock( Class<?>... testClasses )
+    {
+        TestsToRun testsToRun = newTestsToRun( testClasses );
+
+        ScanResult scanResult = mock( ScanResult.class );
+        when( scanResult.applyFilter( any(), any() ) ).thenReturn( testsToRun );
+
+        RunOrderCalculator runOrderCalculator = mock( RunOrderCalculator.class );
+        when( runOrderCalculator.orderTestClasses( any() ) ).thenReturn( testsToRun );
+
+        ReporterFactory reporterFactory = mock( ReporterFactory.class );
+        RunListener runListener = mock( RunListener.class );
+        when( reporterFactory.createReporter() ).thenReturn( runListener );
+
+        ProviderParameters providerParameters = mock( ProviderParameters.class );
+        when( providerParameters.getScanResult() ).thenReturn( scanResult );
+        when( providerParameters.getRunOrderCalculator() ).thenReturn( runOrderCalculator );
+        when( providerParameters.getReporterFactory() ).thenReturn( reporterFactory );
+
+        return providerParameters;
+    }
+
+    private static TestsToRun newTestsToRun( Class<?>... testClasses )
+    {
+        List<Class<?>> classesList = Arrays.asList( testClasses );
+        return new TestsToRun( new LinkedHashSet<>( classesList ) );
+    }
+
+    private class TestPlanSummaryListener
+        extends SummaryGeneratingListener
+    {
+
+        final List<TestExecutionSummary> summaries = new ArrayList<>();
+
+        @Override
+        public void testPlanExecutionFinished( TestPlan testPlan )
+        {
+            super.testPlanExecutionFinished( testPlan );
+            summaries.add( getSummary() );
+        }
+    }
+
+    private static class TestClass1
+    {
+
+        @Test
+        void test1()
+        {
+        }
+
+        @Test
+        void test2()
+        {
+        }
+
+        @Disabled
+        @Test
+        void test3()
+        {
+        }
+
+        @Test
+        void test4()
+        {
+            throw new RuntimeException();
+        }
+
+        static void verifyExecutionSummary( TestExecutionSummary summary )
+        {
+            assertEquals( 4, summary.getTestsFoundCount() );
+            assertEquals( 3, summary.getTestsStartedCount() );
+            assertEquals( 2, summary.getTestsSucceededCount() );
+            assertEquals( 1, summary.getTestsSkippedCount() );
+            assertEquals( 0, summary.getTestsAbortedCount() );
+            assertEquals( 1, summary.getTestsFailedCount() );
+        }
+    }
+
+    private static class TestClass2
+    {
+
+        @Test
+        void test1()
+        {
+        }
+
+        @Test
+        void test2()
+        {
+            throw new RuntimeException();
+        }
+
+        @Test
+        void test3()
+        {
+            assumeTrue( false );
+        }
+
+        static void verifyExecutionSummary( TestExecutionSummary summary )
+        {
+            assertEquals( 3, summary.getTestsFoundCount() );
+            assertEquals( 3, summary.getTestsStartedCount() );
+            assertEquals( 1, summary.getTestsSucceededCount() );
+            assertEquals( 0, summary.getTestsSkippedCount() );
+            assertEquals( 1, summary.getTestsAbortedCount() );
+            assertEquals( 1, summary.getTestsFailedCount() );
+        }
+    }
+}
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java
new file mode 100644
index 0000000..0a37eb5
--- /dev/null
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java
@@ -0,0 +1,252 @@
+package org.apache.maven.surefire.junitplatform;
+
+/*
+ * 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 static java.util.Collections.singletonList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Optional;
+
+import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.RunListener;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.engine.descriptor.ClassTestDescriptor;
+import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;
+import org.junit.platform.engine.TestDescriptor;
+import org.junit.platform.engine.TestExecutionResult;
+import org.junit.platform.engine.UniqueId;
+import org.junit.platform.engine.support.descriptor.EngineDescriptor;
+import org.junit.platform.launcher.TestIdentifier;
+import org.junit.platform.launcher.TestPlan;
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Unit tests for {@link RunListenerAdapter}.
+ *
+ * @since 1.0
+ */
+class RunListenerAdapterTests
+{
+    private RunListener listener;
+    private RunListenerAdapter adapter;
+
+    @BeforeEach
+    void setUp()
+    {
+        listener = mock( RunListener.class );
+        adapter = new RunListenerAdapter( listener );
+    }
+
+    @Test
+    void notifiedWithCorrectNamesWhenMethodExecutionStarted()
+        throws Exception
+    {
+        ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+
+        adapter.executionStarted( newMethodIdentifier() );
+        verify( listener ).testStarting( entryCaptor.capture() );
+
+        ReportEntry entry = entryCaptor.getValue();
+        assertEquals( MY_TEST_METHOD_NAME + "()", entry.getName() );
+        assertEquals( MyTestClass.class.getName(), entry.getSourceName() );
+        assertNotNull( entry.getStackTraceWriter() );
+    }
+
+    @Test
+    void notNotifiedWhenClassExecutionStarted()
+    {
+        adapter.executionStarted( newClassIdentifier() );
+        verify( listener, never() ).testStarting( any() );
+    }
+
+    @Test
+    void notNotifiedWhenEngineExecutionStarted()
+    {
+        adapter.executionStarted( newEngineIdentifier() );
+        verify( listener, never() ).testStarting( any() );
+    }
+
+    @Test
+    void notifiedWhenMethodExecutionSkipped()
+        throws Exception
+    {
+        adapter.executionSkipped( newMethodIdentifier(), "test" );
+        verify( listener ).testSkipped( any() );
+    }
+
+    @Test
+    void notifiedWithCorrectNamesWhenClassExecutionSkipped()
+    {
+        ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+
+        adapter.executionSkipped( newClassIdentifier(), "test" );
+        verify( listener ).testSkipped( entryCaptor.capture() );
+
+        ReportEntry entry = entryCaptor.getValue();
+        assertTrue( MyTestClass.class.getTypeName().contains( entry.getName() ) );
+        assertEquals( MyTestClass.class.getName(), entry.getSourceName() );
+    }
+
+    @Test
+    void notifiedWhenEngineExecutionSkipped()
+    {
+        adapter.executionSkipped( newEngineIdentifier(), "test" );
+        verify( listener ).testSkipped( any() );
+    }
+
+    @Test
+    void notifiedWhenMethodExecutionAborted()
+        throws Exception
+    {
+        adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.aborted( null ) );
+        verify( listener ).testAssumptionFailure( any() );
+    }
+
+    @Test
+    void notifiedWhenClassExecutionAborted()
+    {
+        adapter.executionFinished( newClassIdentifier(), TestExecutionResult.aborted( null ) );
+        verify( listener ).testAssumptionFailure( any() );
+    }
+
+    @Test
+    void notifiedWhenMethodExecutionFailed()
+        throws Exception
+    {
+        adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.failed( new RuntimeException() ) );
+        verify( listener ).testFailed( any() );
+    }
+
+    @Test
+    void notifiedWithCorrectNamesWhenClassExecutionFailed()
+    {
+        ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+
+        adapter.executionFinished( newClassIdentifier(), TestExecutionResult.failed( new RuntimeException() ) );
+        verify( listener ).testFailed( entryCaptor.capture() );
+
+        ReportEntry entry = entryCaptor.getValue();
+        assertEquals( MyTestClass.class.getName(), entry.getSourceName() );
+        assertNotNull( entry.getStackTraceWriter() );
+    }
+
+    @Test
+    void notifiedWhenMethodExecutionSucceeded()
+        throws Exception
+    {
+        adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.successful() );
+        verify( listener ).testSucceeded( any() );
+    }
+
+    @Test
+    void notNotifiedWhenClassExecutionSucceeded()
+    {
+        adapter.executionFinished( newClassIdentifier(), TestExecutionResult.successful() );
+        verify( listener, never() ).testSucceeded( any() );
+    }
+
+    @Test
+    void notifiedWithParentDisplayNameWhenTestClassUnknown()
+    {
+        // Set up a test plan
+        TestPlan plan = TestPlan.from( singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) );
+        adapter.testPlanExecutionStarted( plan );
+
+        // Use the test plan to set up child with parent.
+        final String parentDisplay = "I am your father";
+        TestIdentifier child = newSourcelessIdentifierWithParent( plan, parentDisplay );
+        adapter.executionStarted( child );
+
+        // Check that the adapter has informed Surefire that the test has been invoked,
+        // with the parent name as source (since the test case itself had no source).
+        ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+        verify( listener ).testStarting( entryCaptor.capture() );
+        assertEquals( parentDisplay, entryCaptor.getValue().getSourceName() );
+    }
+
+    private static TestIdentifier newMethodIdentifier()
+        throws Exception
+    {
+        TestDescriptor testDescriptor = new TestMethodTestDescriptor( newId(), MyTestClass.class,
+            MyTestClass.class.getDeclaredMethod( MY_TEST_METHOD_NAME ) );
+        return TestIdentifier.from( testDescriptor );
+    }
+
+    private static TestIdentifier newClassIdentifier()
+    {
+        TestDescriptor testDescriptor = new ClassTestDescriptor( newId(), MyTestClass.class );
+        return TestIdentifier.from( testDescriptor );
+    }
+
+    private static TestIdentifier newSourcelessIdentifierWithParent( TestPlan testPlan, String parentDisplay )
+    {
+        // A parent test identifier with a name.
+        TestDescriptor parent = mock( TestDescriptor.class );
+        when( parent.getUniqueId() ).thenReturn( newId() );
+        when( parent.getDisplayName() ).thenReturn( parentDisplay );
+        TestIdentifier parentId = TestIdentifier.from( parent );
+
+        // The (child) test case that is to be executed as part of a test plan.
+        TestDescriptor child = mock( TestDescriptor.class );
+        when( child.getUniqueId() ).thenReturn( newId() );
+        when( child.isTest() ).thenReturn( true );
+
+        // Ensure the child source is null yet that there is a parent -- the special case to be tested.
+        when( child.getSource() ).thenReturn( Optional.empty() );
+        when( child.getParent() ).thenReturn( Optional.of(parent) );
+        TestIdentifier childId = TestIdentifier.from( child );
+
+        testPlan.add( childId );
+        testPlan.add( parentId );
+
+        return childId;
+    }
+
+    private static TestIdentifier newEngineIdentifier()
+    {
+        TestDescriptor testDescriptor = new EngineDescriptor( newId(), "engine" );
+        return TestIdentifier.from( testDescriptor );
+    }
+
+    private static UniqueId newId()
+    {
+        return UniqueId.forEngine( "engine" );
+    }
+
+    private static final String MY_TEST_METHOD_NAME = "myTestMethod";
+
+    private static class MyTestClass {
+
+        @Test
+        void myTestMethod()
+        {
+        }
+
+    }
+
+}
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java
new file mode 100644
index 0000000..98f5b2b
--- /dev/null
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java
@@ -0,0 +1,190 @@
+package org.apache.maven.surefire.junitplatform;
+
+/*
+ * 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 static java.util.Collections.emptyList;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.DynamicTest;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestFactory;
+import org.junit.platform.engine.Filter;
+import org.junit.platform.launcher.core.LauncherFactory;
+
+/**
+ * Unit tests for {@link TestPlanScannerFilter}.
+ *
+ * @since 1.0
+ */
+public class TestPlanScannerFilterTests
+{
+
+    @Test
+    void emptyClassAccepted()
+    {
+        assertTrue( newFilter().accept( EmptyClass.class ), "accepts empty class because it is a container" );
+    }
+
+    @Test
+    void classWithNoTestMethodsIsAccepted()
+    {
+        assertTrue( newFilter().accept( ClassWithMethods.class ),
+            "accepts class with no @Test methods because it is a container" );
+    }
+
+    @Test
+    void classWithTestMethodsIsAccepted()
+    {
+        assertTrue( newFilter().accept( ClassWithTestMethods.class ) );
+    }
+
+    @Test
+    void classWithNestedTestClassIsAccepted()
+    {
+        assertTrue( newFilter().accept( ClassWithNestedTestClass.class ) );
+    }
+
+    @Test
+    void classWithDeeplyNestedTestClassIsAccepted()
+    {
+        assertTrue( newFilter().accept( ClassWithDeeplyNestedTestClass.class ) );
+    }
+
+    @Test
+    void classWithTestFactoryIsAccepted()
+    {
+        assertTrue( newFilter().accept( ClassWithTestFactory.class ) );
+    }
+
+    @Test
+    void classWithNestedTestFactoryIsAccepted()
+    {
+        assertTrue( newFilter().accept( ClassWithNestedTestFactory.class ) );
+    }
+
+    private TestPlanScannerFilter newFilter()
+    {
+        return new TestPlanScannerFilter( LauncherFactory.create(), new Filter<?>[0] );
+    }
+
+    private static class EmptyClass
+    {
+    }
+
+    @SuppressWarnings("unused")
+    private static class ClassWithMethods
+    {
+
+        void method1()
+        {
+        }
+
+        void method2()
+        {
+        }
+    }
+
+    private static class ClassWithTestMethods
+    {
+
+        @Test
+        void test1()
+        {
+        }
+
+        @Test
+        public void test2()
+        {
+        }
+    }
+
+    private static class ClassWithNestedTestClass
+    {
+
+        @SuppressWarnings("unused")
+        void method()
+        {
+        }
+
+        @Nested
+        class TestClass
+        {
+
+            @Test
+            void test1()
+            {
+            }
+        }
+    }
+
+    private static class ClassWithDeeplyNestedTestClass
+    {
+
+        @Nested
+        class Level1
+        {
+
+            @Nested
+            class Level2
+            {
+
+                @Nested
+                class TestClass
+                {
+
+                    @Test
+                    void test1()
+                    {
+                    }
+                }
+            }
+        }
+    }
+
+    private static class ClassWithTestFactory
+    {
+
+        @TestFactory
+        Stream<DynamicTest> tests()
+        {
+            return Stream.empty();
+        }
+    }
+
+    private static class ClassWithNestedTestFactory
+    {
+
+        @Nested
+        class TestClass
+        {
+
+            @TestFactory
+            List<DynamicTest> tests()
+            {
+                return emptyList();
+            }
+        }
+    }
+
+}

-- 
To stop receiving notification emails like this one, please contact
tibordigana@apache.org.

[maven-surefire] 03/04: Bump JUnit versions and remove unnecessary dependency

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

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

commit 82182d5bc47af16e6072126d8f0ea8141954f1c0
Author: Christian Stein <so...@gmail.com>
AuthorDate: Wed May 2 23:30:04 2018 +0200

    Bump JUnit versions and remove unnecessary dependency
    
    - JUnit Platform 1.2.0
    - JUnit Jupiter 5.2.0
    
    Depending on junit-jupiter-engine pulls in junit-jupiter-api automatically.
---
 surefire-providers/surefire-junit-platform/pom.xml | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/surefire-providers/surefire-junit-platform/pom.xml b/surefire-providers/surefire-junit-platform/pom.xml
index 07fa180..bf4869b 100644
--- a/surefire-providers/surefire-junit-platform/pom.xml
+++ b/surefire-providers/surefire-junit-platform/pom.xml
@@ -88,18 +88,12 @@
         <dependency>
             <groupId>org.junit.platform</groupId>
             <artifactId>junit-platform-launcher</artifactId>
-            <version>1.1.1</version>
-        </dependency>
-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter-api</artifactId>
-            <version>5.1.1</version>
-            <scope>test</scope>
+            <version>1.2.0</version>
         </dependency>
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-engine</artifactId>
-            <version>5.1.1</version>
+            <version>5.2.0</version>
             <scope>test</scope>
         </dependency>
         <dependency>

-- 
To stop receiving notification emails like this one, please contact
tibordigana@apache.org.

[maven-surefire] 04/04: Copy current sources from junit-platform-surefire-provider

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

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

commit 41e80e58e97cb846b2cd9ce2ae5d018070570a02
Author: Christian Stein <so...@gmail.com>
AuthorDate: Thu May 3 11:04:45 2018 +0200

    Copy current sources from junit-platform-surefire-provider
    
    This commit includes enhancements and bug fixes that were applied since
    the initial code donation.
    
    https://issues.apache.org/jira/browse/SUREFIRE-1330
---
 .../junitplatform/JUnitPlatformProvider.java       | 137 ++++--
 .../surefire/junitplatform/RunListenerAdapter.java | 188 ++++++--
 .../surefire/junitplatform/TestMethodFilter.java   |  60 +++
 .../junitplatform/TestPlanScannerFilter.java       |  18 +-
 .../junitplatform/JUnitPlatformProviderTests.java  | 537 +++++++++++++++++++--
 .../junitplatform/RunListenerAdapterTests.java     | 400 +++++++++++++--
 .../junitplatform/TestMethodFilterTests.java       | 105 ++++
 .../junitplatform/TestPlanScannerFilterTests.java  |  30 +-
 8 files changed, 1277 insertions(+), 198 deletions(-)

diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
index 7f58921..510266e 100644
--- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
@@ -19,35 +19,45 @@ package org.apache.maven.surefire.junitplatform;
  * under the License.
  */
 
+import static java.util.Collections.emptyMap;
+import static java.util.stream.Collectors.toList;
+import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
+import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.UncheckedIOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
 import org.apache.maven.surefire.providerapi.AbstractProvider;
 import org.apache.maven.surefire.providerapi.ProviderParameters;
+import org.apache.maven.surefire.report.ConsoleOutputCapture;
+import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReporterException;
 import org.apache.maven.surefire.report.ReporterFactory;
 import org.apache.maven.surefire.report.RunListener;
-import org.apache.maven.surefire.report.SimpleReportEntry;
 import org.apache.maven.surefire.suite.RunResult;
+import org.apache.maven.surefire.testset.TestListResolver;
 import org.apache.maven.surefire.testset.TestSetFailedException;
+import org.apache.maven.surefire.util.ScanResult;
 import org.apache.maven.surefire.util.TestsToRun;
+import org.junit.platform.commons.util.Preconditions;
+import org.junit.platform.commons.util.StringUtils;
 import org.junit.platform.engine.Filter;
 import org.junit.platform.launcher.Launcher;
 import org.junit.platform.launcher.LauncherDiscoveryRequest;
 import org.junit.platform.launcher.TagFilter;
+import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
 import org.junit.platform.launcher.core.LauncherFactory;
 
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Optional;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
-import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
-
-/**
- * @since 1.0
- */
 public class JUnitPlatformProvider
     extends AbstractProvider
 {
@@ -61,6 +71,8 @@ public class JUnitPlatformProvider
 
     static final String INCLUDE_TAGS = "includeTags";
 
+    static final String CONFIGURATION_PARAMETERS = "configurationParameters";
+
     static final String EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED = "The " + INCLUDE_GROUPS + " and " + INCLUDE_TAGS
             + " parameters (or the " + EXCLUDE_GROUPS + " and " + EXCLUDE_TAGS + " parameters) are synonyms - "
             + "only one of each is allowed (though neither is required).";
@@ -69,9 +81,11 @@ public class JUnitPlatformProvider
 
     private final Launcher launcher;
 
-    final Filter<?>[] includeAndExcludeFilters;
+    final Filter<?>[] filters;
 
-    public JUnitPlatformProvider( ProviderParameters parameters )
+    final Map<String, String> configurationParameters;
+
+    JUnitPlatformProvider( ProviderParameters parameters )
     {
         this( parameters, LauncherFactory.create() );
     }
@@ -80,7 +94,8 @@ public class JUnitPlatformProvider
     {
         this.parameters = parameters;
         this.launcher = launcher;
-        this.includeAndExcludeFilters = getIncludeAndExcludeFilters();
+        this.filters = getFilters();
+        this.configurationParameters = getConfigurationParameters();
         Logger.getLogger( "org.junit" ).setLevel( Level.WARNING );
     }
 
@@ -92,7 +107,7 @@ public class JUnitPlatformProvider
 
     @Override
     public RunResult invoke( Object forkTestSet )
-            throws TestSetFailedException, ReporterException, InvocationTargetException
+                    throws TestSetFailedException, ReporterException
     {
         if ( forkTestSet instanceof TestsToRun )
         {
@@ -114,8 +129,9 @@ public class JUnitPlatformProvider
 
     private TestsToRun scanClasspath()
     {
-        TestsToRun scannedClasses = parameters.getScanResult().applyFilter(
-            new TestPlanScannerFilter( launcher, includeAndExcludeFilters ), parameters.getTestClassLoader() );
+        TestPlanScannerFilter filter = new TestPlanScannerFilter( launcher, filters );
+        ScanResult scanResult = parameters.getScanResult();
+        TestsToRun scannedClasses = scanResult.applyFilter( filter, parameters.getTestClassLoader() );
         return parameters.getRunOrderCalculator().orderTestClasses( scannedClasses );
     }
 
@@ -126,12 +142,9 @@ public class JUnitPlatformProvider
         try
         {
             RunListener runListener = reporterFactory.createReporter();
-            launcher.registerTestExecutionListeners( new RunListenerAdapter( runListener ) );
-
-            for ( Class<?> testClass : testsToRun )
-            {
-                invokeSingleClass( testClass, runListener );
-            }
+            ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) runListener );
+            LauncherDiscoveryRequest discoveryRequest = buildLauncherDiscoveryRequest( testsToRun );
+            launcher.execute( discoveryRequest, new RunListenerAdapter( runListener ) );
         }
         finally
         {
@@ -140,40 +153,70 @@ public class JUnitPlatformProvider
         return runResult;
     }
 
-    private void invokeSingleClass( Class<?> testClass, RunListener runListener )
+    private LauncherDiscoveryRequest buildLauncherDiscoveryRequest( TestsToRun testsToRun )
     {
-        SimpleReportEntry classEntry = new SimpleReportEntry( getClass().getName(), testClass.getName() );
-        runListener.testSetStarting( classEntry );
-
-        LauncherDiscoveryRequest discoveryRequest = request().selectors( selectClass( testClass ) ).filters(
-            includeAndExcludeFilters ).build();
-        launcher.execute( discoveryRequest );
-
-        runListener.testSetCompleted( classEntry );
+        LauncherDiscoveryRequestBuilder builder =
+                        request().filters( filters ).configurationParameters( configurationParameters );
+        for ( Class<?> testClass : testsToRun )
+        {
+            builder.selectors( selectClass( testClass ) );
+        }
+        return builder.build();
     }
 
-    private Filter<?>[] getIncludeAndExcludeFilters()
+    private Filter<?>[] getFilters()
     {
         List<Filter<?>> filters = new ArrayList<>();
 
-        Optional<List<String>> includes = getGroupsOrTags( getPropertiesList( INCLUDE_GROUPS ),
-            getPropertiesList( INCLUDE_TAGS ) );
+        Optional<List<String>> includes =
+                        getGroupsOrTags( getPropertiesList( INCLUDE_GROUPS ), getPropertiesList( INCLUDE_TAGS ) );
         includes.map( TagFilter::includeTags ).ifPresent( filters::add );
 
-        Optional<List<String>> excludes = getGroupsOrTags( getPropertiesList( EXCLUDE_GROUPS ),
-            getPropertiesList( EXCLUDE_TAGS ) );
+        Optional<List<String>> excludes =
+                        getGroupsOrTags( getPropertiesList( EXCLUDE_GROUPS ), getPropertiesList( EXCLUDE_TAGS ) );
         excludes.map( TagFilter::excludeTags ).ifPresent( filters::add );
 
-        return filters.toArray( new Filter<?>[filters.size()] );
+        TestListResolver testListResolver = parameters.getTestRequest().getTestListResolver();
+        if ( !testListResolver.isEmpty() )
+        {
+            filters.add( new TestMethodFilter( testListResolver ) );
+        }
+
+        return filters.toArray( new Filter<?>[0] );
+    }
+
+    private Map<String, String> getConfigurationParameters()
+    {
+        String content = parameters.getProviderProperties().get( CONFIGURATION_PARAMETERS );
+        if ( content == null )
+        {
+            return emptyMap();
+        }
+        try ( StringReader reader = new StringReader( content ) )
+        {
+            Map<String, String> result = new HashMap<>();
+            Properties props = new Properties();
+            props.load( reader );
+            props.stringPropertyNames().forEach( key -> result.put( key, props.getProperty( key ) ) );
+            return result;
+        }
+        catch ( IOException ex )
+        {
+            throw new UncheckedIOException( "Error reading " + CONFIGURATION_PARAMETERS, ex );
+        }
     }
 
     private Optional<List<String>> getPropertiesList( String key )
     {
         List<String> compoundProperties = null;
         String property = parameters.getProviderProperties().get( key );
-        if ( property != null )
+        if ( StringUtils.isNotBlank( property ) )
         {
-            compoundProperties = Arrays.asList( property.split( "[, ]+" ) );
+            compoundProperties =
+                            Arrays.stream( property.split( "[,]+" ) )
+                                  .filter( StringUtils::isNotBlank )
+                                  .map( String::trim )
+                                  .collect( toList() );
         }
         return Optional.ofNullable( compoundProperties );
     }
@@ -182,10 +225,7 @@ public class JUnitPlatformProvider
     {
         Optional<List<String>> elements = Optional.empty();
 
-        if ( groups.isPresent() && tags.isPresent() )
-        {
-            throw new IllegalStateException( EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED );
-        }
+        Preconditions.condition(!groups.isPresent() || !tags.isPresent(), EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED );
 
         if ( groups.isPresent() )
         {
@@ -198,5 +238,4 @@ public class JUnitPlatformProvider
 
         return elements;
     }
-
 }
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 9191864..71a70c3 100644
--- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java
@@ -24,6 +24,8 @@ import static org.junit.platform.engine.TestExecutionResult.Status.ABORTED;
 import static org.junit.platform.engine.TestExecutionResult.Status.FAILED;
 
 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;
@@ -36,6 +38,7 @@ 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 1.0
@@ -46,9 +49,11 @@ final class RunListenerAdapter
 
     private final RunListener runListener;
 
-    private Optional<TestPlan> testPlan = Optional.empty();
+    private TestPlan testPlan;
 
-    public RunListenerAdapter( RunListener runListener )
+    private Set<TestIdentifier> testSetNodes = ConcurrentHashMap.newKeySet();
+
+    RunListenerAdapter( RunListener runListener )
     {
         this.runListener = runListener;
     }
@@ -56,78 +61,203 @@ final class RunListenerAdapter
     @Override
     public void testPlanExecutionStarted( TestPlan testPlan )
     {
-        this.testPlan = Optional.of( testPlan );
+        updateTestPlan( testPlan );
     }
 
     @Override
     public void testPlanExecutionFinished( TestPlan testPlan )
     {
-        this.testPlan = Optional.empty();
+        updateTestPlan( null );
     }
 
     @Override
     public void executionStarted( TestIdentifier testIdentifier )
     {
+        if ( testIdentifier.isContainer()
+                        && testIdentifier.getSource().filter( ClassSource.class::isInstance ).isPresent() )
+        {
+            startTestSetIfPossible( testIdentifier );
+        }
         if ( testIdentifier.isTest() )
         {
-            runListener.testStarting( createReportEntry( testIdentifier, Optional.empty() ) );
+            ensureTestSetStarted( testIdentifier );
+            runListener.testStarting( createReportEntry( testIdentifier ) );
         }
     }
 
     @Override
     public void executionSkipped( TestIdentifier testIdentifier, String reason )
     {
-        String source = getClassName( testIdentifier ).orElseGet( () -> parentDisplayName( testIdentifier ) );
-        runListener.testSkipped( ignored( source, testIdentifier.getDisplayName(), reason ) );
+        ensureTestSetStarted( testIdentifier );
+        String source = getLegacyReportingClassName( testIdentifier );
+        runListener.testSkipped( ignored( source, getLegacyReportingName( testIdentifier ), reason ) );
+        completeTestSetIfNecessary( testIdentifier );
     }
 
     @Override
-    public void executionFinished( TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
+    public void executionFinished(
+                    TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
     {
         if ( testExecutionResult.getStatus() == ABORTED )
         {
-            runListener.testAssumptionFailure( createReportEntry( testIdentifier,
-                    testExecutionResult.getThrowable() ) );
+            runListener.testAssumptionFailure( createReportEntry( testIdentifier, testExecutionResult ) );
         }
         else if ( testExecutionResult.getStatus() == FAILED )
         {
-            runListener.testFailed( createReportEntry( testIdentifier, testExecutionResult.getThrowable() ) );
+            reportFailedTest( testIdentifier, testExecutionResult );
         }
         else if ( testIdentifier.isTest() )
         {
-            runListener.testSucceeded( createReportEntry( testIdentifier, Optional.empty() ) );
+            runListener.testSucceeded( createReportEntry( testIdentifier ) );
         }
+        completeTestSetIfNecessary( testIdentifier );
     }
 
-    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier, Optional<Throwable> throwable )
+    private void updateTestPlan( TestPlan testPlan )
     {
-        Optional<String> className = getClassName( testIdentifier );
-        if ( className.isPresent() )
+        this.testPlan = testPlan;
+        testSetNodes.clear();
+    }
+
+    private void ensureTestSetStarted( TestIdentifier testIdentifier )
+    {
+        if ( isTestSetStarted( testIdentifier ) )
+        {
+            return;
+        }
+        if ( testIdentifier.isTest() )
         {
-            StackTraceWriter traceWriter = new PojoStackTraceWriter( className.get(),
-                getMethodName( testIdentifier ).orElse( "" ), throwable.orElse( null ) );
-            return new SimpleReportEntry( className.get(), testIdentifier.getDisplayName(), traceWriter,
-                    (Integer) null );
+            startTestSet( testPlan.getParent( testIdentifier ).orElse( testIdentifier ) );
         }
         else
         {
-            return new SimpleReportEntry( parentDisplayName( testIdentifier ), testIdentifier.getDisplayName(),
-                    (Integer) null );
+            startTestSet( testIdentifier );
+        }
+    }
+
+    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 Optional<String> getClassName( TestIdentifier 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 )
+    {
+        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 );
+        }
+    }
+
+    private SimpleReportEntry createTestSetReportEntry( TestIdentifier testIdentifier )
+    {
+        return new SimpleReportEntry(
+                        JUnitPlatformProvider.class.getName(), testIdentifier.getLegacyReportingName() );
+    }
+
+    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier )
+    {
+        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 )
+    {
+        // 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( ')', '}' );
+    }
+
+    private String getLegacyReportingClassName( TestIdentifier testIdentifier )
+    {
+        return LegacyReportingUtils.getClassName( testPlan, testIdentifier );
+    }
+
+    private StackTraceWriter getStackTraceWriter(
+                    TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
+    {
+        Optional<Throwable> throwable = testExecutionResult.getThrowable();
+        if ( testExecutionResult.getStatus() == FAILED )
+        {
+            // Failed tests must have a StackTraceWriter, otherwise Surefire will fail
+            return getStackTraceWriter( testIdentifier, throwable.orElse( null ) );
+        }
+        return throwable.map( t -> getStackTraceWriter( testIdentifier, t ) ).orElse( null );
+    }
+
+    private StackTraceWriter getStackTraceWriter( TestIdentifier testIdentifier, Throwable throwable )
+    {
+        String className = getClassName( testIdentifier );
+        String methodName = getMethodName( testIdentifier ).orElse( "" );
+        return new PojoStackTraceWriter( className, methodName, throwable );
+    }
+
+    private String getClassName( TestIdentifier testIdentifier )
     {
         TestSource testSource = testIdentifier.getSource().orElse( null );
         if ( testSource instanceof ClassSource )
         {
-            return Optional.of( ( (ClassSource) testSource ).getJavaClass().getName() );
+            return ( (ClassSource) testSource ).getJavaClass().getName();
         }
         if ( testSource instanceof MethodSource )
         {
-            return Optional.of( ( (MethodSource) testSource ).getClassName() );
+            return ( (MethodSource) testSource ).getClassName();
         }
-        return Optional.empty();
+        return testPlan.getParent( testIdentifier ).map( this::getClassName ).orElse( "" );
     }
 
     private Optional<String> getMethodName( TestIdentifier testIdentifier )
@@ -139,12 +269,4 @@ final class RunListenerAdapter
         }
         return Optional.empty();
     }
-
-    private String parentDisplayName( TestIdentifier testIdentifier )
-    {
-        return testPlan
-            .flatMap( plan -> plan.getParent( testIdentifier ) )
-            .map( TestIdentifier::getDisplayName )
-            .orElseGet( testIdentifier::getUniqueId );
-    }
 }
diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestMethodFilter.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestMethodFilter.java
new file mode 100644
index 0000000..45e32db
--- /dev/null
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestMethodFilter.java
@@ -0,0 +1,60 @@
+package org.apache.maven.surefire.junitplatform;
+
+/*
+ * 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.testset.TestListResolver;
+import org.junit.platform.engine.FilterResult;
+import org.junit.platform.engine.TestDescriptor;
+import org.junit.platform.engine.support.descriptor.MethodSource;
+import org.junit.platform.launcher.PostDiscoveryFilter;
+
+/**
+ * @since 1.0.3
+ */
+class TestMethodFilter
+    implements PostDiscoveryFilter
+{
+
+    private final TestListResolver testListResolver;
+
+    TestMethodFilter( TestListResolver testListResolver )
+    {
+        this.testListResolver = testListResolver;
+    }
+
+    @Override
+    public FilterResult apply( TestDescriptor descriptor )
+    {
+        boolean shouldRun = descriptor.getSource()
+                                      .filter( MethodSource.class::isInstance )
+                                      .map( MethodSource.class::cast )
+                                      .map( this::shouldRun )
+                                      .orElse( true );
+
+        return FilterResult.includedIf( shouldRun );
+    }
+
+    private boolean shouldRun( MethodSource source )
+    {
+        String testClass = TestListResolver.toClassFileName( source.getClassName() );
+        String testMethod = source.getMethodName();
+        return this.testListResolver.shouldRun( testClass, testMethod );
+    }
+}
diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java
index 4a95d6e..4b488c1 100644
--- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java
@@ -22,13 +22,10 @@ package org.apache.maven.surefire.junitplatform;
 import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
 import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
 
-import java.util.function.Predicate;
-
 import org.apache.maven.surefire.util.ScannerFilter;
 import org.junit.platform.engine.Filter;
 import org.junit.platform.launcher.Launcher;
 import org.junit.platform.launcher.LauncherDiscoveryRequest;
-import org.junit.platform.launcher.TestIdentifier;
 import org.junit.platform.launcher.TestPlan;
 
 /**
@@ -38,14 +35,11 @@ final class TestPlanScannerFilter
     implements ScannerFilter
 {
 
-    private static final Predicate<TestIdentifier> HAS_TESTS = testIdentifier -> testIdentifier.isTest()
-            || testIdentifier.isContainer();
-
     private final Launcher launcher;
 
     private final Filter<?>[] includeAndExcludeFilters;
 
-    public TestPlanScannerFilter( Launcher launcher, Filter<?>[] includeAndExcludeFilters )
+    TestPlanScannerFilter( Launcher launcher, Filter<?>[] includeAndExcludeFilters )
     {
         this.launcher = launcher;
         this.includeAndExcludeFilters = includeAndExcludeFilters;
@@ -55,10 +49,12 @@ final class TestPlanScannerFilter
     @SuppressWarnings( "rawtypes" )
     public boolean accept( Class testClass )
     {
-        LauncherDiscoveryRequest discoveryRequest = request().selectors( selectClass( testClass ) ).filters(
-            includeAndExcludeFilters ).build();
+        LauncherDiscoveryRequest discoveryRequest = request()
+                        .selectors( selectClass( testClass ) )
+                        .filters( includeAndExcludeFilters ).build();
+
         TestPlan testPlan = launcher.discover( discoveryRequest );
-        return testPlan.countTestIdentifiers( HAS_TESTS ) > 0;
-    }
 
+        return testPlan.containsTests();
+    }
 }
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java
index a32aade..e0db97e 100644
--- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java
@@ -19,24 +19,43 @@ package org.apache.maven.surefire.junitplatform;
  * under the License.
  */
 
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.singletonMap;
+import static java.util.stream.Collectors.toSet;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
+import static org.mockito.AdditionalMatchers.gt;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
 
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.maven.surefire.providerapi.ProviderParameters;
+import org.apache.maven.surefire.report.ConsoleOutputReceiver;
+import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.ReporterFactory;
 import org.apache.maven.surefire.report.RunListener;
+import org.apache.maven.surefire.report.SimpleReportEntry;
+import org.apache.maven.surefire.testset.TestListResolver;
+import org.apache.maven.surefire.testset.TestRequest;
+import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.apache.maven.surefire.util.RunOrderCalculator;
 import org.apache.maven.surefire.util.ScanResult;
 import org.apache.maven.surefire.util.TestsToRun;
@@ -44,10 +63,14 @@ import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.platform.commons.util.PreconditionViolationException;
 import org.junit.platform.launcher.Launcher;
+import org.junit.platform.launcher.TestIdentifier;
 import org.junit.platform.launcher.TestPlan;
 import org.junit.platform.launcher.core.LauncherFactory;
 import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
 import org.junit.platform.launcher.listeners.TestExecutionSummary;
+import org.junit.platform.launcher.listeners.TestExecutionSummary.Failure;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 
 /**
  * Unit tests for {@link JUnitPlatformProvider}.
@@ -59,9 +82,10 @@ class JUnitPlatformProviderTests
 
     @Test
     void getSuitesReturnsScannedClasses()
-        throws Exception
+                    throws Exception
     {
-        ProviderParameters providerParameters = providerParametersMock( TestClass1.class, TestClass2.class );
+        ProviderParameters providerParameters =
+                        providerParametersMock( TestClass1.class, TestClass2.class );
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
 
         assertThat( provider.getSuites() ).containsOnly( TestClass1.class, TestClass2.class );
@@ -69,17 +93,18 @@ class JUnitPlatformProviderTests
 
     @Test
     void invokeThrowsForWrongForkTestSet()
-        throws Exception
+                    throws Exception
     {
         ProviderParameters providerParameters = providerParametersMock( Integer.class );
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
 
-        assertThrows( IllegalArgumentException.class, () -> provider.invoke( "wrong forkTestSet" ) );
+        assertThrows(
+                        IllegalArgumentException.class, () -> invokeProvider( provider, "wrong forkTestSet" ) );
     }
 
     @Test
     void allGivenTestsToRunAreInvoked()
-        throws Exception
+                    throws Exception
     {
         Launcher launcher = LauncherFactory.create();
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParametersMock(), launcher );
@@ -88,16 +113,25 @@ class JUnitPlatformProviderTests
         launcher.registerTestExecutionListeners( executionListener );
 
         TestsToRun testsToRun = newTestsToRun( TestClass1.class, TestClass2.class );
-        provider.invoke( testsToRun );
+        invokeProvider( provider, testsToRun );
 
-        assertThat( executionListener.summaries ).hasSize( 2 );
-        TestClass1.verifyExecutionSummary( executionListener.summaries.get( 0 ) );
-        TestClass2.verifyExecutionSummary( executionListener.summaries.get( 1 ) );
+        assertThat( executionListener.summaries ).hasSize( 1 );
+        TestExecutionSummary summary = executionListener.summaries.get( 0 );
+        assertEquals( TestClass1.TESTS_FOUND + TestClass2.TESTS_FOUND, summary.getTestsFoundCount() );
+        assertEquals(
+                        TestClass1.TESTS_STARTED + TestClass2.TESTS_STARTED, summary.getTestsStartedCount() );
+        assertEquals(
+                        TestClass1.TESTS_SKIPPED + TestClass2.TESTS_SKIPPED, summary.getTestsSkippedCount() );
+        assertEquals(
+                        TestClass1.TESTS_SUCCEEDED + TestClass2.TESTS_SUCCEEDED, summary.getTestsSucceededCount() );
+        assertEquals(
+                        TestClass1.TESTS_ABORTED + TestClass2.TESTS_ABORTED, summary.getTestsAbortedCount() );
+        assertEquals( TestClass1.TESTS_FAILED + TestClass2.TESTS_FAILED, summary.getTestsFailedCount() );
     }
 
     @Test
     void singleTestClassIsInvoked()
-        throws Exception
+                    throws Exception
     {
         Launcher launcher = LauncherFactory.create();
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParametersMock(), launcher );
@@ -105,28 +139,94 @@ class JUnitPlatformProviderTests
         TestPlanSummaryListener executionListener = new TestPlanSummaryListener();
         launcher.registerTestExecutionListeners( executionListener );
 
-        provider.invoke( TestClass1.class );
+        invokeProvider( provider, TestClass1.class );
 
         assertThat( executionListener.summaries ).hasSize( 1 );
-        TestClass1.verifyExecutionSummary( executionListener.summaries.get( 0 ) );
+        TestExecutionSummary summary = executionListener.summaries.get( 0 );
+        assertEquals( TestClass1.TESTS_FOUND, summary.getTestsFoundCount() );
+        assertEquals( TestClass1.TESTS_STARTED, summary.getTestsStartedCount() );
+        assertEquals( TestClass1.TESTS_SKIPPED, summary.getTestsSkippedCount() );
+        assertEquals( TestClass1.TESTS_SUCCEEDED, summary.getTestsSucceededCount() );
+        assertEquals( TestClass1.TESTS_ABORTED, summary.getTestsAbortedCount() );
+        assertEquals( TestClass1.TESTS_FAILED, summary.getTestsFailedCount() );
     }
 
     @Test
     void allDiscoveredTestsAreInvokedForNullArgument()
-        throws Exception
+                    throws Exception
     {
-        ProviderParameters providerParameters = providerParametersMock( TestClass1.class, TestClass2.class );
+        RunListener runListener = runListenerMock();
+        ProviderParameters providerParameters =
+                        providerParametersMock( runListener, TestClass1.class, TestClass2.class );
         Launcher launcher = LauncherFactory.create();
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters, launcher );
 
         TestPlanSummaryListener executionListener = new TestPlanSummaryListener();
         launcher.registerTestExecutionListeners( executionListener );
 
-        provider.invoke( null );
+        invokeProvider( provider, null );
+
+        InOrder inOrder = inOrder( runListener );
+        inOrder
+                        .verify( runListener )
+                        .testSetStarting(
+                                        new SimpleReportEntry(
+                                                        JUnitPlatformProvider.class.getName(),
+                                                        TestClass1.class.getName() ) );
+        inOrder
+                        .verify( runListener )
+                        .testSetCompleted(
+                                        new SimpleReportEntry(
+                                                        JUnitPlatformProvider.class.getName(),
+                                                        TestClass1.class.getName() ) );
+        inOrder
+                        .verify( runListener )
+                        .testSetStarting(
+                                        new SimpleReportEntry(
+                                                        JUnitPlatformProvider.class.getName(),
+                                                        TestClass2.class.getName() ) );
+        inOrder
+                        .verify( runListener )
+                        .testSetCompleted(
+                                        new SimpleReportEntry(
+                                                        JUnitPlatformProvider.class.getName(),
+                                                        TestClass2.class.getName() ) );
+
+        assertThat( executionListener.summaries ).hasSize( 1 );
+        TestExecutionSummary summary = executionListener.summaries.get( 0 );
+        assertEquals( TestClass1.TESTS_FOUND + TestClass2.TESTS_FOUND, summary.getTestsFoundCount() );
+        assertEquals(
+                        TestClass1.TESTS_STARTED + TestClass2.TESTS_STARTED, summary.getTestsStartedCount() );
+        assertEquals(
+                        TestClass1.TESTS_SKIPPED + TestClass2.TESTS_SKIPPED, summary.getTestsSkippedCount() );
+        assertEquals(
+                        TestClass1.TESTS_SUCCEEDED + TestClass2.TESTS_SUCCEEDED, summary.getTestsSucceededCount() );
+        assertEquals(
+                        TestClass1.TESTS_ABORTED + TestClass2.TESTS_ABORTED, summary.getTestsAbortedCount() );
+        assertEquals( TestClass1.TESTS_FAILED + TestClass2.TESTS_FAILED, summary.getTestsFailedCount() );
+    }
 
-        assertThat( executionListener.summaries ).hasSize( 2 );
-        TestClass1.verifyExecutionSummary( executionListener.summaries.get( 0 ) );
-        TestClass2.verifyExecutionSummary( executionListener.summaries.get( 1 ) );
+    @Test
+    void outputIsCaptured()
+                    throws Exception
+    {
+        Launcher launcher = LauncherFactory.create();
+        RunListener runListener = runListenerMock();
+        JUnitPlatformProvider provider =
+                        new JUnitPlatformProvider( providerParametersMock( runListener ), launcher );
+
+        invokeProvider( provider, VerboseTestClass.class );
+
+        ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass( byte[].class );
+        // @formatter:off
+        verify( (ConsoleOutputReceiver) runListener )
+                        .writeTestOutput( captor.capture(), eq( 0 ), gt( 6 ), eq( true ) );
+        verify( (ConsoleOutputReceiver) runListener )
+                        .writeTestOutput( captor.capture(), eq( 0 ), gt( 6 ), eq( false ) );
+        assertThat( captor.getAllValues() )
+                        .extracting( bytes -> new String( bytes, 0, 6 ) )
+                        .containsExactly( "stdout", "stderr" );
+        // @formatter:on
     }
 
     @Test
@@ -139,16 +239,17 @@ class JUnitPlatformProviderTests
     }
 
     @Test
-    void bothExcludedGroupsAndExcludeTagsThrowsException() {
+    void bothExcludedGroupsAndExcludeTagsThrowsException()
+    {
         Map<String, String> properties = new HashMap<>();
-        properties.put(JUnitPlatformProvider.EXCLUDE_GROUPS, "groupOne, groupTwo");
-        properties.put(JUnitPlatformProvider.EXCLUDE_TAGS, "tagOne, tagTwo");
-        verifyPreconditionViolationException(properties);
+        properties.put( JUnitPlatformProvider.EXCLUDE_GROUPS, "groupOne, groupTwo" );
+        properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "tagOne, tagTwo" );
+        verifyPreconditionViolationException( properties );
     }
 
     @Test
     void onlyGroupsIsDeclared()
-        throws Exception
+                    throws Exception
     {
         Map<String, String> properties = new HashMap<>();
         properties.put( JUnitPlatformProvider.INCLUDE_GROUPS, "groupOne, groupTwo" );
@@ -158,12 +259,12 @@ class JUnitPlatformProviderTests
 
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
 
-        assertEquals( 1, provider.includeAndExcludeFilters.length );
+        assertEquals( 1, provider.filters.length );
     }
 
     @Test
     void onlyExcludeTagsIsDeclared()
-        throws Exception
+                    throws Exception
     {
         Map<String, String> properties = new HashMap<>();
         properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "tagOne, tagTwo" );
@@ -173,12 +274,44 @@ class JUnitPlatformProviderTests
 
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
 
-        assertEquals( 1, provider.includeAndExcludeFilters.length );
+        assertEquals( 1, provider.filters.length );
+    }
+
+    @Test
+    void noFiltersAreCreatedIfTagsAreEmpty()
+                    throws Exception
+    {
+        Map<String, String> properties = new HashMap<>();
+        properties.put( JUnitPlatformProvider.INCLUDE_TAGS, "" );
+        properties.put( JUnitPlatformProvider.INCLUDE_GROUPS, "" );
+
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+        when( providerParameters.getProviderProperties() ).thenReturn( properties );
+
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+        assertEquals( 0, provider.filters.length );
+    }
+
+    @Test
+    void filtersWithEmptyTagsAreNotRegistered()
+                    throws Exception
+    {
+        Map<String, String> properties = new HashMap<>();
+
+        // Here only tagOne is registered as a valid tag and other tags are ignored as they are empty
+        properties.put( JUnitPlatformProvider.EXCLUDE_GROUPS, "tagOne," );
+        properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "" );
+
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+        when( providerParameters.getProviderProperties() ).thenReturn( properties );
+
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+        assertEquals( 1, provider.filters.length );
     }
 
     @Test
     void bothIncludeAndExcludeAreAllowed()
-        throws Exception
+                    throws Exception
     {
         Map<String, String> properties = new HashMap<>();
         properties.put( JUnitPlatformProvider.INCLUDE_TAGS, "tagOne, tagTwo" );
@@ -189,18 +322,173 @@ class JUnitPlatformProviderTests
 
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
 
-        assertEquals( 2, provider.includeAndExcludeFilters.length );
+        assertEquals( 2, provider.filters.length );
+    }
+
+    @Test
+    void tagExpressionsAreSupportedForIncludeTagsContainingVerticalBar()
+    {
+        Map<String, String> properties = new HashMap<>();
+        properties.put( JUnitPlatformProvider.INCLUDE_TAGS, "tagOne | tagTwo" );
+        properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "tagThree | tagFour" );
+
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+        when( providerParameters.getProviderProperties() ).thenReturn( properties );
+
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+
+        assertEquals( 2, provider.filters.length );
+    }
+
+    @Test
+    void tagExpressionsAreSupportedForIncludeTagsContainingAmpersand()
+    {
+        Map<String, String> properties = new HashMap<>();
+        properties.put( JUnitPlatformProvider.INCLUDE_TAGS, "tagOne & !tagTwo" );
+        properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "tagThree & !tagFour" );
+
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+        when( providerParameters.getProviderProperties() ).thenReturn( properties );
+
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+
+        assertEquals( 2, provider.filters.length );
     }
 
     @Test
     void noFiltersAreCreatedIfNoPropertiesAreDeclared()
-        throws Exception
+                    throws Exception
+    {
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+
+        assertEquals( 0, provider.filters.length );
+    }
+
+    @Test
+    void defaultConfigurationParametersAreEmpty()
     {
         ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+        when( providerParameters.getProviderProperties() ).thenReturn( emptyMap() );
 
         JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
 
-        assertEquals( 0, provider.includeAndExcludeFilters.length );
+        assertTrue( provider.configurationParameters.isEmpty() );
+    }
+
+    @Test
+    void parsesConfigurationParameters()
+    {
+        ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
+        when( providerParameters.getProviderProperties() )
+                        .thenReturn( //
+                                     singletonMap(
+                                                     JUnitPlatformProvider.CONFIGURATION_PARAMETERS,
+                                                     "foo = true\nbar 42\rbaz: *\r\nqux: EOF" ) );
+
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters );
+
+        assertEquals( 4, provider.configurationParameters.size() );
+        assertEquals( "true", provider.configurationParameters.get( "foo" ) );
+        assertEquals( "42", provider.configurationParameters.get( "bar" ) );
+        assertEquals( "*", provider.configurationParameters.get( "baz" ) );
+        assertEquals( "EOF", provider.configurationParameters.get( "qux" ) );
+    }
+
+    @Test
+    void executesSingleTestIncludedByName()
+                    throws Exception
+    {
+        // following is equivalent of adding '-Dtest=TestClass3#prefix1Suffix1'
+        // '*' needed because it's a nested class and thus has name prefixed with '$'
+        String pattern = "*TestClass3#prefix1Suffix1";
+
+        testExecutionOfMatchingTestMethods( TestClass3.class, pattern, "prefix1Suffix1()" );
+    }
+
+    @Test
+    void executesMultipleTestsIncludedByName()
+                    throws Exception
+    {
+        // following is equivalent of adding '-Dtest=TestClass3#prefix1Suffix1+prefix2Suffix1'
+        // '*' needed because it's a nested class and thus has name prefixed with '$'
+        String pattern = "*TestClass3#prefix1Suffix1+prefix2Suffix1";
+
+        testExecutionOfMatchingTestMethods(
+                        TestClass3.class, pattern, "prefix1Suffix1()", "prefix2Suffix1()" );
+    }
+
+    @Test
+    void executesMultipleTestsIncludedByNamePattern()
+                    throws Exception
+    {
+        // following is equivalent of adding '-Dtest=TestClass3#prefix1*'
+        // '*' needed because it's a nested class and thus has name prefixed with '$'
+        String pattern = "*TestClass3#prefix1*";
+
+        testExecutionOfMatchingTestMethods(
+                        TestClass3.class, pattern, "prefix1Suffix1()", "prefix1Suffix2()" );
+    }
+
+    @Test
+    void executesMultipleTestsIncludedByNamePatternWithQuestionMark()
+                    throws Exception
+    {
+        // following is equivalent of adding '-Dtest=TestClass3#prefix?Suffix2'
+        // '*' needed because it's a nested class and thus has name prefixed with '$'
+        String pattern = "*TestClass3#prefix?Suffix2";
+
+        testExecutionOfMatchingTestMethods(
+                        TestClass3.class, pattern, "prefix1Suffix2()", "prefix2Suffix2()" );
+    }
+
+    @Test
+    void doesNotExecuteTestsExcludedByName()
+                    throws Exception
+    {
+        // following is equivalent of adding '-Dtest=!TestClass3#prefix1Suffix2'
+        // '*' needed because it's a nested class and thus has name prefixed with '$'
+        String pattern = "!*TestClass3#prefix1Suffix2";
+
+        testExecutionOfMatchingTestMethods(
+                        TestClass3.class, pattern, "prefix1Suffix1()", "prefix2Suffix1()", "prefix2Suffix2()" );
+    }
+
+    @Test
+    void doesNotExecuteTestsExcludedByNamePattern()
+                    throws Exception
+    {
+        // following is equivalent of adding '-Dtest=!TestClass3#prefix2*'
+        // '*' needed because it's a nested class and thus has name prefixed with '$'
+        String pattern = "!*TestClass3#prefix2*";
+
+        testExecutionOfMatchingTestMethods(
+                        TestClass3.class, pattern, "prefix1Suffix1()", "prefix1Suffix2()" );
+    }
+
+    void testExecutionOfMatchingTestMethods(
+                    Class<?> testClass, String pattern, String... expectedTestNames )
+                    throws Exception
+    {
+        TestListResolver testListResolver = new TestListResolver( pattern );
+        ProviderParameters providerParameters = providerParametersMock( testListResolver, testClass );
+        Launcher launcher = LauncherFactory.create();
+        JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters, launcher );
+
+        TestPlanSummaryListener executionListener = new TestPlanSummaryListener();
+        launcher.registerTestExecutionListeners( executionListener );
+
+        invokeProvider( provider, null );
+
+        assertEquals( 1, executionListener.summaries.size() );
+        TestExecutionSummary summary = executionListener.summaries.get( 0 );
+        int expectedCount = expectedTestNames.length;
+        assertEquals( expectedCount, summary.getTestsFoundCount() );
+        assertEquals( expectedCount, summary.getTestsFailedCount() );
+        assertEquals( expectedCount, summary.getFailures().size() );
+
+        assertThat( failedTestDisplayNames( summary ) ).contains( expectedTestNames );
     }
 
     private void verifyPreconditionViolationException( Map<String, String> properties )
@@ -208,14 +496,35 @@ class JUnitPlatformProviderTests
         ProviderParameters providerParameters = providerParametersMock( TestClass1.class );
         when( providerParameters.getProviderProperties() ).thenReturn( properties );
 
-        Throwable throwable = assertThrows( PreconditionViolationException.class, () ->
-                new JUnitPlatformProvider(providerParameters) );
+        Throwable throwable =
+                        assertThrows(
+                                        PreconditionViolationException.class,
+                                        () -> new JUnitPlatformProvider( providerParameters ) );
 
         assertEquals( JUnitPlatformProvider.EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED, throwable.getMessage() );
     }
 
     private static ProviderParameters providerParametersMock( Class<?>... testClasses )
     {
+        return providerParametersMock( runListenerMock(), testClasses );
+    }
+
+    private static ProviderParameters providerParametersMock(
+                    RunListener runListener, Class<?>... testClasses )
+    {
+        TestListResolver testListResolver = new TestListResolver( "" );
+        return providerParametersMock( runListener, testListResolver, testClasses );
+    }
+
+    private static ProviderParameters providerParametersMock(
+                    TestListResolver testListResolver, Class<?>... testClasses )
+    {
+        return providerParametersMock( runListenerMock(), testListResolver, testClasses );
+    }
+
+    private static ProviderParameters providerParametersMock(
+                    RunListener runListener, TestListResolver testListResolver, Class<?>... testClasses )
+    {
         TestsToRun testsToRun = newTestsToRun( testClasses );
 
         ScanResult scanResult = mock( ScanResult.class );
@@ -225,17 +534,37 @@ class JUnitPlatformProviderTests
         when( runOrderCalculator.orderTestClasses( any() ) ).thenReturn( testsToRun );
 
         ReporterFactory reporterFactory = mock( ReporterFactory.class );
-        RunListener runListener = mock( RunListener.class );
         when( reporterFactory.createReporter() ).thenReturn( runListener );
 
+        TestRequest testRequest = mock( TestRequest.class );
+        when( testRequest.getTestListResolver() ).thenReturn( testListResolver );
+
         ProviderParameters providerParameters = mock( ProviderParameters.class );
         when( providerParameters.getScanResult() ).thenReturn( scanResult );
         when( providerParameters.getRunOrderCalculator() ).thenReturn( runOrderCalculator );
         when( providerParameters.getReporterFactory() ).thenReturn( reporterFactory );
+        when( providerParameters.getTestRequest() ).thenReturn( testRequest );
 
         return providerParameters;
     }
 
+    private static RunListener runListenerMock()
+    {
+        return mock( RunListener.class, withSettings().extraInterfaces( ConsoleOutputReceiver.class ) );
+    }
+
+    private static Set<String> failedTestDisplayNames( TestExecutionSummary summary )
+    {
+        // @formatter:off
+        return summary
+                        .getFailures()
+                        .stream()
+                        .map( Failure::getTestIdentifier )
+                        .map( TestIdentifier::getDisplayName )
+                        .collect( toSet() );
+        // @formatter:on
+    }
+
     private static TestsToRun newTestsToRun( Class<?>... testClasses )
     {
         List<Class<?>> classesList = Arrays.asList( testClasses );
@@ -243,7 +572,7 @@ class JUnitPlatformProviderTests
     }
 
     private class TestPlanSummaryListener
-        extends SummaryGeneratingListener
+                    extends SummaryGeneratingListener
     {
 
         final List<TestExecutionSummary> summaries = new ArrayList<>();
@@ -256,9 +585,42 @@ class JUnitPlatformProviderTests
         }
     }
 
-    private static class TestClass1
+    /**
+     * Invokes the provider, then restores system out and system error.
+     *
+     * @see <a href="https://github.com/junit-team/junit5/issues/986">#986</a>
+     */
+    private void invokeProvider( JUnitPlatformProvider provider, Object forkTestSet )
+                    throws TestSetFailedException, InvocationTargetException
+    {
+        PrintStream systemOut = System.out;
+        PrintStream systemErr = System.err;
+        try
+        {
+            provider.invoke( forkTestSet );
+        }
+        finally
+        {
+            System.setOut( systemOut );
+            System.setErr( systemErr );
+        }
+    }
+
+    static class TestClass1
     {
 
+        static final int TESTS_FOUND = 4;
+
+        static final int TESTS_STARTED = 3;
+
+        static final int TESTS_SKIPPED = 1;
+
+        static final int TESTS_SUCCEEDED = 2;
+
+        static final int TESTS_ABORTED = 0;
+
+        static final int TESTS_FAILED = 1;
+
         @Test
         void test1()
         {
@@ -280,21 +642,23 @@ class JUnitPlatformProviderTests
         {
             throw new RuntimeException();
         }
-
-        static void verifyExecutionSummary( TestExecutionSummary summary )
-        {
-            assertEquals( 4, summary.getTestsFoundCount() );
-            assertEquals( 3, summary.getTestsStartedCount() );
-            assertEquals( 2, summary.getTestsSucceededCount() );
-            assertEquals( 1, summary.getTestsSkippedCount() );
-            assertEquals( 0, summary.getTestsAbortedCount() );
-            assertEquals( 1, summary.getTestsFailedCount() );
-        }
     }
 
-    private static class TestClass2
+    static class TestClass2
     {
 
+        static final int TESTS_FOUND = 3;
+
+        static final int TESTS_STARTED = 3;
+
+        static final int TESTS_SKIPPED = 0;
+
+        static final int TESTS_SUCCEEDED = 1;
+
+        static final int TESTS_ABORTED = 1;
+
+        static final int TESTS_FAILED = 1;
+
         @Test
         void test1()
         {
@@ -311,15 +675,82 @@ class JUnitPlatformProviderTests
         {
             assumeTrue( false );
         }
+    }
+
+    static class VerboseTestClass
+    {
+        @Test
+        void test()
+        {
+            System.out.println( "stdout" );
+            System.err.println( "stderr" );
+        }
+    }
+
+    @Test
+    void usesClassNamesForXmlReport()
+                    throws TestSetFailedException, InvocationTargetException
+    {
+        String[] classNames = { Sub1Tests.class.getName(), Sub2Tests.class.getName() };
+        ProviderParameters providerParameters =
+                        providerParametersMock( Sub1Tests.class, Sub2Tests.class );
+
+        JUnitPlatformProvider jUnitPlatformProvider = new JUnitPlatformProvider( providerParameters );
+        TestsToRun testsToRun = newTestsToRun( Sub1Tests.class, Sub2Tests.class );
+
+        invokeProvider( jUnitPlatformProvider, testsToRun );
+        RunListener reporter = providerParameters.getReporterFactory().createReporter();
+
+        ArgumentCaptor<ReportEntry> reportEntryArgumentCaptor =
+                        ArgumentCaptor.forClass( ReportEntry.class );
+        verify( reporter, times( 2 ) ).testSucceeded( reportEntryArgumentCaptor.capture() );
+
+        List<ReportEntry> allValues = reportEntryArgumentCaptor.getAllValues();
+        assertThat( allValues ).extracting( ReportEntry::getSourceName ).containsExactly( classNames );
+    }
+
+    static class AbstractTestClass
+    {
+        @Test
+        void test()
+        {
+        }
+    }
+
+    static class Sub1Tests
+                    extends AbstractTestClass
+    {
+    }
+
+    static class Sub2Tests
+                    extends AbstractTestClass
+    {
+    }
+
+    static class TestClass3
+    {
+        @Test
+        void prefix1Suffix1()
+        {
+            throw new RuntimeException();
+        }
 
-        static void verifyExecutionSummary( TestExecutionSummary summary )
+        @Test
+        void prefix2Suffix1()
+        {
+            throw new RuntimeException();
+        }
+
+        @Test
+        void prefix1Suffix2()
         {
-            assertEquals( 3, summary.getTestsFoundCount() );
-            assertEquals( 3, summary.getTestsStartedCount() );
-            assertEquals( 1, summary.getTestsSucceededCount() );
-            assertEquals( 0, summary.getTestsSkippedCount() );
-            assertEquals( 1, summary.getTestsAbortedCount() );
-            assertEquals( 1, summary.getTestsFailedCount() );
+            throw new RuntimeException();
+        }
+
+        @Test
+        void prefix2Suffix2()
+        {
+            throw new RuntimeException();
         }
     }
 }
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java
index 0a37eb5..d3ec7a4 100644
--- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java
@@ -19,31 +19,45 @@ package org.apache.maven.surefire.junitplatform;
  * under the License.
  */
 
-import static java.util.Collections.singletonList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singleton;
 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.TestExecutionResult.successful;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import java.util.Collections;
 import java.util.Optional;
 
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
+import org.apache.maven.surefire.report.SimpleReportEntry;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.engine.descriptor.ClassTestDescriptor;
 import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;
 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.launcher.TestIdentifier;
 import org.junit.platform.launcher.TestPlan;
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 
 /**
  * Unit tests for {@link RunListenerAdapter}.
@@ -52,7 +66,9 @@ import org.mockito.ArgumentCaptor;
  */
 class RunListenerAdapterTests
 {
+
     private RunListener listener;
+
     private RunListenerAdapter adapter;
 
     @BeforeEach
@@ -60,32 +76,183 @@ class RunListenerAdapterTests
     {
         listener = mock( RunListener.class );
         adapter = new RunListenerAdapter( listener );
+        adapter.testPlanExecutionStarted( TestPlan.from( emptyList() ) );
     }
 
     @Test
     void notifiedWithCorrectNamesWhenMethodExecutionStarted()
-        throws Exception
+                    throws Exception
     {
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
 
-        adapter.executionStarted( newMethodIdentifier() );
+        TestPlan testPlan =
+                        TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) );
+        adapter.testPlanExecutionStarted( testPlan );
+
+        TestIdentifier methodIdentifier =
+                        identifiersAsParentOnTestPlan( testPlan, newClassDescriptor(), newMethodDescriptor() );
+
+        adapter.executionStarted( methodIdentifier );
         verify( listener ).testStarting( entryCaptor.capture() );
 
         ReportEntry entry = entryCaptor.getValue();
-        assertEquals( MY_TEST_METHOD_NAME + "()", entry.getName() );
+        assertEquals( MY_TEST_METHOD_NAME, entry.getName() );
         assertEquals( MyTestClass.class.getName(), entry.getSourceName() );
-        assertNotNull( entry.getStackTraceWriter() );
+        assertNull( entry.getStackTraceWriter() );
     }
 
     @Test
-    void notNotifiedWhenClassExecutionStarted()
+    void notifiedWithCompatibleNameForMethodWithArguments()
+                    throws Exception
     {
-        adapter.executionStarted( newClassIdentifier() );
-        verify( listener, never() ).testStarting( any() );
+        ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+
+        TestPlan testPlan =
+                        TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) );
+        adapter.testPlanExecutionStarted( testPlan );
+
+        TestIdentifier methodIdentifier =
+                        identifiersAsParentOnTestPlan(
+                                        testPlan, newClassDescriptor(), newMethodDescriptor( String.class ) );
+
+        adapter.executionStarted( methodIdentifier );
+        verify( listener ).testStarting( entryCaptor.capture() );
+
+        ReportEntry entry = entryCaptor.getValue();
+        assertEquals( MY_TEST_METHOD_NAME + "{String}", entry.getName() );
+        assertEquals( MyTestClass.class.getName(), entry.getSourceName() );
+        assertNull( entry.getStackTraceWriter() );
+    }
+
+    @Test
+    void notifiedEagerlyForTestSetWhenClassExecutionStarted()
+                    throws Exception
+    {
+        EngineDescriptor engine = newEngineDescriptor();
+        TestDescriptor parent = newClassDescriptor();
+        engine.addChild( parent );
+        TestDescriptor child = newMethodDescriptor();
+        parent.addChild( child );
+        TestPlan plan = TestPlan.from( Collections.singletonList( engine ) );
+
+        adapter.testPlanExecutionStarted( plan );
+        adapter.executionStarted( TestIdentifier.from( engine ) );
+        adapter.executionStarted( TestIdentifier.from( parent ) );
+        verify( listener )
+                        .testSetStarting(
+                                        new SimpleReportEntry(
+                                                        JUnitPlatformProvider.class.getName(),
+                                                        MyTestClass.class.getName() ) );
+        verifyNoMoreInteractions( listener );
+
+        adapter.executionStarted( TestIdentifier.from( child ) );
+        verify( listener )
+                        .testStarting( new SimpleReportEntry( MyTestClass.class.getName(), MY_TEST_METHOD_NAME ) );
+        verifyNoMoreInteractions( listener );
+
+        adapter.executionFinished( TestIdentifier.from( child ), successful() );
+        verify( listener )
+                        .testSucceeded( new SimpleReportEntry( MyTestClass.class.getName(), MY_TEST_METHOD_NAME ) );
+        verifyNoMoreInteractions( listener );
+
+        adapter.executionFinished( TestIdentifier.from( parent ), successful() );
+        verify( listener )
+                        .testSetCompleted(
+                                        new SimpleReportEntry(
+                                                        JUnitPlatformProvider.class.getName(),
+                                                        MyTestClass.class.getName() ) );
+        verifyNoMoreInteractions( listener );
+
+        adapter.executionFinished( TestIdentifier.from( engine ), successful() );
+        verifyNoMoreInteractions( listener );
+    }
+
+    @Test
+    void notifiedLazilyForTestSetWhenFirstTestWithoutClassDescriptorParentStarted()
+    {
+        EngineDescriptor engine = newEngineDescriptor();
+        TestDescriptor parent =
+                        newTestDescriptor(
+                                        engine.getUniqueId().append( "container", "noClass" ), "parent",
+                                        Type.CONTAINER );
+        engine.addChild( parent );
+        TestDescriptor child1 =
+                        newTestDescriptor( parent.getUniqueId().append( "test", "child1" ), "child1", Type.TEST );
+        parent.addChild( child1 );
+        TestDescriptor child2 =
+                        newTestDescriptor( parent.getUniqueId().append( "test", "child2" ), "child2", Type.TEST );
+        parent.addChild( child2 );
+        TestPlan plan = TestPlan.from( Collections.singletonList( engine ) );
+
+        adapter.testPlanExecutionStarted( plan );
+        adapter.executionStarted( TestIdentifier.from( engine ) );
+        adapter.executionStarted( TestIdentifier.from( 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();
+
+        adapter.executionFinished( TestIdentifier.from( child1 ), successful() );
+        verify( listener ).testSucceeded( new SimpleReportEntry( "parent", "child1" ) );
+        verifyNoMoreInteractions( listener );
+
+        adapter.executionStarted( TestIdentifier.from( child2 ) );
+        verify( listener ).testStarting( new SimpleReportEntry( "parent", "child2" ) );
+        verifyNoMoreInteractions( listener );
+
+        adapter.executionFinished( TestIdentifier.from( child2 ), successful() );
+        verify( listener ).testSucceeded( new SimpleReportEntry( "parent", "child2" ) );
+        verifyNoMoreInteractions( listener );
+
+        adapter.executionFinished( TestIdentifier.from( parent ), successful() );
+        verify( listener )
+                        .testSetCompleted( new SimpleReportEntry( JUnitPlatformProvider.class.getName(), "parent" ) );
+        verifyNoMoreInteractions( listener );
+
+        adapter.executionFinished( TestIdentifier.from( engine ), successful() );
+        verifyNoMoreInteractions( listener );
+    }
+
+    @Test
+    void notifiedForTestSetForSingleNodeEngine()
+    {
+        EngineDescriptor engine =
+                        new EngineDescriptor( UniqueId.forEngine( "engine" ), "engine" )
+                        {
+                            @Override
+                            public Type getType()
+                            {
+                                return Type.TEST;
+                            }
+                        };
+        TestPlan plan = TestPlan.from( Collections.singletonList( engine ) );
+
+        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();
+
+        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();
     }
 
     @Test
     void notNotifiedWhenEngineExecutionStarted()
+                    throws Exception
     {
         adapter.executionStarted( newEngineIdentifier() );
         verify( listener, never() ).testStarting( any() );
@@ -93,7 +260,7 @@ class RunListenerAdapterTests
 
     @Test
     void notifiedWhenMethodExecutionSkipped()
-        throws Exception
+                    throws Exception
     {
         adapter.executionSkipped( newMethodIdentifier(), "test" );
         verify( listener ).testSkipped( any() );
@@ -101,19 +268,27 @@ class RunListenerAdapterTests
 
     @Test
     void notifiedWithCorrectNamesWhenClassExecutionSkipped()
+                    throws Exception
     {
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+        TestPlan testPlan =
+                        TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) );
+        adapter.testPlanExecutionStarted( testPlan );
 
-        adapter.executionSkipped( newClassIdentifier(), "test" );
+        TestIdentifier classIdentifier =
+                        identifiersAsParentOnTestPlan( testPlan, newEngineDescriptor(), newClassDescriptor() );
+
+        adapter.executionSkipped( classIdentifier, "test" );
         verify( listener ).testSkipped( entryCaptor.capture() );
 
         ReportEntry entry = entryCaptor.getValue();
         assertTrue( MyTestClass.class.getTypeName().contains( entry.getName() ) );
-        assertEquals( MyTestClass.class.getName(), entry.getSourceName() );
+        assertEquals( MyTestClass.class.getTypeName(), entry.getSourceName() );
     }
 
     @Test
     void notifiedWhenEngineExecutionSkipped()
+                    throws Exception
     {
         adapter.executionSkipped( newEngineIdentifier(), "test" );
         verify( listener ).testSkipped( any() );
@@ -121,7 +296,7 @@ class RunListenerAdapterTests
 
     @Test
     void notifiedWhenMethodExecutionAborted()
-        throws Exception
+                    throws Exception
     {
         adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.aborted( null ) );
         verify( listener ).testAssumptionFailure( any() );
@@ -129,57 +304,89 @@ class RunListenerAdapterTests
 
     @Test
     void notifiedWhenClassExecutionAborted()
+                    throws Exception
     {
         adapter.executionFinished( newClassIdentifier(), TestExecutionResult.aborted( null ) );
         verify( listener ).testAssumptionFailure( any() );
     }
 
     @Test
-    void notifiedWhenMethodExecutionFailed()
-        throws Exception
+    void notifiedWhenMethodExecutionFailedWithAnAssertionError()
+                    throws Exception
     {
-        adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.failed( new RuntimeException() ) );
+        adapter.executionFinished(
+                        newMethodIdentifier(), TestExecutionResult.failed( new AssertionError() ) );
         verify( listener ).testFailed( any() );
     }
 
     @Test
+    void notifiedWhenMethodExecutionFailedWithANonAssertionError()
+                    throws Exception
+    {
+        adapter.executionFinished(
+                        newMethodIdentifier(), TestExecutionResult.failed( new RuntimeException() ) );
+        verify( listener ).testError( any() );
+    }
+
+    @Test
     void notifiedWithCorrectNamesWhenClassExecutionFailed()
+                    throws Exception
     {
         ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+        TestPlan testPlan =
+                        TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) );
+        adapter.testPlanExecutionStarted( testPlan );
 
-        adapter.executionFinished( newClassIdentifier(), TestExecutionResult.failed( new RuntimeException() ) );
+        adapter.executionFinished(
+                        identifiersAsParentOnTestPlan( testPlan, newEngineDescriptor(), newClassDescriptor() ),
+                        TestExecutionResult.failed( new AssertionError() ) );
         verify( listener ).testFailed( entryCaptor.capture() );
 
         ReportEntry entry = entryCaptor.getValue();
-        assertEquals( MyTestClass.class.getName(), entry.getSourceName() );
+        assertEquals( MyTestClass.class.getTypeName(), entry.getSourceName() );
         assertNotNull( entry.getStackTraceWriter() );
     }
 
     @Test
     void notifiedWhenMethodExecutionSucceeded()
-        throws Exception
+                    throws Exception
     {
-        adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.successful() );
+        adapter.executionFinished( newMethodIdentifier(), successful() );
         verify( listener ).testSucceeded( any() );
     }
 
     @Test
-    void notNotifiedWhenClassExecutionSucceeded()
+    void notifiedForTestSetWhenClassExecutionSucceeded()
+                    throws Exception
     {
-        adapter.executionFinished( newClassIdentifier(), TestExecutionResult.successful() );
+        EngineDescriptor engineDescriptor = newEngineDescriptor();
+        TestDescriptor classDescriptor = newClassDescriptor();
+        engineDescriptor.addChild( classDescriptor );
+        adapter.testPlanExecutionStarted( TestPlan.from( singleton( engineDescriptor ) ) );
+        adapter.executionStarted( TestIdentifier.from( classDescriptor ) );
+
+        adapter.executionFinished( TestIdentifier.from( classDescriptor ), successful() );
+
+        verify( listener )
+                        .testSetCompleted(
+                                        new SimpleReportEntry(
+                                                        JUnitPlatformProvider.class.getName(),
+                                                        MyTestClass.class.getName() ) );
         verify( listener, never() ).testSucceeded( any() );
     }
 
     @Test
     void notifiedWithParentDisplayNameWhenTestClassUnknown()
+                    throws Exception
     {
         // Set up a test plan
-        TestPlan plan = TestPlan.from( singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) );
+        TestPlan plan =
+                        TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) );
         adapter.testPlanExecutionStarted( plan );
 
         // Use the test plan to set up child with parent.
         final String parentDisplay = "I am your father";
-        TestIdentifier child = newSourcelessIdentifierWithParent( plan, parentDisplay );
+        TestIdentifier child = newSourcelessChildIdentifierWithParent( plan, parentDisplay, null );
         adapter.executionStarted( child );
 
         // Check that the adapter has informed Surefire that the test has been invoked,
@@ -189,36 +396,117 @@ class RunListenerAdapterTests
         assertEquals( parentDisplay, entryCaptor.getValue().getSourceName() );
     }
 
+    @Test
+    void stackTraceWriterPresentWhenParentHasSource()
+                    throws Exception
+    {
+        TestPlan plan =
+                        TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Some Plan" ) ) );
+        adapter.testPlanExecutionStarted( plan );
+
+        TestIdentifier child =
+                        newSourcelessChildIdentifierWithParent( plan, "Parent", ClassSource.from( MyTestClass.class ) );
+        adapter.executionFinished( child, TestExecutionResult.failed( new RuntimeException() ) );
+        ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+        verify( listener ).testError( entryCaptor.capture() );
+        assertNotNull( entryCaptor.getValue().getStackTraceWriter() );
+    }
+
+    @Test
+    void stackTraceWriterDefaultsToTestClass()
+                    throws Exception
+    {
+        TestPlan plan =
+                        TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Some Plan" ) ) );
+        adapter.testPlanExecutionStarted( plan );
+
+        TestIdentifier child = newSourcelessChildIdentifierWithParent( plan, "Parent", null );
+        adapter.executionFinished( child, TestExecutionResult.failed( new RuntimeException( "message" ) ) );
+        ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+        verify( listener ).testError( entryCaptor.capture() );
+        assertNotNull( entryCaptor.getValue().getStackTraceWriter() );
+        assertNotNull( entryCaptor.getValue().getStackTraceWriter().smartTrimmedStackTrace() );
+        assertNotNull( entryCaptor.getValue().getStackTraceWriter().writeTraceToString() );
+        assertNotNull( entryCaptor.getValue().getStackTraceWriter().writeTrimmedTraceToString() );
+    }
+
+    @Test
+    void stackTraceWriterPresentEvenWithoutException()
+                    throws Exception
+    {
+        adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.failed( null ) );
+        ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+        verify( listener ).testError( entryCaptor.capture() );
+        assertNotNull( entryCaptor.getValue().getStackTraceWriter() );
+    }
+
+    @Test
+    void displayNamesIgnoredInReport()
+                    throws NoSuchMethodException
+    {
+        TestMethodTestDescriptor descriptor =
+                        new TestMethodTestDescriptor(
+                                        newId(), MyTestClass.class,
+                                        MyTestClass.class.getDeclaredMethod( "myNamedTestMethod" ) );
+
+        TestIdentifier factoryIdentifier = TestIdentifier.from( descriptor );
+        ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class );
+
+        adapter.executionSkipped( factoryIdentifier, "" );
+        verify( listener ).testSkipped( entryCaptor.capture() );
+
+        ReportEntry value = entryCaptor.getValue();
+
+        assertEquals( "myNamedTestMethod", value.getName() );
+    }
+
     private static TestIdentifier newMethodIdentifier()
-        throws Exception
+                    throws Exception
     {
-        TestDescriptor testDescriptor = new TestMethodTestDescriptor( newId(), MyTestClass.class,
-            MyTestClass.class.getDeclaredMethod( MY_TEST_METHOD_NAME ) );
-        return TestIdentifier.from( testDescriptor );
+        return TestIdentifier.from( newMethodDescriptor() );
+    }
+
+    private static TestDescriptor newMethodDescriptor( Class<?>... parameterTypes )
+                    throws Exception
+    {
+        return new TestMethodTestDescriptor(
+                        UniqueId.forEngine( "method" ),
+                        MyTestClass.class,
+                        MyTestClass.class.getDeclaredMethod( MY_TEST_METHOD_NAME, parameterTypes ) );
     }
 
     private static TestIdentifier newClassIdentifier()
     {
-        TestDescriptor testDescriptor = new ClassTestDescriptor( newId(), MyTestClass.class );
-        return TestIdentifier.from( testDescriptor );
+        return TestIdentifier.from( newClassDescriptor() );
     }
 
-    private static TestIdentifier newSourcelessIdentifierWithParent( TestPlan testPlan, String parentDisplay )
+    private static TestDescriptor newClassDescriptor()
+    {
+        return new ClassTestDescriptor(
+                        UniqueId.root( "class", MyTestClass.class.getName() ), MyTestClass.class );
+    }
+
+    private static TestIdentifier newSourcelessChildIdentifierWithParent(
+                    TestPlan testPlan, String parentDisplay, TestSource parentTestSource )
     {
         // A parent test identifier with a name.
         TestDescriptor parent = mock( TestDescriptor.class );
         when( parent.getUniqueId() ).thenReturn( newId() );
         when( parent.getDisplayName() ).thenReturn( parentDisplay );
+        when( parent.getLegacyReportingName() ).thenReturn( parentDisplay );
+        when( parent.getSource() ).thenReturn( Optional.ofNullable( parentTestSource ) );
+        when( parent.getType() ).thenReturn( Type.CONTAINER );
         TestIdentifier parentId = TestIdentifier.from( parent );
 
         // The (child) test case that is to be executed as part of a test plan.
         TestDescriptor child = mock( TestDescriptor.class );
         when( child.getUniqueId() ).thenReturn( newId() );
-        when( child.isTest() ).thenReturn( true );
+        when( child.getType() ).thenReturn( Type.TEST );
+        when( child.getLegacyReportingName() ).thenReturn( "child" );
 
         // Ensure the child source is null yet that there is a parent -- the special case to be tested.
         when( child.getSource() ).thenReturn( Optional.empty() );
-        when( child.getParent() ).thenReturn( Optional.of(parent) );
+        when( child.getParent() ).thenReturn( Optional.of( parent ) );
         TestIdentifier childId = TestIdentifier.from( child );
 
         testPlan.add( childId );
@@ -229,10 +517,41 @@ class RunListenerAdapterTests
 
     private static TestIdentifier newEngineIdentifier()
     {
-        TestDescriptor testDescriptor = new EngineDescriptor( newId(), "engine" );
+        TestDescriptor testDescriptor = newEngineDescriptor();
         return TestIdentifier.from( testDescriptor );
     }
 
+    private static EngineDescriptor newEngineDescriptor()
+    {
+        return new EngineDescriptor( UniqueId.forEngine( "engine" ), "engine" );
+    }
+
+    private TestDescriptor newTestDescriptor( UniqueId uniqueId, String displayName, Type type )
+    {
+        return new AbstractTestDescriptor( uniqueId, displayName )
+        {
+            @Override
+            public Type getType()
+            {
+                return type;
+            }
+        };
+    }
+
+    private static TestIdentifier identifiersAsParentOnTestPlan(
+                    TestPlan plan, TestDescriptor parent, TestDescriptor child )
+    {
+        child.setParent( parent );
+
+        TestIdentifier parentIdentifier = TestIdentifier.from( parent );
+        TestIdentifier childIdentifier = TestIdentifier.from( child );
+
+        plan.add( parentIdentifier );
+        plan.add( childIdentifier );
+
+        return childIdentifier;
+    }
+
     private static UniqueId newId()
     {
         return UniqueId.forEngine( "engine" );
@@ -240,13 +559,22 @@ class RunListenerAdapterTests
 
     private static final String MY_TEST_METHOD_NAME = "myTestMethod";
 
-    private static class MyTestClass {
-
+    private static class MyTestClass
+    {
         @Test
         void myTestMethod()
         {
         }
 
-    }
+        @Test
+        void myTestMethod( String foo )
+        {
+        }
 
+        @DisplayName( "name" )
+        @Test
+        void myNamedTestMethod()
+        {
+        }
+    }
 }
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestMethodFilterTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestMethodFilterTests.java
new file mode 100644
index 0000000..29ca326
--- /dev/null
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestMethodFilterTests.java
@@ -0,0 +1,105 @@
+package org.apache.maven.surefire.junitplatform;
+
+/*
+ * 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 static org.apache.maven.surefire.testset.TestListResolver.toClassFileName;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Method;
+
+import org.apache.maven.surefire.testset.TestListResolver;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.engine.descriptor.ClassTestDescriptor;
+import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;
+import org.junit.platform.engine.FilterResult;
+import org.junit.platform.engine.UniqueId;
+
+/**
+ * Unit tests for {@link TestMethodFilter}.
+ *
+ * @since 1.0.3
+ */
+class TestMethodFilterTests
+{
+
+    private final TestListResolver resolver = mock( TestListResolver.class );
+
+    private final TestMethodFilter filter = new TestMethodFilter( this.resolver );
+
+    @Test
+    void includesBasedOnTestListResolver()
+                    throws Exception
+    {
+        when( resolver.shouldRun( toClassFileName( TestClass.class ), "testMethod" ) ).thenReturn( true );
+
+        FilterResult result = filter.apply( newTestMethodDescriptor() );
+
+        assertTrue( result.included() );
+        assertFalse( result.excluded() );
+    }
+
+    @Test
+    void excludesBasedOnTestListResolver()
+                    throws Exception
+    {
+        when( resolver.shouldRun( toClassFileName( TestClass.class ), "testMethod" ) ).thenReturn( false );
+
+        FilterResult result = filter.apply( newTestMethodDescriptor() );
+
+        assertFalse( result.included() );
+        assertTrue( result.excluded() );
+    }
+
+    @Test
+    void includesTestDescriptorWithClassSource()
+                    throws Exception
+    {
+        FilterResult result = filter.apply( newClassTestDescriptor() );
+
+        assertTrue( result.included() );
+        assertFalse( result.excluded() );
+    }
+
+    private static TestMethodTestDescriptor newTestMethodDescriptor()
+                    throws Exception
+    {
+        UniqueId uniqueId = UniqueId.forEngine( "method" );
+        Class<TestClass> testClass = TestClass.class;
+        Method testMethod = testClass.getMethod( "testMethod" );
+        return new TestMethodTestDescriptor( uniqueId, testClass, testMethod );
+    }
+
+    private static ClassTestDescriptor newClassTestDescriptor()
+                    throws Exception
+    {
+        UniqueId uniqueId = UniqueId.forEngine( "class" );
+        return new ClassTestDescriptor( uniqueId, TestClass.class );
+    }
+
+    public static class TestClass
+    {
+        public void testMethod()
+        {
+        }
+    }
+}
diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java
index 98f5b2b..8ecedc7 100644
--- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java
+++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java
@@ -20,6 +20,7 @@ package org.apache.maven.surefire.junitplatform;
  */
 
 import static java.util.Collections.emptyList;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.List;
@@ -37,20 +38,20 @@ import org.junit.platform.launcher.core.LauncherFactory;
  *
  * @since 1.0
  */
-public class TestPlanScannerFilterTests
+class TestPlanScannerFilterTests
 {
 
     @Test
-    void emptyClassAccepted()
+    void emptyClassIsNotAccepted()
     {
-        assertTrue( newFilter().accept( EmptyClass.class ), "accepts empty class because it is a container" );
+        assertFalse( newFilter().accept( EmptyClass.class ), "does not accept empty class" );
     }
 
     @Test
-    void classWithNoTestMethodsIsAccepted()
+    void classWithNoTestMethodsIsNotAccepted()
     {
-        assertTrue( newFilter().accept( ClassWithMethods.class ),
-            "accepts class with no @Test methods because it is a container" );
+        assertFalse(
+                        newFilter().accept( ClassWithMethods.class ), "does not accept class with no @Test methods" );
     }
 
     @Test
@@ -88,12 +89,11 @@ public class TestPlanScannerFilterTests
         return new TestPlanScannerFilter( LauncherFactory.create(), new Filter<?>[0] );
     }
 
-    private static class EmptyClass
+    static class EmptyClass
     {
     }
 
-    @SuppressWarnings("unused")
-    private static class ClassWithMethods
+    static class ClassWithMethods
     {
 
         void method1()
@@ -105,7 +105,7 @@ public class TestPlanScannerFilterTests
         }
     }
 
-    private static class ClassWithTestMethods
+    static class ClassWithTestMethods
     {
 
         @Test
@@ -119,10 +119,9 @@ public class TestPlanScannerFilterTests
         }
     }
 
-    private static class ClassWithNestedTestClass
+    static class ClassWithNestedTestClass
     {
 
-        @SuppressWarnings("unused")
         void method()
         {
         }
@@ -138,7 +137,7 @@ public class TestPlanScannerFilterTests
         }
     }
 
-    private static class ClassWithDeeplyNestedTestClass
+    static class ClassWithDeeplyNestedTestClass
     {
 
         @Nested
@@ -162,7 +161,7 @@ public class TestPlanScannerFilterTests
         }
     }
 
-    private static class ClassWithTestFactory
+    static class ClassWithTestFactory
     {
 
         @TestFactory
@@ -172,7 +171,7 @@ public class TestPlanScannerFilterTests
         }
     }
 
-    private static class ClassWithNestedTestFactory
+    static class ClassWithNestedTestFactory
     {
 
         @Nested
@@ -186,5 +185,4 @@ public class TestPlanScannerFilterTests
             }
         }
     }
-
 }

-- 
To stop receiving notification emails like this one, please contact
tibordigana@apache.org.