You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2019/03/01 19:12:05 UTC

[maven-surefire] branch master updated: [SUREFIRE-1585] Align JUnit Platform version at runtime

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

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


The following commit(s) were added to refs/heads/master by this push:
     new de50626  [SUREFIRE-1585] Align JUnit Platform version at runtime
de50626 is described below

commit de506262225c43e43be4fd64cf25940e5a807aaa
Author: tibordigana <ti...@apache.org>
AuthorDate: Fri Feb 1 06:03:27 2019 +0100

    [SUREFIRE-1585] Align JUnit Platform version at runtime
---
 maven-failsafe-plugin/pom.xml                      |   13 +-
 maven-surefire-common/pom.xml                      |   23 +-
 .../plugin/surefire/AbstractSurefireMojo.java      |  352 ++++--
 .../apache/maven/plugin/surefire/ProviderInfo.java |    2 +-
 .../surefire/SurefireDependencyResolver.java       |  103 +-
 .../maven/plugin/surefire/TestClassPath.java       |   34 +-
 .../AbstractSurefireMojoJava7PlusTest.java         |    5 +-
 .../plugin/surefire/AbstractSurefireMojoTest.java  | 1271 +++++++++++++++++++-
 .../surefire/SurefireDependencyResolverTest.java   |    8 +-
 maven-surefire-plugin/pom.xml                      |    5 +
 .../src/site/apt/examples/junit-platform.apt.vm    |  110 +-
 maven-surefire-report-plugin/pom.xml               |    5 -
 pom.xml                                            |   39 +-
 surefire-api/pom.xml                               |    4 +-
 .../maven/surefire/its/JUnitPlatformEnginesIT.java |    8 +-
 .../maven/surefire/its/jiras/Surefire1585IT.java   |   54 +
 .../resources/surefire-1585-junit4-vintage/pom.xml |   61 +
 .../src/test/java/JUnit4Test.java                  |   26 +-
 .../resources/surefire-1585-jupiter-api/pom.xml    |   54 +
 .../src/test/java/JupiterTest.java                 |   28 +-
 surefire-providers/common-java5/pom.xml            |    4 +-
 surefire-report-parser/pom.xml                     |    4 +-
 22 files changed, 1970 insertions(+), 243 deletions(-)

diff --git a/maven-failsafe-plugin/pom.xml b/maven-failsafe-plugin/pom.xml
index c947b54..31dcfbf 100644
--- a/maven-failsafe-plugin/pom.xml
+++ b/maven-failsafe-plugin/pom.xml
@@ -60,6 +60,11 @@
             <classifier>site-source</classifier>
         </dependency>
         <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.maven.plugin-tools</groupId>
             <artifactId>maven-plugin-annotations</artifactId>
             <scope>provided</scope>
@@ -208,16 +213,16 @@
                             </artifactSet>
                             <relocations>
                                 <relocation>
-                                    <pattern>org.apache.maven.shared</pattern>
-                                    <shadedPattern>org.apache.maven.surefire.shade.org.apache.maven.shared</shadedPattern>
+                                    <pattern>org.apache.maven.shared.utils</pattern>
+                                    <shadedPattern>org.apache.maven.surefire.shade.failsafe.org.apache.maven.shared.utils</shadedPattern>
                                 </relocation>
                                 <relocation>
                                     <pattern>org.apache.commons.io</pattern>
-                                    <shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.io</shadedPattern>
+                                    <shadedPattern>org.apache.maven.surefire.shade.failsafe.org.apache.commons.io</shadedPattern>
                                 </relocation>
                                 <relocation>
                                     <pattern>org.apache.commons.lang3</pattern>
-                                    <shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.lang3</shadedPattern>
+                                    <shadedPattern>org.apache.maven.surefire.shade.failsafe.org.apache.commons.lang3</shadedPattern>
                                 </relocation>
                             </relocations>
                         </configuration>
diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml
index 57adfa9..770642e 100644
--- a/maven-surefire-common/pom.xml
+++ b/maven-surefire-common/pom.xml
@@ -53,27 +53,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.maven</groupId>
-            <artifactId>maven-plugin-api</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>org.apache.maven.plugin-tools</groupId>
             <artifactId>maven-plugin-annotations</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
-            <artifactId>maven-artifact</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.maven</groupId>
-            <artifactId>maven-model</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.maven</groupId>
             <artifactId>maven-toolchain</artifactId>
         </dependency>
         <dependency>
@@ -81,6 +66,10 @@
             <artifactId>maven-common-artifact-filters</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.apache.maven.shared</groupId>
+            <artifactId>maven-artifact-transfer</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.codehaus.plexus</groupId>
             <artifactId>plexus-java</artifactId>
         </dependency>
@@ -165,8 +154,8 @@
                             </artifactSet>
                             <relocations>
                                 <relocation>
-                                    <pattern>org.apache.maven.shared</pattern>
-                                    <shadedPattern>org.apache.maven.surefire.shade.common.org.apache.maven.shared</shadedPattern>
+                                    <pattern>org.apache.maven.shared.utils</pattern>
+                                    <shadedPattern>org.apache.maven.surefire.shade.common.org.apache.maven.shared.utils</shadedPattern>
                                 </relocation>
                                 <relocation>
                                     <pattern>org.apache.commons.io</pattern>
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 7c76ef1..ba7f1e0 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
@@ -21,8 +21,12 @@ package org.apache.maven.plugin.surefire;
  */
 
 import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.handler.ArtifactHandler;
 import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.model.Dependency;
 import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.repository.RepositorySystem;
 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
 import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
@@ -50,6 +54,7 @@ import org.apache.maven.plugin.surefire.util.DirectoryScanner;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
+import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
 import org.apache.maven.shared.utils.io.FileUtils;
 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
 import org.apache.maven.surefire.booter.Classpath;
@@ -90,6 +95,7 @@ import java.math.BigDecimal;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -112,6 +118,7 @@ import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonMap;
 import static org.apache.commons.lang3.StringUtils.substringBeforeLast;
 import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
+import static org.apache.maven.artifact.ArtifactUtils.artifactMapByVersionlessId;
 import static org.apache.maven.plugin.surefire.SurefireDependencyResolver.isWithinVersionSpec;
 import static org.apache.maven.plugin.surefire.util.DependencyScanner.filter;
 import static org.apache.maven.plugin.surefire.SurefireHelper.replaceThreadNumberPlaceholders;
@@ -478,15 +485,6 @@ public abstract class AbstractSurefireMojo
     private String junitArtifactName;
 
     /**
-     * Allows you to specify the name of the JUnit Platform artifact.
-     * If not set, {@code org.junit.platform:junit-platform-engine} will be used.
-     *
-     * @since 2.22.0
-     */
-    @Parameter( property = "junitPlatformArtifactName", defaultValue = "org.junit.platform:junit-platform-engine" )
-    private String junitPlatformArtifactName;
-
-    /**
      * Allows you to specify the name of the TestNG artifact. If not set, {@code org.testng:testng} will be used.
      *
      * @since 2.3.1
@@ -663,6 +661,9 @@ public abstract class AbstractSurefireMojo
     @Parameter( defaultValue = "${project.pluginArtifactRepositories}", readonly = true, required = true )
     private List<ArtifactRepository> remoteRepositories;
 
+    @Parameter( defaultValue = "${project.remoteArtifactRepositories}", required = true, readonly = true )
+    private List<ArtifactRepository> projectRemoteRepositories;
+
     /**
      * Flag to disable the generation of report files in xml format.
      *
@@ -737,6 +738,9 @@ public abstract class AbstractSurefireMojo
     @Component
     private RepositorySystem repositorySystem;
 
+    @Component
+    private DependencyResolver dependencyResolver;
+
     private Artifact surefireBooterArtifact;
 
     private Toolchain toolchain;
@@ -798,7 +802,7 @@ public abstract class AbstractSurefireMojo
      */
     protected Artifact getMojoArtifact()
     {
-        return pluginDescriptor.getPluginArtifact();
+        return getPluginDescriptor().getPluginArtifact();
     }
 
     private String getDefaultExcludes()
@@ -806,7 +810,7 @@ public abstract class AbstractSurefireMojo
         return "**/*$*";
     }
 
-    private SurefireDependencyResolver dependencyResolver;
+    private SurefireDependencyResolver surefireDependencyResolver;
 
     private TestListResolver specificTests;
 
@@ -842,6 +846,11 @@ public abstract class AbstractSurefireMojo
         }
     }
 
+    void setLogger( Logger logger )
+    {
+        this.logger = logger;
+    }
+
     @Nonnull
     protected final PluginConsoleLogger getConsoleLogger()
     {
@@ -860,9 +869,22 @@ public abstract class AbstractSurefireMojo
 
     private void setupStuff()
     {
-        createDependencyResolver();
-        surefireBooterArtifact = getSurefireBooterArtifact();
-        toolchain = getToolchain();
+        surefireDependencyResolver = new SurefireDependencyResolver( getRepositorySystem(),
+                getConsoleLogger(), getLocalRepository(),
+                getRemoteRepositories(),
+                getProjectRemoteRepositories(),
+                getPluginName(), getDependencyResolver() );
+
+        surefireBooterArtifact = getBooterArtifact();
+        if ( surefireBooterArtifact == null )
+        {
+            throw new RuntimeException( "Unable to locate surefire-booter in the list of plugin artifacts" );
+        }
+
+        if ( getToolchainManager() != null )
+        {
+            toolchain = getToolchainManager().getToolchainFromBuildContext( "jdk", getSession() );
+        }
     }
 
     @Nonnull
@@ -983,7 +1005,8 @@ public abstract class AbstractSurefireMojo
     private void executeAfterPreconditionsChecked( @Nonnull DefaultScanResult scanResult )
         throws MojoExecutionException, MojoFailureException
     {
-        List<ProviderInfo> providers = createProviders();
+        TestClassPath testClasspath = generateTestClasspath();
+        List<ProviderInfo> providers = createProviders( testClasspath );
 
         RunResult current = noTestsRun();
 
@@ -992,7 +1015,7 @@ public abstract class AbstractSurefireMojo
         {
             try
             {
-                current = current.aggregate( executeProvider( provider, scanResult ) );
+                current = current.aggregate( executeProvider( provider, scanResult, testClasspath ) );
             }
             catch ( SurefireBooterForkException | SurefireExecutionException | TestSetFailedException e )
             {
@@ -1011,21 +1034,13 @@ public abstract class AbstractSurefireMojo
         handleSummary( current, firstForkException );
     }
 
-    private void createDependencyResolver()
-    {
-        dependencyResolver = new SurefireDependencyResolver( getRepositorySystem(),
-                                                                   getConsoleLogger(), getLocalRepository(),
-                                                                   getRemoteRepositories(),
-                                                                   getPluginName() );
-    }
-
-    protected List<ProviderInfo> createProviders()
+    protected List<ProviderInfo> createProviders( TestClassPath testClasspath )
         throws MojoExecutionException
     {
         Artifact junitDepArtifact = getJunitDepArtifact();
         return new ProviderList( new DynamicProviderInfo( null ),
                               new TestNgProviderInfo( getTestNgArtifact() ),
-                              new JUnitPlatformProviderInfo( getJunitPlatformArtifact() ),
+                              new JUnitPlatformProviderInfo( getJunitPlatformArtifact(), testClasspath ),
                               new JUnitCoreProviderInfo( getJunitArtifact(), junitDepArtifact ),
                               new JUnit4ProviderInfo( getJunitArtifact(), junitDepArtifact ),
                               new JUnit3ProviderInfo() )
@@ -1117,7 +1132,8 @@ public abstract class AbstractSurefireMojo
     }
 
     @Nonnull
-    private RunResult executeProvider( @Nonnull ProviderInfo provider, @Nonnull DefaultScanResult scanResult )
+    private RunResult executeProvider( @Nonnull ProviderInfo provider, @Nonnull DefaultScanResult scanResult,
+                                       @Nonnull TestClassPath testClasspathWrapper )
         throws MojoExecutionException, MojoFailureException, SurefireExecutionException, SurefireBooterForkException,
         TestSetFailedException
     {
@@ -1132,8 +1148,8 @@ public abstract class AbstractSurefireMojo
         {
             createCopyAndReplaceForkNumPlaceholder( effectiveProperties, 1 ).copyToSystemProperties();
 
-            InPluginVMSurefireStarter surefireStarter =
-                createInprocessStarter( provider, classLoaderConfiguration, runOrderParameters, scanResult, platform );
+            InPluginVMSurefireStarter surefireStarter = createInprocessStarter( provider, classLoaderConfiguration,
+                    runOrderParameters, scanResult, platform, testClasspathWrapper );
             return surefireStarter.runSuitesInProcess( scanResult );
         }
         else
@@ -1149,7 +1165,8 @@ public abstract class AbstractSurefireMojo
             try
             {
                 forkStarter = createForkStarter( provider, forkConfiguration, classLoaderConfiguration,
-                                                       runOrderParameters, getConsoleLogger(), scanResult, platform );
+                                                       runOrderParameters, getConsoleLogger(), scanResult, platform,
+                                                       testClasspathWrapper );
 
                 return forkStarter.run( effectiveProperties, scanResult );
             }
@@ -1214,16 +1231,14 @@ public abstract class AbstractSurefireMojo
         this.repositorySystem = repositorySystem;
     }
 
-    final Toolchain getToolchain()
+    public DependencyResolver getDependencyResolver()
     {
-        Toolchain tc = null;
-
-        if ( getToolchainManager() != null )
-        {
-            tc = getToolchainManager().getToolchainFromBuildContext( "jdk", getSession() );
-        }
+        return dependencyResolver;
+    }
 
-        return tc;
+    public void setDependencyResolver( DependencyResolver dependencyResolver )
+    {
+        this.dependencyResolver = dependencyResolver;
     }
 
     private boolean existsModuleDescriptor()
@@ -1688,7 +1703,7 @@ public abstract class AbstractSurefireMojo
         return h;
     }
 
-    public File getStatisticsFile( String configurationHash )
+    private File getStatisticsFile( String configurationHash )
     {
         return new File( getBasedir(), ".surefire-" + configurationHash );
     }
@@ -1696,7 +1711,8 @@ public abstract class AbstractSurefireMojo
     private StartupConfiguration createStartupConfiguration( @Nonnull ProviderInfo provider, boolean isInprocess,
                                                              @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
                                                              @Nonnull DefaultScanResult scanResult,
-                                                             @Nonnull Platform platform )
+                                                             @Nonnull Platform platform,
+                                                             @Nonnull TestClassPath testClasspathWrapper )
         throws MojoExecutionException
     {
         try
@@ -1714,11 +1730,12 @@ public abstract class AbstractSurefireMojo
                         .toString();
 
                 return newStartupConfigWithModularPath( classLoaderConfiguration, providerArtifacts, providerName,
-                        getModuleDescriptor(), scanResult, javaHome );
+                        getModuleDescriptor(), scanResult, javaHome, testClasspathWrapper );
             }
             else
             {
-                return newStartupConfigWithClasspath( classLoaderConfiguration, providerArtifacts, providerName );
+                return newStartupConfigWithClasspath( classLoaderConfiguration, providerArtifacts, providerName,
+                        testClasspathWrapper );
             }
         }
         catch ( IOException e )
@@ -1729,13 +1746,10 @@ public abstract class AbstractSurefireMojo
 
     private StartupConfiguration newStartupConfigWithClasspath(
             @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull Set<Artifact> providerArtifacts,
-            @Nonnull String providerName )
+            @Nonnull String providerName, @Nonnull TestClassPath testClasspathWrapper )
     {
-        TestClassPath testClasspathWrapper = generateTestClasspath();
         Classpath testClasspath = testClasspathWrapper.toClasspath();
 
-        testClasspathWrapper.avoidArtifactDuplicates( providerArtifacts );
-
         Classpath providerClasspath = ClasspathCache.getCachedClassPath( providerName );
         if ( providerClasspath == null )
         {
@@ -1769,8 +1783,8 @@ public abstract class AbstractSurefireMojo
             boolean contains = false;
             for ( Artifact providerArtifact : providerArtifacts )
             {
-                if ( providerArtifact.getGroupId().equals( inPluginArtifact.getGroupId() )
-                        && providerArtifact.getArtifactId().equals( inPluginArtifact.getArtifactId() ) )
+                if ( hasGroupArtifactId( providerArtifact.getGroupId(), providerArtifact.getArtifactId(),
+                        inPluginArtifact ) )
                 {
                     contains = true;
                     break;
@@ -1784,6 +1798,11 @@ public abstract class AbstractSurefireMojo
         return result;
     }
 
+    private static boolean hasGroupArtifactId( String groupId, String artifactId, Artifact artifact )
+    {
+        return groupId.equals( artifact.getGroupId() ) && artifactId.equals( artifact.getArtifactId() );
+    }
+
     private static Classpath createInProcClasspath( Classpath providerClasspath, Set<Artifact> newArtifacts )
     {
         Classpath inprocClasspath = providerClasspath.clone();
@@ -1806,14 +1825,11 @@ public abstract class AbstractSurefireMojo
     private StartupConfiguration newStartupConfigWithModularPath(
             @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull Set<Artifact> providerArtifacts,
             @Nonnull String providerName, @Nonnull File moduleDescriptor, @Nonnull DefaultScanResult scanResult,
-            @Nonnull String javaHome )
+            @Nonnull String javaHome, @Nonnull TestClassPath testClasspathWrapper )
             throws IOException
     {
-        TestClassPath testClasspathWrapper = generateTestClasspath();
         Classpath testClasspath = testClasspathWrapper.toClasspath();
 
-        testClasspathWrapper.avoidArtifactDuplicates( providerArtifacts );
-
         Classpath providerClasspath = ClasspathCache.getCachedClassPath( providerName );
         if ( providerClasspath == null )
         {
@@ -2123,14 +2139,19 @@ public abstract class AbstractSurefireMojo
         return getProjectArtifactMap().get( "junit:junit-dep" );
     }
 
-
     private Artifact getJunitPlatformArtifact()
     {
-        Artifact artifact = getProjectArtifactMap().get( getJunitPlatformArtifactName() );
-        Artifact projectArtifact = project.getArtifact();
-        String projectArtifactName = projectArtifact.getGroupId() + ":" + projectArtifact.getArtifactId();
+        Artifact artifact = getProjectArtifactMap().get( "org.junit.platform:junit-platform-commons" );
+        if ( artifact == null )
+        {
+            artifact = getPluginArtifactMap().get( "org.junit.platform:junit-platform-engine" );
+        }
 
-        if ( artifact == null && projectArtifactName.equals( getJunitPlatformArtifactName() ) )
+        Artifact projectArtifact = project.getArtifact();
+        String projectGroupId = projectArtifact.getGroupId();
+        if ( artifact == null && ( "org.junit.platform".equals( projectGroupId )
+                || "org.junit.jupiter".equals( projectGroupId )
+                || "org.junit.vintage".equals( projectGroupId ) ) )
         {
             artifact = projectArtifact;
         }
@@ -2141,11 +2162,12 @@ public abstract class AbstractSurefireMojo
     private ForkStarter createForkStarter( @Nonnull ProviderInfo provider, @Nonnull ForkConfiguration forkConfiguration,
                                            @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
                                            @Nonnull RunOrderParameters runOrderParameters, @Nonnull ConsoleLogger log,
-                                           @Nonnull DefaultScanResult scanResult, @Nonnull Platform platform )
+                                           @Nonnull DefaultScanResult scanResult, @Nonnull Platform platform,
+                                           @Nonnull TestClassPath testClasspathWrapper )
         throws MojoExecutionException, MojoFailureException
     {
-        StartupConfiguration startupConfiguration =
-                createStartupConfiguration( provider, false, classLoaderConfiguration, scanResult, platform );
+        StartupConfiguration startupConfiguration = createStartupConfiguration( provider, false,
+                classLoaderConfiguration, scanResult, platform, testClasspathWrapper );
         String configChecksum = getConfigChecksum();
         StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration( configChecksum, true );
         ProviderConfiguration providerConfiguration = createProviderConfiguration( runOrderParameters );
@@ -2157,11 +2179,12 @@ public abstract class AbstractSurefireMojo
                                                               @Nonnull ClassLoaderConfiguration classLoaderConfig,
                                                               @Nonnull RunOrderParameters runOrderParameters,
                                                               @Nonnull DefaultScanResult scanResult,
-                                                              @Nonnull Platform platform )
+                                                              @Nonnull Platform platform,
+                                                              @Nonnull TestClassPath testClasspathWrapper )
         throws MojoExecutionException, MojoFailureException
     {
-        StartupConfiguration startupConfiguration =
-                createStartupConfiguration( provider, true, classLoaderConfig, scanResult, platform );
+        StartupConfiguration startupConfiguration = createStartupConfiguration( provider, true, classLoaderConfig,
+                scanResult, platform, testClasspathWrapper );
         String configChecksum = getConfigChecksum();
         StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration( configChecksum, false );
         ProviderConfiguration providerConfiguration = createProviderConfiguration( runOrderParameters );
@@ -2353,16 +2376,6 @@ public abstract class AbstractSurefireMojo
         return new JdkAttributes( jvmToUse, isBuiltInJava9AtLeast() );
     }
 
-    private Artifact getSurefireBooterArtifact()
-    {
-        Artifact artifact = getBooterArtifact();
-        if ( artifact == null )
-        {
-            throw new RuntimeException( "Unable to locate surefire-booter in the list of plugin artifacts" );
-        }
-        return artifact;
-    }
-
     /**
      * Where surefire stores its own temp files
      *
@@ -2451,6 +2464,7 @@ public abstract class AbstractSurefireMojo
         checksum.add( isParallelOptimized() );
         checksum.add( isTrimStackTrace() );
         checksum.add( getRemoteRepositories() );
+        checksum.add( getProjectRemoteRepositories() );
         checksum.add( isDisableXmlReport() );
         checksum.add( isUseSystemClassLoader() );
         checksum.add( isUseManifestOnlyJar() );
@@ -2504,7 +2518,7 @@ public abstract class AbstractSurefireMojo
      */
     private TestClassPath generateTestClasspath()
     {
-        @SuppressWarnings( "unchecked" ) Set<Artifact> classpathArtifacts = getProject().getArtifacts();
+        Set<Artifact> classpathArtifacts = getProject().getArtifacts();
 
         if ( getClasspathDependencyScopeExclude() != null && !getClasspathDependencyScopeExclude().isEmpty() )
         {
@@ -2520,7 +2534,7 @@ public abstract class AbstractSurefireMojo
         }
 
         return new TestClassPath( classpathArtifacts, getClassesDirectory(),
-                getTestClassesDirectory(), getAdditionalClasspathElements(), getConsoleLogger() );
+                getTestClassesDirectory(), getAdditionalClasspathElements() );
     }
 
     /**
@@ -2561,7 +2575,9 @@ public abstract class AbstractSurefireMojo
         if ( existing == null )
         {
             List<String> items = new ArrayList<>();
-            for ( Artifact artifact : dependencyResolver.resolveArtifact( surefireArtifact ).getArtifacts() )
+            Set<Artifact> booterArtifacts =
+                    surefireDependencyResolver.resolvePluginArtifact( surefireArtifact ).getArtifacts();
+            for ( Artifact artifact : booterArtifacts )
             {
                 getConsoleLogger().debug(
                     "Adding to " + getPluginName() + " booter test classpath: " + artifact.getFile().getAbsolutePath()
@@ -2665,7 +2681,7 @@ public abstract class AbstractSurefireMojo
                                                             + "is picking up an old junit version" );
                     }
                     throw new MojoFailureException( "groups/excludedGroups require TestNG, JUnit48+ or JUnit 5 "
-                            + "on project test classpath" );
+                            + "(a specific engine required on classpath) on project test classpath" );
                 }
             }
 
@@ -2789,7 +2805,7 @@ public abstract class AbstractSurefireMojo
         {
             Artifact surefireArtifact = getBooterArtifact();
             String version = surefireArtifact.getBaseVersion();
-            return dependencyResolver.getProviderClasspath( "surefire-testng", version );
+            return surefireDependencyResolver.getProviderClasspath( "surefire-testng", version );
         }
     }
 
@@ -2820,7 +2836,7 @@ public abstract class AbstractSurefireMojo
             // add the JUnit provider as default - it doesn't require JUnit to be present,
             // since it supports POJO tests.
             String version = surefireBooterArtifact.getBaseVersion();
-            return dependencyResolver.getProviderClasspath( "surefire-junit3", version );
+            return surefireDependencyResolver.getProviderClasspath( "surefire-junit3", version );
         }
     }
 
@@ -2859,18 +2875,23 @@ public abstract class AbstractSurefireMojo
         public Set<Artifact> getProviderClasspath()
         {
             String version = surefireBooterArtifact.getBaseVersion();
-            return dependencyResolver.getProviderClasspath( "surefire-junit4", version );
+            return surefireDependencyResolver.getProviderClasspath( "surefire-junit4", version );
         }
     }
 
     final class JUnitPlatformProviderInfo
         implements ProviderInfo
     {
-        private final Artifact junitArtifact;
+        private static final String PROVIDER_DEP_GID = "org.junit.platform";
+        private static final String PROVIDER_DEP_AID = "junit-platform-launcher";
 
-        JUnitPlatformProviderInfo( Artifact junitArtifact )
+        private final Artifact junitPlatformArtifact;
+        private final TestClassPath testClasspath;
+
+        JUnitPlatformProviderInfo( Artifact junitPlatformArtifact, TestClassPath testClasspath )
         {
-            this.junitArtifact = junitArtifact;
+            this.junitPlatformArtifact = junitPlatformArtifact;
+            this.testClasspath = testClasspath;
         }
 
         @Override
@@ -2883,7 +2904,7 @@ public abstract class AbstractSurefireMojo
         @Override
         public boolean isApplicable()
         {
-            return junitArtifact != null;
+            return junitPlatformArtifact != null;
         }
 
         @Override
@@ -2894,10 +2915,134 @@ public abstract class AbstractSurefireMojo
 
         @Override
         @Nonnull
-        public Set<Artifact> getProviderClasspath()
+        public Set<Artifact> getProviderClasspath() throws MojoExecutionException
         {
-            String version = surefireBooterArtifact.getBaseVersion();
-            return dependencyResolver.getProviderClasspath( "surefire-junit-platform", version );
+            String surefireVersion = surefireBooterArtifact.getBaseVersion();
+            Map<String, Artifact> providerArtifacts =
+                    surefireDependencyResolver.getProviderClasspathAsMap( "surefire-junit-platform", surefireVersion );
+            Map<String, Artifact> testDependencies = testClasspath.getTestDependencies();
+
+            if ( hasDependencyPlatformEngine( testDependencies ) )
+            {
+                String filterTestDependency = "org.junit.platform:junit-platform-engine";
+                getConsoleLogger().debug( "Test dependencies contain " + filterTestDependency );
+                narrowProviderDependencies( filterTestDependency, providerArtifacts, testDependencies );
+            }
+            else
+            {
+                ProjectBuildingRequest request = getSession().getProjectBuildingRequest();
+                Collection<Dependency> pluginDependencies = getPluginDescriptor().getPlugin().getDependencies();
+                Set<Artifact> engines =
+                        surefireDependencyResolver.resolvePluginDependencies( request, pluginDependencies );
+                if ( hasDependencyPlatformEngine( engines ) )
+                {
+                    Map<String, Artifact> engineArtifacts = artifactMapByVersionlessId( engines );
+                    providerArtifacts.putAll( engineArtifacts );
+                    alignVersions( providerArtifacts, engineArtifacts );
+                }
+                else if ( hasDependencyJupiterAPI( testDependencies ) )
+                {
+                    String engineGroupId = "org.junit.jupiter";
+                    String engineArtifactId = "junit-jupiter-engine";
+                    String engineCoordinates = engineGroupId + ":" + engineArtifactId;
+                    String api = "org.junit.jupiter:junit-jupiter-api";
+                    getConsoleLogger().debug( "Test dependencies contain " + api + ". Resolving " + engineCoordinates );
+                    String engineVersion = testDependencies.get( api ).getBaseVersion();
+                    addEngineByApi( engineGroupId, engineArtifactId, engineVersion,
+                            providerArtifacts, testDependencies );
+                }
+            }
+            providerArtifacts.keySet().removeAll( testDependencies.keySet() );
+            return new LinkedHashSet<>( providerArtifacts.values() );
+        }
+
+        private void addEngineByApi( String engineGroupId, String engineArtifactId, String engineVersion,
+                                     Map<String, Artifact> providerArtifacts, Map<String, Artifact> testDependencies )
+        {
+            providerArtifacts.keySet().removeAll( testDependencies.keySet() );
+            for ( Artifact dep : resolve( engineGroupId, engineArtifactId, engineVersion, null, "jar" ) )
+            {
+                String key = dep.getGroupId() + ":" + dep.getArtifactId();
+                if ( !testDependencies.containsKey( key ) )
+                {
+                    providerArtifacts.put( key, dep );
+                }
+            }
+            alignVersions( providerArtifacts, testDependencies );
+        }
+
+        private void narrowProviderDependencies( String filterTestDependency,
+                                                 Map<String, Artifact> providerArtifacts,
+                                                 Map<String, Artifact> testDependencies )
+        {
+            Artifact engine = testDependencies.get( filterTestDependency );
+            String groupId = engine.getGroupId();
+            String artifactId = engine.getArtifactId();
+            String version = engine.getBaseVersion();
+            String classifier = engine.getClassifier();
+            String type = engine.getType();
+            for ( Artifact engineDep : resolve( groupId, artifactId, version, classifier, type ) )
+            {
+                providerArtifacts.remove( engineDep.getGroupId() + ":" + engineDep.getArtifactId() );
+                getConsoleLogger().debug( "Removed artifact " + engineDep
+                        + " from provider. Already appears in test classpath." );
+            }
+            alignVersions( providerArtifacts, testDependencies );
+        }
+
+        private void alignVersions( Map<String, Artifact> providerArtifacts,
+                                    Map<String, Artifact> referencedDependencies )
+        {
+            String version = referencedDependencies.get( "org.junit.platform:junit-platform-commons" ).getBaseVersion();
+            for ( Artifact launcherArtifact : resolve( PROVIDER_DEP_GID, PROVIDER_DEP_AID, version, null, "jar" ) )
+            {
+                String key = launcherArtifact.getGroupId() + ":" + launcherArtifact.getArtifactId();
+                if ( providerArtifacts.containsKey( key ) )
+                {
+                    providerArtifacts.put( key, launcherArtifact );
+                }
+            }
+        }
+
+        private Set<Artifact> resolve( String g, String a, String v, String c, String t )
+        {
+            ArtifactHandler handler = junitPlatformArtifact.getArtifactHandler();
+            Artifact artifact = new DefaultArtifact( g, a, v, null, t, c, handler );
+            getConsoleLogger().debug( "Resolving artifact " + g + ":" + a + ":" + v );
+            Set<Artifact> r = surefireDependencyResolver.resolveProjectArtifact( artifact ).getArtifacts();
+            getConsoleLogger().debug( "Resolved artifact " + g + ":" + a + ":" + v + " to " + r );
+            return r;
+        }
+
+        private boolean hasDependencyJupiterAPI( Map<String, Artifact> dependencies )
+        {
+            return dependencies.containsKey( "org.junit.jupiter:junit-jupiter-api" )
+                    || hasGroupArtifactId( "org.junit.jupiter", "junit-jupiter-api", getProject().getArtifact() );
+        }
+
+        private boolean hasDependencyPlatformEngine( Map<String, Artifact> dependencies )
+        {
+            return dependencies.containsKey( "org.junit.platform:junit-platform-engine" )
+                    || hasGroupArtifactId( "org.junit.platform", "junit-platform-engine", getProject().getArtifact() );
+        }
+
+        private boolean hasDependencyPlatformEngine( Collection<Artifact> dependencies )
+        {
+            if ( hasGroupArtifactId( "org.junit.platform", "junit-platform-engine", getProject().getArtifact() ) )
+            {
+                return true;
+            }
+
+            for ( Artifact dependency : dependencies )
+            {
+                if ( dependency.getGroupId().equals( "org.junit.platform" )
+                        && dependency.getArtifactId().equals( "junit-platform-engine" ) )
+                {
+                    return true;
+                }
+            }
+
+            return false;
         }
     }
 
@@ -2945,7 +3090,7 @@ public abstract class AbstractSurefireMojo
         public Set<Artifact> getProviderClasspath()
         {
             String version = surefireBooterArtifact.getBaseVersion();
-            return dependencyResolver.getProviderClasspath( "surefire-junit47", version );
+            return surefireDependencyResolver.getProviderClasspath( "surefire-junit47", version );
         }
     }
 
@@ -2993,7 +3138,7 @@ public abstract class AbstractSurefireMojo
         @Nonnull
         public Set<Artifact> getProviderClasspath()
         {
-            return dependencyResolver.addProviderToClasspath( getPluginArtifactMap(), getMojoArtifact(),
+            return surefireDependencyResolver.addProviderToClasspath( getPluginArtifactMap(), getMojoArtifact(),
                     getCommonArtifact(), getApiArtifact(), getLoggerApiArtifact() );
         }
     }
@@ -3176,7 +3321,6 @@ public abstract class AbstractSurefireMojo
         this.projectArtifactMap = projectArtifactMap;
     }
 
-
     public String getReportNameSuffix()
     {
         return reportNameSuffix;
@@ -3305,17 +3449,6 @@ public abstract class AbstractSurefireMojo
         this.junitArtifactName = junitArtifactName;
     }
 
-    public String getJunitPlatformArtifactName()
-    {
-        return junitPlatformArtifactName;
-    }
-
-    @SuppressWarnings( "UnusedDeclaration" )
-    public void setJunitPlatformArtifactName( String junitPlatformArtifactName )
-    {
-        this.junitPlatformArtifactName = junitPlatformArtifactName;
-    }
-
     public String getTestNGArtifactName()
     {
         return testNGArtifactName;
@@ -3423,6 +3556,17 @@ public abstract class AbstractSurefireMojo
         this.trimStackTrace = trimStackTrace;
     }
 
+    public List<ArtifactRepository> getProjectRemoteRepositories()
+    {
+        return projectRemoteRepositories;
+    }
+
+    @SuppressWarnings( "UnusedDeclaration" )
+    public void setProjectRemoteRepositories( List<ArtifactRepository> projectRemoteRepositories )
+    {
+        this.projectRemoteRepositories = projectRemoteRepositories;
+    }
+
     public List<ArtifactRepository> getRemoteRepositories()
     {
         return remoteRepositories;
@@ -3518,6 +3662,12 @@ public abstract class AbstractSurefireMojo
         this.dependenciesToScan = dependenciesToScan;
     }
 
+    @SuppressWarnings( "UnusedDeclaration" )
+    void setPluginDescriptor( PluginDescriptor pluginDescriptor )
+    {
+        this.pluginDescriptor = pluginDescriptor;
+    }
+
     public PluginDescriptor getPluginDescriptor()
     {
         return pluginDescriptor;
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
index cb63cc1..fea74fd 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
@@ -36,7 +36,7 @@ public interface ProviderInfo
     boolean isApplicable();
 
     @Nonnull
-    Set<Artifact> getProviderClasspath();
+    Set<Artifact> getProviderClasspath() throws MojoExecutionException;
 
     void addProviderProperties() throws MojoExecutionException;
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
index b255f38..170107c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
@@ -19,6 +19,7 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -29,20 +30,29 @@ import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
-import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
 import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
 import org.apache.maven.artifact.versioning.VersionRange;
 import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.repository.RepositorySystem;
+import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
+import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
+import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
+import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import static java.util.Collections.singletonList;
-import static org.apache.maven.artifact.Artifact.SCOPE_TEST;
+import static java.util.Arrays.asList;
+import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;
+import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE_PLUS_RUNTIME;
+import static org.apache.maven.artifact.Artifact.SCOPE_RUNTIME;
+import static org.apache.maven.artifact.ArtifactUtils.artifactMapByVersionlessId;
 import static org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
 
 /**
@@ -76,19 +86,27 @@ final class SurefireDependencyResolver
 
     private final ArtifactRepository localRepository;
 
-    private final List<ArtifactRepository> remoteRepositories;
+    private final List<ArtifactRepository> pluginRemoteRepositories;
+
+    private final List<ArtifactRepository> projectRemoteRepositories;
 
     private final String pluginName;
 
+    private final DependencyResolver depencencyResolver;
+
     SurefireDependencyResolver( RepositorySystem repositorySystem, ConsoleLogger log,
                                 ArtifactRepository localRepository,
-                                List<ArtifactRepository> remoteRepositories, String pluginName )
+                                List<ArtifactRepository> pluginRemoteRepositories,
+                                List<ArtifactRepository> projectRemoteRepositories, String pluginName,
+                                DependencyResolver depencencyResolver )
     {
         this.repositorySystem = repositorySystem;
         this.log = log;
         this.localRepository = localRepository;
-        this.remoteRepositories = remoteRepositories;
+        this.pluginRemoteRepositories = pluginRemoteRepositories;
+        this.projectRemoteRepositories = projectRemoteRepositories;
         this.pluginName = pluginName;
+        this.depencencyResolver = depencencyResolver;
     }
 
     static boolean isWithinVersionSpec( @Nullable Artifact artifact, @Nonnull String versionSpec )
@@ -115,23 +133,46 @@ final class SurefireDependencyResolver
         }
     }
 
-    ArtifactResolutionResult resolveArtifact( Artifact providerArtifact )
+    Set<Artifact> resolvePluginDependencies( ProjectBuildingRequest request, Collection<Dependency> pluginDependencies )
+            throws MojoExecutionException
     {
-        return resolveArtifact( providerArtifact, null );
+        try
+        {
+            Iterable<ArtifactResult> resolvedPluginDependencies = depencencyResolver.resolveDependencies( request,
+                    pluginDependencies, null, ScopeFilter.including( SCOPE_COMPILE, SCOPE_RUNTIME ) );
+
+            Set<Artifact> resolved = new LinkedHashSet<>();
+            for ( ArtifactResult resolvedPluginDependency : resolvedPluginDependencies )
+            {
+                resolved.add( resolvedPluginDependency.getArtifact() );
+            }
+            return resolved;
+        }
+        catch ( DependencyResolverException e )
+        {
+            throw new MojoExecutionException( e.getLocalizedMessage(), e );
+        }
     }
 
-    private ArtifactResolutionResult resolveArtifact( Artifact providerArtifact, @Nullable Artifact excludeArtifact )
+    ArtifactResolutionResult resolvePluginArtifact( Artifact artifact )
+    {
+        return resolveArtifact( artifact, pluginRemoteRepositories );
+    }
+
+    ArtifactResolutionResult resolveProjectArtifact( Artifact artifact )
+    {
+        return resolveArtifact( artifact, projectRemoteRepositories );
+    }
+
+    private ArtifactResolutionResult resolveArtifact( Artifact artifact, List<ArtifactRepository> repositories )
     {
         ArtifactResolutionRequest request = new ArtifactResolutionRequest()
-                                                    .setArtifact( providerArtifact )
-                                                    .setRemoteRepositories( remoteRepositories )
-                                                    .setLocalRepository( localRepository )
-                                                    .setResolveTransitively( true );
-        if ( excludeArtifact != null )
-        {
-            String pattern = excludeArtifact.getGroupId() + ":" + excludeArtifact.getArtifactId();
-            request.setCollectionFilter( new ExcludesArtifactFilter( singletonList( pattern ) ) );
-        }
+                .setArtifact( artifact )
+                .setLocalRepository( localRepository )
+                .setResolveTransitively( true )
+                .setCollectionFilter( new RuntimeArtifactFilter() )
+                .setRemoteRepositories( repositories );
+
         return repositorySystem.resolve( request );
     }
 
@@ -142,7 +183,7 @@ final class SurefireDependencyResolver
 
         Artifact providerArtifact = repositorySystem.createDependencyArtifact( provider );
 
-        ArtifactResolutionResult result = resolveArtifact( providerArtifact );
+        ArtifactResolutionResult result = resolvePluginArtifact( providerArtifact );
 
         if ( log.isDebugEnabled() )
         {
@@ -157,17 +198,23 @@ final class SurefireDependencyResolver
         return orderProviderArtifacts( result.getArtifacts() );
     }
 
+    @Nonnull
+    Map<String, Artifact> getProviderClasspathAsMap( String providerArtifactId, String providerVersion )
+    {
+        return artifactMapByVersionlessId( getProviderClasspath( providerArtifactId, providerVersion ) );
+    }
+
     Set<Artifact> addProviderToClasspath( Map<String, Artifact> pluginArtifactMap, Artifact mojoPluginArtifact,
                                           Artifact surefireCommon, Artifact surefireApi, Artifact surefireLoggerApi )
     {
         Set<Artifact> providerArtifacts = new LinkedHashSet<>();
-        ArtifactResolutionResult artifactResolutionResult = resolveArtifact( mojoPluginArtifact );
+        ArtifactResolutionResult artifactResolutionResult = resolvePluginArtifact( mojoPluginArtifact );
         for ( Artifact artifact : pluginArtifactMap.values() )
         {
             if ( !artifactResolutionResult.getArtifacts().contains( artifact ) )
             {
                 providerArtifacts.add( artifact );
-                for ( Artifact dependency : resolveArtifact( artifact ).getArtifacts() )
+                for ( Artifact dependency : resolvePluginArtifact( artifact ).getArtifacts() )
                 {
                     String groupId = dependency.getGroupId();
                     String artifactId = dependency.getArtifactId();
@@ -219,7 +266,19 @@ final class SurefireDependencyResolver
         dependency.setArtifactId( providerArtifactId );
         dependency.setVersion( providerVersion );
         dependency.setType( "jar" );
-        dependency.setScope( SCOPE_TEST );
         return dependency;
     }
+
+    static class RuntimeArtifactFilter implements ArtifactFilter
+    {
+        private static final Collection<String> SCOPES =
+                asList( SCOPE_COMPILE, SCOPE_COMPILE_PLUS_RUNTIME, SCOPE_RUNTIME );
+
+        @Override
+        public boolean include( Artifact artifact )
+        {
+            String scope = artifact.getScope();
+            return !artifact.isOptional() && ( scope == null || SCOPES.contains( scope ) );
+        }
+    }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java
index ee8fadb..3a37816 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java
@@ -20,14 +20,13 @@ package org.apache.maven.plugin.surefire;
  */
 
 import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.booter.Classpath;
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
 
 import static java.util.Collections.addAll;
 import static org.apache.maven.shared.utils.StringUtils.split;
@@ -38,45 +37,26 @@ final class TestClassPath
     private final File classesDirectory;
     private final File testClassesDirectory;
     private final String[] additionalClasspathElements;
-    private final ConsoleLogger logger;
 
     TestClassPath( Iterable<Artifact> artifacts,
                    File classesDirectory,
                    File testClassesDirectory,
-                   String[] additionalClasspathElements,
-                   ConsoleLogger logger )
+                   String[] additionalClasspathElements )
     {
         this.artifacts = artifacts;
         this.classesDirectory = classesDirectory;
         this.testClassesDirectory = testClassesDirectory;
         this.additionalClasspathElements = additionalClasspathElements;
-        this.logger = logger;
     }
 
-    void avoidArtifactDuplicates( Set<Artifact> providerArtifacts )
+    Map<String, Artifact> getTestDependencies()
     {
+        Map<String, Artifact> artifactMapping = new LinkedHashMap<>();
         for ( Artifact artifact : artifacts )
         {
-            Iterator<Artifact> it = providerArtifacts.iterator();
-            while ( it.hasNext() )
-            {
-                Artifact providerArtifact = it.next();
-                String classifier1 = providerArtifact.getClassifier();
-                String classifier2 = artifact.getClassifier();
-                if ( providerArtifact.getGroupId().equals( artifact.getGroupId() )
-                        && providerArtifact.getArtifactId().equals( artifact.getArtifactId() )
-                        && providerArtifact.getType().equals( artifact.getType() )
-                        && ( classifier1 == null ? classifier2 == null : classifier1.equals( classifier2 ) ) )
-                {
-                    it.remove();
-                    if ( logger.isDebugEnabled() )
-                    {
-                        logger.debug( "Removed artifact " + providerArtifact + " from provider. "
-                                + "Already appears in test classpath." );
-                    }
-                }
-            }
+            artifactMapping.put( artifact.getGroupId() + ":" + artifact.getArtifactId(), artifact );
         }
+        return artifactMapping;
     }
 
     Classpath toClasspath()
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
index 0f9ee80..2d268bc 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
@@ -108,7 +108,7 @@ public class AbstractSurefireMojoJava7PlusTest
 
         TestClassPath testClasspath =
                 new TestClassPath( asList( modular, nonModular, junit, hamcrest ), classesDir, testClassesDir,
-                        null, null );
+                        null );
 
         doReturn( testClasspath ).when( mojo, "generateTestClasspath" );
         doReturn( 1 ).when( mojo, "getEffectiveForkCount" );
@@ -171,11 +171,10 @@ public class AbstractSurefireMojoJava7PlusTest
 
         StartupConfiguration conf = invokeMethod( mojo, "newStartupConfigWithModularPath",
                 classLoaderConfiguration, providerClasspath, "org.asf.Provider", moduleInfo, scanResult,
-                "" );
+                "", testClasspath );
 
         verify( mojo, times( 1 ) ).effectiveIsEnableAssertions();
         verify( mojo, times( 1 ) ).isChildDelegation();
-        verifyPrivate( mojo, times( 1 ) ).invoke( "generateTestClasspath" );
         verify( mojo, times( 1 ) ).getEffectiveForkCount();
         verify( mojo, times( 1 ) ).getTestClassesDirectory();
         verify( scanResult, times( 1 ) ).getClasses();
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
index a6fc708..080f2b5 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
@@ -22,9 +22,22 @@ package org.apache.maven.plugin.surefire;
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.artifact.handler.ArtifactHandler;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
+import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
 import org.apache.maven.artifact.versioning.VersionRange;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.apache.maven.plugin.surefire.AbstractSurefireMojo.JUnitPlatformProviderInfo;
 import org.apache.maven.plugin.surefire.log.PluginConsoleLogger;
 import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.repository.RepositorySystem;
+import org.apache.maven.shared.artifact.filter.resolve.TransformableFilter;
+import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
+import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
 import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.StartupConfiguration;
@@ -33,7 +46,10 @@ import org.codehaus.plexus.logging.Logger;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
@@ -41,6 +57,7 @@ import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -50,13 +67,14 @@ import java.util.Set;
 
 import static java.io.File.separatorChar;
 import static java.util.Arrays.asList;
-import static java.util.Collections.singleton;
+import static java.util.Collections.*;
 import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
 import static org.apache.maven.artifact.versioning.VersionRange.createFromVersion;
 import static org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
 import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.fest.assertions.MapAssert.entry;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
@@ -247,7 +265,7 @@ public class AbstractSurefireMojoTest
         File classesDir = mockFile( "classes" );
         File testClassesDir = mockFile( "test-classes" );
         TestClassPath testClasspath =
-                new TestClassPath( asList( junit, hamcrest ), classesDir, testClassesDir, null, null );
+                new TestClassPath( asList( junit, hamcrest ), classesDir, testClassesDir, null );
 
         doReturn( testClasspath ).when( mojo, "generateTestClasspath" );
         doReturn( 1 ).when( mojo, "getEffectiveForkCount" );
@@ -267,11 +285,10 @@ public class AbstractSurefireMojoTest
         when( mojo.getConsoleLogger() ).thenReturn( new PluginConsoleLogger( logger ) );
 
         StartupConfiguration conf = invokeMethod( mojo, "newStartupConfigWithClasspath",
-                classLoaderConfiguration, providerArtifacts, "org.asf.Provider" );
+                classLoaderConfiguration, providerArtifacts, "org.asf.Provider", testClasspath );
 
         verify( mojo, times( 1 ) ).effectiveIsEnableAssertions();
         verify( mojo, times( 1 ) ).isChildDelegation();
-        verifyPrivate( mojo, times( 1 ) ).invoke( "generateTestClasspath" );
         verify( mojo, times( 1 ) ).getEffectiveForkCount();
         ArgumentCaptor<String> argument = ArgumentCaptor.forClass( String.class );
         verify( logger, times( 6 ) ).debug( argument.capture() );
@@ -360,9 +377,1214 @@ public class AbstractSurefireMojoTest
                 .isDirectory();
     }
 
+    @Test
+    public void shouldSmartlyResolveJUnit5ProviderWithJUnit4() throws Exception
+    {
+        MavenProject mavenProject = new MavenProject();
+        mavenProject.setArtifact( new DefaultArtifact( "dummy", "pom", createFromVersion( "1.0.0" ),
+                null, "jar", null, mock( ArtifactHandler.class ) ) );
+        mojo.setProject( mavenProject );
+
+        final VersionRange surefireVersion = createFromVersion( "1" );
+
+        Artifact junitPlatformArtifact = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Artifact testClasspathJUnit = new DefaultArtifact( "junit", "junit", createFromVersion( "4.12" ), null, "jar",
+                null, mock( ArtifactHandler.class ) );
+
+        Iterable<Artifact> testArtifacts = singleton( testClasspathJUnit );
+
+        File classesDirectory = new File( "target/classes" );
+
+        File testClassesDirectory = new File( "target/test-classes" );
+
+        TestClassPath testClasspathWrapper =
+                new TestClassPath( testArtifacts, classesDirectory, testClassesDirectory, null );
+
+        Artifact forkedBooter = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-booter", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        mojo.setPluginArtifactMap( singletonMap( "org.apache.maven.surefire:surefire-booter", forkedBooter ) );
+        mojo.setRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        mojo.setProjectRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        RepositorySystem repositorySystem = mock( RepositorySystem.class );
+        final Artifact surefireProvider = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        when( repositorySystem.createDependencyArtifact( any( Dependency.class ) ) ).thenAnswer( new Answer<Artifact>()
+        {
+            @Override
+            public Artifact answer( InvocationOnMock invocation )
+            {
+                Dependency provider = (Dependency) invocation.getArguments()[0];
+                assertThat( provider.getGroupId() ).isEqualTo( "org.apache.maven.surefire" );
+                assertThat( provider.getArtifactId() ).isEqualTo( "surefire-junit-platform" );
+                return surefireProvider;
+            }
+        } );
+        final ArtifactResolutionResult surefireProviderResolutionResult = mock( ArtifactResolutionResult.class );
+        final ArtifactResolutionResult junit4ResolutionResult = mock( ArtifactResolutionResult.class );
+        when( repositorySystem.resolve( any( ArtifactResolutionRequest.class ) ) )
+                .thenAnswer( new Answer<ArtifactResolutionResult>()
+                {
+                    @Override
+                    public ArtifactResolutionResult answer( InvocationOnMock invocation )
+                    {
+                        ArtifactResolutionRequest req = (ArtifactResolutionRequest) invocation.getArguments()[0];
+                        Artifact artifact = req.getArtifact();
+                        if ( artifact == surefireProvider )
+                        {
+                            return surefireProviderResolutionResult;
+                        }
+                        else if ( artifact.getGroupId().equals( "junit" )
+                                && artifact.getArtifactId().equals( "junit" )
+                                && artifact.getVersion().equals( "4.12" ) )
+                        {
+                            return junit4ResolutionResult;
+                        }
+                        else
+                        {
+                            fail();
+                            return null;
+                        }
+                    }
+                } );
+
+        DependencyResolver dependencyResolver = mock( DependencyResolver.class );
+        when( dependencyResolver.resolveDependencies( any( ProjectBuildingRequest.class ),
+                ArgumentMatchers.<Dependency>anyCollection(), isNull( Collection.class ),
+                any( TransformableFilter.class ) ) )
+                .thenAnswer( new Answer<Object>()
+                {
+                    @Override
+                    public Object answer( InvocationOnMock invocation )
+                    {
+                        Collection deps = (Collection) invocation.getArguments()[1];
+                        assertThat( deps ).isEmpty();
+                        return emptySet();
+                    }
+                } );
+
+        Artifact java5 = new DefaultArtifact( "org.apache.maven.surefire", "common-java5",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact launcher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact apiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact engine = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact commons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact opentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Set<Artifact> providerArtifacts = new HashSet<>();
+        providerArtifacts.add( surefireProvider );
+        providerArtifacts.add( java5 );
+        providerArtifacts.add( launcher );
+        providerArtifacts.add( apiguardian );
+        providerArtifacts.add( engine );
+        providerArtifacts.add( commons );
+        providerArtifacts.add( opentest4j );
+
+        when( surefireProviderResolutionResult.getArtifacts() )
+                .thenReturn( providerArtifacts );
+
+        Artifact junit = new DefaultArtifact( "junit", "junit",
+                createFromVersion( "4.12" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact hamcrest = new DefaultArtifact( "org.hamcrest", "hamcrest-core",
+                createFromVersion( "1.3" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Set<Artifact> junitArtifacts = new HashSet<>();
+        junitArtifacts.add( junit );
+        junitArtifacts.add( hamcrest );
+        when( junit4ResolutionResult.getArtifacts() )
+                .thenReturn( junitArtifacts );
+
+        mojo.setRepositorySystem( repositorySystem );
+        mojo.setLogger( mock( Logger.class ) );
+        mojo.setDependencyResolver( dependencyResolver );
+
+        invokeMethod( mojo, "setupStuff" );
+
+        MavenSession session = mock( MavenSession.class );
+        mojo.setSession( session );
+        when( session.getProjectBuildingRequest() )
+                .thenReturn( mock( ProjectBuildingRequest.class ) );
+
+        PluginDescriptor pluginDescriptor = mock( PluginDescriptor.class );
+        mojo.setPluginDescriptor( pluginDescriptor );
+        Plugin p = mock( Plugin.class );
+        when( pluginDescriptor.getPlugin() )
+                .thenReturn( p );
+        when( p.getDependencies() )
+                .thenReturn( Collections.<Dependency>emptyList() );
+
+        JUnitPlatformProviderInfo prov =
+                mojo.createJUnitPlatformProviderInfo( junitPlatformArtifact, testClasspathWrapper );
+
+        Artifact expectedProvider = new DefaultArtifact( "org.apache.maven.surefire", "surefire-junit-platform",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact expectedCommonJava5 = new DefaultArtifact( "org.apache.maven.surefire", "common-java5",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact expectedLauncher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact expectedApiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact expectedJUnit5Engine = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact expectedOpentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact expectedPlatformCommons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        assertThat( prov.getProviderClasspath() )
+                .hasSize( 7 )
+                .containsOnly( expectedProvider, expectedCommonJava5, expectedLauncher, expectedApiguardian,
+                        expectedJUnit5Engine, expectedOpentest4j, expectedPlatformCommons );
+
+        assertThat( testClasspathWrapper.getTestDependencies() )
+                .hasSize( 1 )
+                .includes( entry( "junit:junit", testClasspathJUnit ) );
+    }
+
+    @Test
+    public void shouldSmartlyResolveJUnit5ProviderWithVintage() throws Exception
+    {
+        MavenProject mavenProject = new MavenProject();
+        mavenProject.setArtifact( new DefaultArtifact( "dummy", "pom", createFromVersion( "1.0.0" ),
+                null, "jar", null, mock( ArtifactHandler.class ) ) );
+        mojo.setProject( mavenProject );
+
+        final VersionRange surefireVersion = createFromVersion( "1" );
+
+        Artifact junitPlatformArtifact = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathSomeTestArtifact = new DefaultArtifact( "third.party", "artifact",
+                createFromVersion( "1.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathVintage = new DefaultArtifact( "org.junit.vintage", "junit-vintage-engine",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathApiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathPlatformEng = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Artifact testClasspathJUnit4 = new DefaultArtifact( "junit", "junit",
+                createFromVersion( "4.12" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Artifact testClasspathHamcrest = new DefaultArtifact( "org.hamcrest", "hamcrest-core",
+                createFromVersion( "1.3" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathOpentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathCommons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Iterable<Artifact> testArtifacts = asList( testClasspathSomeTestArtifact, testClasspathVintage,
+                testClasspathApiguardian, testClasspathPlatformEng, testClasspathJUnit4, testClasspathHamcrest,
+                testClasspathOpentest4j, testClasspathCommons );
+
+        File classesDirectory = new File( "target/classes" );
+
+        File testClassesDirectory = new File( "target/test-classes" );
+
+        TestClassPath testClasspathWrapper =
+                new TestClassPath( testArtifacts, classesDirectory, testClassesDirectory, null );
+
+        Artifact forkedBooter = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-booter", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        mojo.setPluginArtifactMap( singletonMap( "org.apache.maven.surefire:surefire-booter", forkedBooter ) );
+        mojo.setRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        mojo.setProjectRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        RepositorySystem repositorySystem = mock( RepositorySystem.class );
+        final Artifact surefireProvider = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        when( repositorySystem.createDependencyArtifact( any( Dependency.class ) ) ).thenAnswer( new Answer<Artifact>()
+        {
+            @Override
+            public Artifact answer( InvocationOnMock invocation )
+            {
+                Dependency provider = (Dependency) invocation.getArguments()[0];
+                assertThat( provider.getGroupId() ).isEqualTo( "org.apache.maven.surefire" );
+                assertThat( provider.getArtifactId() ).isEqualTo( "surefire-junit-platform" );
+                return surefireProvider;
+            }
+        } );
+        final ArtifactResolutionResult surefireProviderResolutionResult = mock( ArtifactResolutionResult.class );
+        final ArtifactResolutionResult junit4ResolutionResult = mock( ArtifactResolutionResult.class );
+        when( repositorySystem.resolve( any( ArtifactResolutionRequest.class ) ) )
+                .thenAnswer( new Answer<ArtifactResolutionResult>()
+                {
+                    @Override
+                    public ArtifactResolutionResult answer( InvocationOnMock invocation )
+                    {
+                        ArtifactResolutionRequest req = (ArtifactResolutionRequest) invocation.getArguments()[0];
+                        Artifact resolvable = req.getArtifact();
+                        if ( resolvable == surefireProvider )
+                        {
+                            return surefireProviderResolutionResult;
+                        }
+                        else if ( "org.junit.platform".equals( resolvable.getGroupId() )
+                                && "junit-platform-engine".equals( resolvable.getArtifactId() )
+                                && "1.4.0".equals( resolvable.getVersion() ) )
+                        {
+                            return createVintageEngineResolutionResult();
+                        }
+                        else if ( "org.junit.platform".equals( resolvable.getGroupId() )
+                                && "junit-platform-launcher".equals( resolvable.getArtifactId() )
+                                && "1.4.0".equals( resolvable.getVersion() ) )
+                        {
+                            return createExpectedJUnitPlatformLauncherResolutionResult();
+                        }
+                        else
+                        {
+                            fail();
+                            return null;
+                        }
+                    }
+                } );
+
+        Artifact java5 = new DefaultArtifact( "org.apache.maven.surefire", "common-java5",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact launcher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact apiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact engine = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact commons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact opentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Set<Artifact> providerArtifacts = new HashSet<>();
+        providerArtifacts.add( surefireProvider );
+        providerArtifacts.add( java5 );
+        providerArtifacts.add( launcher );
+        providerArtifacts.add( apiguardian );
+        providerArtifacts.add( engine );
+        providerArtifacts.add( commons );
+        providerArtifacts.add( opentest4j );
+
+        when( surefireProviderResolutionResult.getArtifacts() )
+                .thenReturn( providerArtifacts );
+
+        Artifact junit = new DefaultArtifact( "junit", "junit",
+                createFromVersion( "4.12" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact hamcrest = new DefaultArtifact( "org.hamcrest", "hamcrest-core",
+                createFromVersion( "1.3" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Set<Artifact> junitArtifacts = new HashSet<>();
+        junitArtifacts.add( junit );
+        junitArtifacts.add( hamcrest );
+        when( junit4ResolutionResult.getArtifacts() )
+                .thenReturn( junitArtifacts );
+
+        mojo.setRepositorySystem( repositorySystem );
+        mojo.setLogger( mock( Logger.class ) );
+
+        invokeMethod( mojo, "setupStuff" );
+        JUnitPlatformProviderInfo prov =
+                mojo.createJUnitPlatformProviderInfo( junitPlatformArtifact, testClasspathWrapper );
+
+        Artifact expectedProvider = new DefaultArtifact( "org.apache.maven.surefire", "surefire-junit-platform",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact expectedCommonJava5 = new DefaultArtifact( "org.apache.maven.surefire", "common-java5",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact expectedLauncher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        assertThat( prov.getProviderClasspath() )
+                .hasSize( 3 )
+                .containsOnly( expectedProvider, expectedCommonJava5, expectedLauncher );
+
+        assertThat( testClasspathWrapper.getTestDependencies() )
+                .hasSize( 8 )
+                .includes( entry(  "third.party:artifact", testClasspathSomeTestArtifact ),
+                        entry(  "org.junit.vintage:junit-vintage-engine", testClasspathVintage ),
+                        entry(  "org.apiguardian:apiguardian-api", testClasspathApiguardian ),
+                        entry(  "org.junit.platform:junit-platform-engine", testClasspathPlatformEng ),
+                        entry(  "junit:junit", testClasspathJUnit4 ),
+                        entry(  "org.hamcrest:hamcrest-core", testClasspathHamcrest ),
+                        entry(  "org.opentest4j:opentest4j", testClasspathOpentest4j ),
+                        entry( "org.junit.platform:junit-platform-commons", testClasspathCommons ) );
+    }
+
+    @Test
+    public void shouldSmartlyResolveJUnit5ProviderWithJUnit5Commons() throws Exception
+    {
+        MavenProject mavenProject = new MavenProject();
+        mavenProject.setArtifact( new DefaultArtifact( "dummy", "pom", createFromVersion( "1.0.0" ),
+                null, "jar", null, mock( ArtifactHandler.class ) ) );
+        mojo.setProject( mavenProject );
+
+        final VersionRange surefireVersion = createFromVersion( "1" );
+
+        Artifact junitPlatformArtifact = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathSomeTestArtifact = new DefaultArtifact( "third.party", "artifact",
+                createFromVersion( "1.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathCommons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathApiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Iterable<Artifact> testArtifacts =
+                asList( testClasspathSomeTestArtifact, testClasspathApiguardian, testClasspathCommons );
+
+        File classesDirectory = new File( "target/classes" );
+
+        File testClassesDirectory = new File( "target/test-classes" );
+
+        TestClassPath testClasspathWrapper =
+                new TestClassPath( testArtifacts, classesDirectory, testClassesDirectory, null );
+
+        Artifact forkedBooter = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-booter", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        mojo.setPluginArtifactMap( singletonMap( "org.apache.maven.surefire:surefire-booter", forkedBooter ) );
+        mojo.setRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        mojo.setProjectRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        RepositorySystem repositorySystem = mock( RepositorySystem.class );
+        final Artifact surefireProvider = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        when( repositorySystem.createDependencyArtifact( any( Dependency.class ) ) ).thenAnswer( new Answer<Artifact>()
+        {
+            @Override
+            public Artifact answer( InvocationOnMock invocation )
+            {
+                Dependency provider = (Dependency) invocation.getArguments()[0];
+                assertThat( provider.getGroupId() ).isEqualTo( "org.apache.maven.surefire" );
+                assertThat( provider.getArtifactId() ).isEqualTo( "surefire-junit-platform" );
+                return surefireProvider;
+            }
+        } );
+
+        when( repositorySystem.resolve( any( ArtifactResolutionRequest.class ) ) )
+                .thenAnswer( new Answer<ArtifactResolutionResult>()
+                {
+                    @Override
+                    public ArtifactResolutionResult answer( InvocationOnMock invocation )
+                    {
+                        ArtifactResolutionRequest req = (ArtifactResolutionRequest) invocation.getArguments()[0];
+                        Artifact resolvable = req.getArtifact();
+                        if ( resolvable == surefireProvider )
+                        {
+                            return createSurefireProviderResolutionResult( surefireVersion );
+                        }
+                        else
+                        {
+                            fail();
+                            return null;
+                        }
+                    }
+                } );
+
+        DependencyResolver dependencyResolver = mock( DependencyResolver.class );
+        when( dependencyResolver.resolveDependencies( any( ProjectBuildingRequest.class ),
+                ArgumentMatchers.<Dependency>anyCollection(), isNull( Collection.class ),
+                any( TransformableFilter.class ) ) )
+                .thenAnswer( new Answer<Object>()
+                {
+                    @Override
+                    public Object answer( InvocationOnMock invocation )
+                    {
+                        Collection deps = (Collection) invocation.getArguments()[1];
+                        assertThat( deps ).isEmpty();
+                        return emptySet();
+                    }
+                } );
+
+        mojo.setRepositorySystem( repositorySystem );
+        mojo.setLogger( mock( Logger.class ) );
+        mojo.setDependencyResolver( dependencyResolver );
+
+        invokeMethod( mojo, "setupStuff" );
+
+        MavenSession session = mock( MavenSession.class );
+        mojo.setSession( session );
+        when( session.getProjectBuildingRequest() )
+                .thenReturn( mock( ProjectBuildingRequest.class ) );
+
+        PluginDescriptor pluginDescriptor = mock( PluginDescriptor.class );
+        mojo.setPluginDescriptor( pluginDescriptor );
+        Plugin p = mock( Plugin.class );
+        when( pluginDescriptor.getPlugin() )
+                .thenReturn( p );
+        when( p.getDependencies() )
+                .thenReturn( Collections.<Dependency>emptyList() );
+
+        JUnitPlatformProviderInfo prov =
+                mojo.createJUnitPlatformProviderInfo( junitPlatformArtifact, testClasspathWrapper );
+        Set<Artifact> resolvedProviderArtifacts = prov.getProviderClasspath();
+
+        Artifact provider = new DefaultArtifact( "org.apache.maven.surefire", "surefire-junit-platform",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact java5 = new DefaultArtifact( "org.apache.maven.surefire", "common-java5",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact launcher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact engine = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact opentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        assertThat( resolvedProviderArtifacts )
+                .hasSize( 5 )
+                .containsOnly( provider, java5, launcher, engine, opentest4j );
+
+        assertThat( testClasspathWrapper.getTestDependencies() )
+                .hasSize( 3 )
+                .includes( entry( "third.party:artifact", testClasspathSomeTestArtifact ),
+                        entry( "org.junit.platform:junit-platform-commons", testClasspathCommons ),
+                        entry( "org.apiguardian:apiguardian-api", testClasspathApiguardian ) );
+    }
+
+    @Test
+    public void shouldSmartlyResolveJUnit5ProviderWithJUnit5Engine() throws Exception
+    {
+        MavenProject mavenProject = new MavenProject();
+        mavenProject.setArtifact( new DefaultArtifact( "dummy", "pom", createFromVersion( "1.0.0" ),
+                null, "jar", null, mock( ArtifactHandler.class ) ) );
+        mojo.setProject( mavenProject );
+
+        final VersionRange surefireVersion = createFromVersion( "1" );
+
+        Artifact junitPlatformArtifact = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathSomeTestArtifact = new DefaultArtifact( "third.party", "artifact",
+                createFromVersion( "1.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathJUnit5 = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathApiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathCommons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathOpentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Iterable<Artifact> testArtifacts = asList( testClasspathSomeTestArtifact, testClasspathJUnit5,
+                testClasspathApiguardian, testClasspathCommons, testClasspathOpentest4j );
+
+        File classesDirectory = new File( "target/classes" );
+
+        File testClassesDirectory = new File( "target/test-classes" );
+
+        TestClassPath testClasspathWrapper =
+                new TestClassPath( testArtifacts, classesDirectory, testClassesDirectory, null );
+
+        Artifact forkedBooter = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-booter", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        mojo.setPluginArtifactMap( singletonMap( "org.apache.maven.surefire:surefire-booter", forkedBooter ) );
+        mojo.setRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        mojo.setProjectRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        RepositorySystem repositorySystem = mock( RepositorySystem.class );
+        final Artifact surefireProvider = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        when( repositorySystem.createDependencyArtifact( any( Dependency.class ) ) ).thenAnswer( new Answer<Artifact>()
+        {
+            @Override
+            public Artifact answer( InvocationOnMock invocation )
+            {
+                Dependency provider = (Dependency) invocation.getArguments()[0];
+                assertThat( provider.getGroupId() ).isEqualTo( "org.apache.maven.surefire" );
+                assertThat( provider.getArtifactId() ).isEqualTo( "surefire-junit-platform" );
+                return surefireProvider;
+            }
+        } );
+
+        when( repositorySystem.resolve( any( ArtifactResolutionRequest.class ) ) )
+                .thenAnswer( new Answer<ArtifactResolutionResult>()
+                {
+                    @Override
+                    public ArtifactResolutionResult answer( InvocationOnMock invocation )
+                    {
+                        ArtifactResolutionRequest req = (ArtifactResolutionRequest) invocation.getArguments()[0];
+                        Artifact resolvable = req.getArtifact();
+                        if ( resolvable == surefireProvider )
+                        {
+                            return createSurefireProviderResolutionResult( surefireVersion );
+                        }
+                        else if ( resolvable.equals( testClasspathJUnit5 )  )
+                        {
+                            return createResolutionResult( testClasspathJUnit5, testClasspathApiguardian,
+                                    testClasspathCommons, testClasspathOpentest4j );
+                        }
+                        else if ( "org.junit.platform".equals( resolvable.getGroupId() )
+                                && "junit-platform-launcher".equals( resolvable.getArtifactId() )
+                                && "1.4.0".equals( resolvable.getVersion() ) )
+                        {
+                            return createExpectedJUnitPlatformLauncherResolutionResult();
+                        }
+                        else
+                        {
+                            fail();
+                            return null;
+                        }
+                    }
+                } );
+
+        mojo.setRepositorySystem( repositorySystem );
+        mojo.setLogger( mock( Logger.class ) );
+
+        invokeMethod( mojo, "setupStuff" );
+        JUnitPlatformProviderInfo prov =
+                mojo.createJUnitPlatformProviderInfo( junitPlatformArtifact, testClasspathWrapper );
+        Set<Artifact> resolvedProviderArtifacts = prov.getProviderClasspath();
+
+        Artifact java5 = new DefaultArtifact( "org.apache.maven.surefire", "common-java5",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact launcher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        assertThat( resolvedProviderArtifacts )
+                .hasSize( 3 )
+                .containsOnly( surefireProvider, java5, launcher );
+
+        assertThat( testClasspathWrapper.getTestDependencies() )
+                .hasSize( 5 )
+                .includes( entry( "third.party:artifact", testClasspathSomeTestArtifact ),
+                        entry( "org.junit.platform:junit-platform-engine", testClasspathJUnit5 ),
+                        entry( "org.apiguardian:apiguardian-api", testClasspathApiguardian ),
+                        entry( "org.junit.platform:junit-platform-commons", testClasspathCommons ),
+                        entry( "org.opentest4j:opentest4j", testClasspathOpentest4j ) );
+    }
+
+    @Test
+    public void shouldSmartlyResolveJUnit5ProviderWithJupiterApi() throws Exception
+    {
+        MavenProject mavenProject = new MavenProject();
+        mavenProject.setArtifact( new DefaultArtifact( "dummy", "pom", createFromVersion( "1.0.0" ),
+                null, "jar", null, mock( ArtifactHandler.class ) ) );
+        mojo.setProject( mavenProject );
+
+        final VersionRange surefireVersion = createFromVersion( "1" );
+
+        Artifact junitPlatformArtifact = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathSomeTestArtifact = new DefaultArtifact( "third.party", "artifact",
+                createFromVersion( "1.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathJupiterApi = new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-api",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathApiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathCommons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathOpentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Iterable<Artifact> testArtifacts = asList( testClasspathSomeTestArtifact, testClasspathJupiterApi,
+                testClasspathApiguardian, testClasspathCommons, testClasspathOpentest4j );
+
+        File classesDirectory = new File( "target/classes" );
+
+        File testClassesDirectory = new File( "target/test-classes" );
+
+        TestClassPath testClasspathWrapper =
+                new TestClassPath( testArtifacts, classesDirectory, testClassesDirectory, null );
+
+        Artifact forkedBooter = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-booter", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        mojo.setPluginArtifactMap( singletonMap( "org.apache.maven.surefire:surefire-booter", forkedBooter ) );
+        mojo.setRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        mojo.setProjectRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        RepositorySystem repositorySystem = mock( RepositorySystem.class );
+        final Artifact surefireProvider = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        when( repositorySystem.createDependencyArtifact( any( Dependency.class ) ) ).thenAnswer( new Answer<Artifact>()
+        {
+            @Override
+            public Artifact answer( InvocationOnMock invocation )
+            {
+                Dependency provider = (Dependency) invocation.getArguments()[0];
+                assertThat( provider.getGroupId() ).isEqualTo( "org.apache.maven.surefire" );
+                assertThat( provider.getArtifactId() ).isEqualTo( "surefire-junit-platform" );
+                return surefireProvider;
+            }
+        } );
+
+        when( repositorySystem.resolve( any( ArtifactResolutionRequest.class ) ) )
+                .thenAnswer( new Answer<ArtifactResolutionResult>()
+                {
+                    @Override
+                    public ArtifactResolutionResult answer( InvocationOnMock invocation )
+                    {
+                        ArtifactResolutionRequest req = (ArtifactResolutionRequest) invocation.getArguments()[0];
+                        Artifact resolvable = req.getArtifact();
+                        if ( resolvable == surefireProvider )
+                        {
+                            return createSurefireProviderResolutionResult( surefireVersion );
+                        }
+                        else if ( resolvable.equals( testClasspathJupiterApi )  )
+                        {
+                            return createResolutionResult( testClasspathJupiterApi, testClasspathApiguardian,
+                                    testClasspathCommons, testClasspathOpentest4j );
+                        }
+                        else if ( "org.junit.platform".equals( resolvable.getGroupId() )
+                                && "junit-platform-launcher".equals( resolvable.getArtifactId() )
+                                && "1.4.0".equals( resolvable.getVersion() ) )
+                        {
+                            return createExpectedJUnitPlatformLauncherResolutionResult();
+                        }
+                        else if ( "org.junit.jupiter".equals( resolvable.getGroupId() )
+                                && "junit-jupiter-engine".equals( resolvable.getArtifactId() )
+                                && "5.4.0".equals( resolvable.getVersion() ) )
+                        {
+                            return createJupiterEngineResolutionResult();
+                        }
+                        else
+                        {
+                            fail();
+                            return null;
+                        }
+                    }
+                } );
+
+        DependencyResolver dependencyResolver = mock( DependencyResolver.class );
+        when( dependencyResolver.resolveDependencies( any( ProjectBuildingRequest.class ),
+                ArgumentMatchers.<Dependency>anyCollection(), isNull( Collection.class ),
+                any( TransformableFilter.class ) ) )
+                .thenAnswer( new Answer<Object>()
+                {
+                    @Override
+                    public Object answer( InvocationOnMock invocation )
+                    {
+                        Collection deps = (Collection) invocation.getArguments()[1];
+                        assertThat( deps ).isEmpty();
+                        return emptySet();
+                    }
+                } );
+
+        mojo.setRepositorySystem( repositorySystem );
+        mojo.setLogger( mock( Logger.class ) );
+        mojo.setDependencyResolver( dependencyResolver );
+
+        invokeMethod( mojo, "setupStuff" );
+
+        MavenSession session = mock( MavenSession.class );
+        mojo.setSession( session );
+        when( session.getProjectBuildingRequest() )
+                .thenReturn( mock( ProjectBuildingRequest.class ) );
+
+        PluginDescriptor pluginDescriptor = mock( PluginDescriptor.class );
+        mojo.setPluginDescriptor( pluginDescriptor );
+        Plugin p = mock( Plugin.class );
+        when( pluginDescriptor.getPlugin() )
+                .thenReturn( p );
+        when( p.getDependencies() )
+                .thenReturn( Collections.<Dependency>emptyList() );
+
+        JUnitPlatformProviderInfo prov =
+                mojo.createJUnitPlatformProviderInfo( junitPlatformArtifact, testClasspathWrapper );
+        Set<Artifact> resolvedProviderArtifacts = prov.getProviderClasspath();
+
+        Artifact java5 = new DefaultArtifact( "org.apache.maven.surefire", "common-java5",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact launcher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact jupiterEngine = new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-engine",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact platformEngine = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        assertThat( resolvedProviderArtifacts )
+                .hasSize( 5 )
+                .containsOnly( surefireProvider, java5, launcher, jupiterEngine, platformEngine );
+
+        assertThat( testClasspathWrapper.getTestDependencies() )
+                .hasSize( 5 )
+                .includes( entry( "third.party:artifact", testClasspathSomeTestArtifact ),
+                        entry( "org.junit.jupiter:junit-jupiter-api", testClasspathJupiterApi ),
+                        entry( "org.apiguardian:apiguardian-api", testClasspathApiguardian ),
+                        entry( "org.junit.platform:junit-platform-commons", testClasspathCommons ),
+                        entry( "org.opentest4j:opentest4j", testClasspathOpentest4j ) );
+    }
+
+    @Test
+    public void shouldSmartlyResolveJUnit5ProviderWithJupiterEngine() throws Exception
+    {
+        MavenProject mavenProject = new MavenProject();
+        mavenProject.setArtifact( new DefaultArtifact( "dummy", "pom", createFromVersion( "1.0.0" ),
+                null, "jar", null, mock( ArtifactHandler.class ) ) );
+        mojo.setProject( mavenProject );
+
+        final VersionRange surefireVersion = createFromVersion( "1" );
+
+        Artifact junitPlatformArtifact = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathSomeTestArtifact = new DefaultArtifact( "third.party", "artifact",
+                createFromVersion( "1.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathJupiterEngine = new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-engine",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathPlatformEngine = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathJupiterApi = new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-api",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathApiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathCommons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathOpentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Iterable<Artifact> testArtifacts = asList( testClasspathSomeTestArtifact, testClasspathJupiterEngine,
+                testClasspathPlatformEngine, testClasspathJupiterApi, testClasspathApiguardian, testClasspathCommons,
+                testClasspathOpentest4j );
+
+        File classesDirectory = new File( "target/classes" );
+
+        File testClassesDirectory = new File( "target/test-classes" );
+
+        TestClassPath testClasspathWrapper =
+                new TestClassPath( testArtifacts, classesDirectory, testClassesDirectory, null );
+
+        Artifact forkedBooter = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-booter", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        mojo.setPluginArtifactMap( singletonMap( "org.apache.maven.surefire:surefire-booter", forkedBooter ) );
+        mojo.setRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        mojo.setProjectRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        RepositorySystem repositorySystem = mock( RepositorySystem.class );
+        final Artifact surefireProvider = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        when( repositorySystem.createDependencyArtifact( any( Dependency.class ) ) ).thenAnswer( new Answer<Artifact>()
+        {
+            @Override
+            public Artifact answer( InvocationOnMock invocation )
+            {
+                Dependency provider = (Dependency) invocation.getArguments()[0];
+                assertThat( provider.getGroupId() ).isEqualTo( "org.apache.maven.surefire" );
+                assertThat( provider.getArtifactId() ).isEqualTo( "surefire-junit-platform" );
+                return surefireProvider;
+            }
+        } );
+
+        when( repositorySystem.resolve( any( ArtifactResolutionRequest.class ) ) )
+                .thenAnswer( new Answer<ArtifactResolutionResult>()
+                {
+                    @Override
+                    public ArtifactResolutionResult answer( InvocationOnMock invocation )
+                    {
+                        ArtifactResolutionRequest req = (ArtifactResolutionRequest) invocation.getArguments()[0];
+                        Artifact resolvable = req.getArtifact();
+                        if ( resolvable == surefireProvider )
+                        {
+                            return createSurefireProviderResolutionResult( surefireVersion );
+                        }
+                        else if ( resolvable.equals( testClasspathJupiterApi )  )
+                        {
+                            return createResolutionResult( testClasspathJupiterApi, testClasspathApiguardian,
+                                    testClasspathCommons, testClasspathOpentest4j );
+                        }
+                        else if ( "org.junit.platform".equals( resolvable.getGroupId() )
+                                && "junit-platform-launcher".equals( resolvable.getArtifactId() )
+                                && "1.4.0".equals( resolvable.getVersion() ) )
+                        {
+                            return createExpectedJUnitPlatformLauncherResolutionResult();
+                        }
+                        else if ( "org.junit.platform".equals( resolvable.getGroupId() )
+                                && "junit-platform-engine".equals( resolvable.getArtifactId() )
+                                && "1.4.0".equals( resolvable.getVersion() ) )
+                        {
+                            return createVintageEngineResolutionResult();
+                        }
+                        else
+                        {
+                            fail();
+                            return null;
+                        }
+                    }
+                } );
+
+        mojo.setRepositorySystem( repositorySystem );
+        mojo.setLogger( mock( Logger.class ) );
+
+        invokeMethod( mojo, "setupStuff" );
+        JUnitPlatformProviderInfo prov =
+                mojo.createJUnitPlatformProviderInfo( junitPlatformArtifact, testClasspathWrapper );
+        Set<Artifact> resolvedProviderArtifacts = prov.getProviderClasspath();
+
+        Artifact java5 = new DefaultArtifact( "org.apache.maven.surefire", "common-java5",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact launcher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        assertThat( resolvedProviderArtifacts )
+                .hasSize( 3 )
+                .containsOnly( surefireProvider, java5, launcher );
+
+        assertThat( testClasspathWrapper.getTestDependencies() )
+                .hasSize( 7 )
+                .includes( entry( "third.party:artifact", testClasspathSomeTestArtifact ),
+                        entry( "org.junit.jupiter:junit-jupiter-engine", testClasspathJupiterEngine ),
+                        entry( "org.junit.platform:junit-platform-engine", testClasspathPlatformEngine ),
+                        entry( "org.junit.jupiter:junit-jupiter-api", testClasspathJupiterApi ),
+                        entry( "org.apiguardian:apiguardian-api", testClasspathApiguardian ),
+                        entry( "org.junit.platform:junit-platform-commons", testClasspathCommons ),
+                        entry( "org.opentest4j:opentest4j", testClasspathOpentest4j ) );
+    }
+
+    @Test
+    public void shouldSmartlyResolveJUnit5ProviderWithJupiterEngineInPluginDependencies() throws Exception
+    {
+
+        final VersionRange surefireVersion = createFromVersion( "1" );
+
+        final Artifact plugin = new DefaultArtifact( "org.apache.maven.surefire", "maven-surefire-plugin",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact forkedBooter = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-booter", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact pluginDepJupiterEngine = new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-engine",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact pluginDepPlatformEngine = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact pluginDepJupiterApi = new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-api",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact pluginDepApiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact pluginDepCommons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact pluginDepOpentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Map<String, Artifact> pluginDependencies = new HashMap<>();
+        pluginDependencies.put( "org.apache.maven.surefire:maven-surefire-plugin", plugin );
+        pluginDependencies.put( "org.apache.maven.surefire:surefire-booter", forkedBooter );
+        pluginDependencies.put( "org.junit.jupiter:junit-jupiter-engine", pluginDepJupiterEngine );
+        pluginDependencies.put( "org.junit.platform:junit-platform-engine", pluginDepPlatformEngine );
+        pluginDependencies.put( "org.junit.jupiter:junit-jupiter-api", pluginDepJupiterApi );
+        pluginDependencies.put( "org.apiguardian:apiguardian-api", pluginDepApiguardian );
+        pluginDependencies.put( "org.junit.platform:junit-platform-commons", pluginDepCommons );
+        pluginDependencies.put( "org.opentest4j:opentest4j", pluginDepOpentest4j );
+        mojo.setPluginArtifactMap( pluginDependencies );
+
+        MavenProject mavenProject = new MavenProject();
+        mavenProject.setArtifact( new DefaultArtifact( "dummy", "pom", createFromVersion( "1.0.0" ),
+                null, "jar", null, mock( ArtifactHandler.class ) ) );
+        mojo.setProject( mavenProject );
+
+        Artifact junitPlatformArtifact = new DefaultArtifact( "g", "a",
+                createFromVersion( "0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathSomeTestArtifact = new DefaultArtifact( "third.party", "artifact",
+                createFromVersion( "1.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathJupiterApi = new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-api",
+                createFromVersion( "5.3.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathApiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathCommons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        final Artifact testClasspathOpentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Iterable<Artifact> testArtifacts = asList( testClasspathSomeTestArtifact, testClasspathJupiterApi,
+                testClasspathApiguardian, testClasspathCommons, testClasspathOpentest4j );
+
+        File classesDirectory = new File( "target/classes" );
+
+        File testClassesDirectory = new File( "target/test-classes" );
+
+        TestClassPath testClasspathWrapper =
+                new TestClassPath( testArtifacts, classesDirectory, testClassesDirectory, null );
+
+        mojo.setRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        mojo.setProjectRemoteRepositories( Collections.<ArtifactRepository>emptyList() );
+        RepositorySystem repositorySystem = mock( RepositorySystem.class );
+        final Artifact surefireProvider = new DefaultArtifact( "org.apache.maven.surefire",
+                "surefire-junit-platform", surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        when( repositorySystem.createDependencyArtifact( any( Dependency.class ) ) ).thenAnswer( new Answer<Artifact>()
+        {
+            @Override
+            public Artifact answer( InvocationOnMock invocation )
+            {
+                Dependency provider = (Dependency) invocation.getArguments()[0];
+                assertThat( provider.getGroupId() ).isEqualTo( "org.apache.maven.surefire" );
+                assertThat( provider.getArtifactId() ).isEqualTo( "surefire-junit-platform" );
+                return surefireProvider;
+            }
+        } );
+
+        when( repositorySystem.resolve( any( ArtifactResolutionRequest.class ) ) )
+                .thenAnswer( new Answer<ArtifactResolutionResult>()
+                {
+                    @Override
+                    public ArtifactResolutionResult answer( InvocationOnMock invocation )
+                    {
+                        ArtifactResolutionRequest req = (ArtifactResolutionRequest) invocation.getArguments()[0];
+                        Artifact resolvable = req.getArtifact();
+                        if ( resolvable == surefireProvider )
+                        {
+                            return createSurefireProviderResolutionResult( surefireVersion );
+                        }
+                        else if ( "org.junit.platform".equals( resolvable.getGroupId() )
+                                && "junit-platform-launcher".equals( resolvable.getArtifactId() )
+                                && "1.4.0".equals( resolvable.getVersion() ) )
+                        {
+                            return createExpectedJUnitPlatformLauncherResolutionResult();
+                        }
+                        else if ( "org.apache.maven.surefire".equals( resolvable.getGroupId() )
+                                && "maven-surefire-plugin".equals( resolvable.getArtifactId() )
+                                && "1".equals( resolvable.getVersion() ) )
+                        {
+                            Set<Artifact> pluginItself = new HashSet<>();
+                            pluginItself.add( plugin );
+                            pluginItself.add( forkedBooter );
+                            ArtifactResolutionResult launcherResolutionResult = mock( ArtifactResolutionResult.class );
+                            when( launcherResolutionResult.getArtifacts() )
+                                    .thenReturn( pluginItself );
+                            return launcherResolutionResult;
+                        }
+                        else
+                        {
+                            fail();
+                            return null;
+                        }
+                    }
+                } );
+
+        DependencyResolver dependencyResolver = mock( DependencyResolver.class );
+        when( dependencyResolver.resolveDependencies( any( ProjectBuildingRequest.class ),
+                ArgumentMatchers.<Dependency>anyCollection(), isNull( Collection.class ),
+                any( TransformableFilter.class ) ) )
+                .thenAnswer( new Answer<Object>()
+                {
+                    @Override
+                    public Object answer( InvocationOnMock invocation )
+                    {
+                        Collection deps = (Collection) invocation.getArguments()[1];
+                        assertThat( deps ).hasSize( 1 );
+                        Dependency resolvable = (Dependency) deps.iterator().next();
+                        if ( "org.junit.jupiter".equals( resolvable.getGroupId() )
+                                && "junit-jupiter-engine".equals( resolvable.getArtifactId() )
+                                && "5.4.0".equals( resolvable.getVersion() ) )
+                        {
+                            Set<ArtifactResult> resolvedPluginDeps = new HashSet<>();
+                            resolvedPluginDeps.add( toArtifactResult( pluginDepJupiterEngine ) );
+                            resolvedPluginDeps.add( toArtifactResult( pluginDepPlatformEngine ) );
+                            resolvedPluginDeps.add( toArtifactResult( pluginDepJupiterApi ) );
+                            resolvedPluginDeps.add( toArtifactResult( pluginDepApiguardian ) );
+                            resolvedPluginDeps.add( toArtifactResult( pluginDepCommons ) );
+                            resolvedPluginDeps.add( toArtifactResult( pluginDepOpentest4j ) );
+                            return resolvedPluginDeps;
+                        }
+                        else
+                        {
+                            fail();
+                            return null;
+                        }
+                    }
+                } );
+
+        mojo.setRepositorySystem( repositorySystem );
+        mojo.setLogger( mock( Logger.class ) );
+        mojo.setDependencyResolver( dependencyResolver );
+
+        invokeMethod( mojo, "setupStuff" );
+
+        JUnitPlatformProviderInfo prov =
+                mojo.createJUnitPlatformProviderInfo( junitPlatformArtifact, testClasspathWrapper );
+
+        MavenSession session = mock( MavenSession.class );
+        mojo.setSession( session );
+        when( session.getProjectBuildingRequest() )
+                .thenReturn( mock( ProjectBuildingRequest.class ) );
+
+        PluginDescriptor pluginDescriptor = mock( PluginDescriptor.class );
+        mojo.setPluginDescriptor( pluginDescriptor );
+        Plugin p = mock( Plugin.class );
+        when( pluginDescriptor.getPlugin() )
+                .thenReturn( p );
+        List<Dependency> directPluginDependencies = toDependencies( pluginDepJupiterEngine );
+        when( p.getDependencies() )
+                .thenReturn( directPluginDependencies );
+
+        Set<Artifact> resolvedProviderArtifacts = prov.getProviderClasspath();
+
+        Artifact java5 = new DefaultArtifact( "org.apache.maven.surefire", "common-java5",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact launcher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact jupiterEngine = new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-engine",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact platformEngine = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        assertThat( resolvedProviderArtifacts )
+                .hasSize( 5 )
+                .containsOnly( surefireProvider, java5, launcher, jupiterEngine, platformEngine );
+
+        assertThat( testClasspathWrapper.getTestDependencies() )
+                .hasSize( 5 )
+                .includes( entry( "third.party:artifact", testClasspathSomeTestArtifact ),
+                        entry( "org.junit.jupiter:junit-jupiter-api", testClasspathJupiterApi ),
+                        entry( "org.apiguardian:apiguardian-api", testClasspathApiguardian ),
+                        entry( "org.junit.platform:junit-platform-commons", testClasspathCommons ),
+                        entry( "org.opentest4j:opentest4j", testClasspathOpentest4j ) );
+    }
+
+    private static ArtifactResolutionResult createJUnitPlatformLauncherResolutionResult(
+            Artifact junit5Engine, Artifact apiguardian, Artifact commons, Artifact opentest4j )
+    {
+        ArtifactResolutionResult launcherResolutionResult = mock( ArtifactResolutionResult.class );
+        Set<Artifact> resolvedLauncherArtifacts = new HashSet<>();
+        Artifact launcher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                commons.getVersionRange(), null, "jar", null, mock( ArtifactHandler.class ) );
+        resolvedLauncherArtifacts.add( launcher );
+        resolvedLauncherArtifacts.add( apiguardian );
+        resolvedLauncherArtifacts.add( junit5Engine );
+        resolvedLauncherArtifacts.add( commons );
+        resolvedLauncherArtifacts.add( opentest4j );
+        resolvedLauncherArtifacts.remove( null );
+        when( launcherResolutionResult.getArtifacts() )
+                .thenReturn( resolvedLauncherArtifacts );
+        return launcherResolutionResult;
+    }
+
+    private static ArtifactResolutionResult createVintageEngineResolutionResult()
+    {
+        ArtifactResolutionResult launcherResolutionResult = mock( ArtifactResolutionResult.class );
+        Set<Artifact> resolvedLauncherArtifacts = new HashSet<>();
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.junit.vintage", "junit-vintage-engine",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-api",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "junit", "junit",
+                createFromVersion( "4.12" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.hamcrest", "hamcrest-core",
+                createFromVersion( "1.3" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        when( launcherResolutionResult.getArtifacts() )
+                .thenReturn( resolvedLauncherArtifacts );
+        return launcherResolutionResult;
+    }
+
+    private static ArtifactResolutionResult createJupiterEngineResolutionResult()
+    {
+        ArtifactResolutionResult launcherResolutionResult = mock( ArtifactResolutionResult.class );
+        Set<Artifact> resolvedLauncherArtifacts = new HashSet<>();
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-engine",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.junit.jupiter", "junit-jupiter-api",
+                createFromVersion( "5.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        resolvedLauncherArtifacts.add( new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) ) );
+        when( launcherResolutionResult.getArtifacts() )
+                .thenReturn( resolvedLauncherArtifacts );
+        return launcherResolutionResult;
+    }
+
+    private static ArtifactResolutionResult createExpectedJUnitPlatformLauncherResolutionResult()
+    {
+        Artifact engine = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact commons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.4.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact apiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact opentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        return createJUnitPlatformLauncherResolutionResult( engine, apiguardian, commons, opentest4j );
+    }
+
+    private static ArtifactResolutionResult createResolutionResult( Artifact... artifacts )
+    {
+        ArtifactResolutionResult testClasspathCommonsResolutionResult = mock( ArtifactResolutionResult.class );
+        Set<Artifact> resolvedCommonsArtifacts = new HashSet<>();
+        Collections.addAll( resolvedCommonsArtifacts, artifacts );
+        when( testClasspathCommonsResolutionResult.getArtifacts() )
+                .thenReturn( resolvedCommonsArtifacts );
+        return testClasspathCommonsResolutionResult;
+    }
+
+    private static ArtifactResolutionResult createSurefireProviderResolutionResult( VersionRange surefireVersion )
+    {
+        ArtifactResolutionResult surefirePlatformResolutionResult = mock( ArtifactResolutionResult.class );
+
+        Artifact provider = new DefaultArtifact( "org.apache.maven.surefire", "surefire-junit-platform",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact java5 = new DefaultArtifact( "org.apache.maven.surefire", "common-java5",
+                surefireVersion, null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact launcher = new DefaultArtifact( "org.junit.platform", "junit-platform-launcher",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact apiguardian = new DefaultArtifact( "org.apiguardian", "apiguardian-api",
+                createFromVersion( "1.0.0" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact engine = new DefaultArtifact( "org.junit.platform", "junit-platform-engine",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact commons = new DefaultArtifact( "org.junit.platform", "junit-platform-commons",
+                createFromVersion( "1.3.2" ), null, "jar", null, mock( ArtifactHandler.class ) );
+        Artifact opentest4j = new DefaultArtifact( "org.opentest4j", "opentest4j",
+                createFromVersion( "1.1.1" ), null, "jar", null, mock( ArtifactHandler.class ) );
+
+        Set<Artifact> providerArtifacts = new HashSet<>();
+        providerArtifacts.add( provider );
+        providerArtifacts.add( java5 );
+        providerArtifacts.add( launcher );
+        providerArtifacts.add( apiguardian );
+        providerArtifacts.add( engine );
+        providerArtifacts.add( commons );
+        providerArtifacts.add( opentest4j );
+
+        when( surefirePlatformResolutionResult.getArtifacts() )
+                .thenReturn( providerArtifacts );
+        return surefirePlatformResolutionResult;
+    }
+
     public static class Mojo
             extends AbstractSurefireMojo
     {
+        private JUnitPlatformProviderInfo createJUnitPlatformProviderInfo( Artifact providerArtifact,
+                                                                           TestClassPath testClasspathWrapper )
+        {
+            return new JUnitPlatformProviderInfo( providerArtifact, testClasspathWrapper );
+        }
+
+        @Override
+        protected void logDebugOrCliShowErrors( String s )
+        {
+            // do nothing
+        }
+
         @Override
         protected String getPluginName()
         {
@@ -726,7 +1948,8 @@ public class AbstractSurefireMojoTest
         @Override
         protected Artifact getMojoArtifact()
         {
-            return null;
+            return new DefaultArtifact( "org.apache.maven.surefire", "maven-surefire-plugin", createFromVersion( "1" ),
+                    null, "jar", null, mock( ArtifactHandler.class ) );
         }
     }
 
@@ -736,4 +1959,38 @@ public class AbstractSurefireMojoTest
         when( f.getAbsolutePath() ).thenReturn( absolutePath );
         return f;
     }
+
+    private static Dependency toDependency( Artifact artifact )
+    {
+        Dependency dependency = new Dependency();
+        dependency.setGroupId( artifact.getGroupId() );
+        dependency.setArtifactId( artifact.getArtifactId() );
+        dependency.setVersion( artifact.getBaseVersion() );
+        dependency.setType( "jar" );
+        return dependency;
+    }
+
+    private static List<Dependency> toDependencies( Artifact... artifacts )
+    {
+        List<Dependency> dependencies = new ArrayList<>();
+        for ( Artifact artifact : artifacts )
+        {
+            dependencies.add( toDependency( artifact) );
+        }
+        return dependencies;
+    }
+
+    private static ArtifactResult toArtifactResult( final Artifact artifact )
+    {
+        class AR implements ArtifactResult
+        {
+
+            @Override
+            public Artifact getArtifact()
+            {
+                return artifact;
+            }
+        }
+        return new AR();
+    }
 }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireDependencyResolverTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireDependencyResolverTest.java
index ab57aef..fdb1bbe 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireDependencyResolverTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireDependencyResolverTest.java
@@ -145,9 +145,9 @@ public class SurefireDependencyResolverTest
                 } );
 
         SurefireDependencyResolver surefireDependencyResolver =
-                new SurefireDependencyResolver( repositorySystem, null, null, null, null );
+                new SurefireDependencyResolver( repositorySystem, null, null, null, null, null, null );
 
-        ArtifactResolutionResult actualResult = surefireDependencyResolver.resolveArtifact( provider );
+        ArtifactResolutionResult actualResult = surefireDependencyResolver.resolvePluginArtifact( provider );
 
         assertThat( actualResult )
                 .isSameAs( expectedResult );
@@ -243,7 +243,7 @@ public class SurefireDependencyResolverTest
         ConsoleLogger log = mock( ConsoleLogger.class );
 
         SurefireDependencyResolver surefireDependencyResolver =
-                new SurefireDependencyResolver( repositorySystem, log, null, null, null );
+                new SurefireDependencyResolver( repositorySystem, log, null, null, null, null, null );
 
         when( log.isDebugEnabled() )
                 .thenReturn( true );
@@ -349,7 +349,7 @@ public class SurefireDependencyResolverTest
                 } );
 
         SurefireDependencyResolver surefireDependencyResolver =
-                new SurefireDependencyResolver( repositorySystem, null, null, null, null );
+                new SurefireDependencyResolver( repositorySystem, null, null, null, null, null, null );
 
         Map<String, Artifact> pluginArtifactsMapping = new HashMap<>();
         pluginArtifactsMapping.put( plugin.getGroupId() + ":" + plugin.getArtifactId(), plugin );
diff --git a/maven-surefire-plugin/pom.xml b/maven-surefire-plugin/pom.xml
index 8f1503d..ac34a2b 100644
--- a/maven-surefire-plugin/pom.xml
+++ b/maven-surefire-plugin/pom.xml
@@ -50,6 +50,11 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.maven.plugin-tools</groupId>
             <artifactId>maven-plugin-annotations</artifactId>
             <scope>provided</scope>
diff --git a/maven-surefire-plugin/src/site/apt/examples/junit-platform.apt.vm b/maven-surefire-plugin/src/site/apt/examples/junit-platform.apt.vm
index 4258387..1a11987 100644
--- a/maven-surefire-plugin/src/site/apt/examples/junit-platform.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/examples/junit-platform.apt.vm
@@ -40,7 +40,7 @@ Using JUnit 5 Platform
     <dependency>
         <groupId>org.junit.jupiter</groupId>
         <artifactId>junit-jupiter-engine</artifactId>
-        <version>5.3.2</version>
+        <version>5.4.0</version>
         <scope>test</scope>
     </dependency>
     [...]
@@ -63,7 +63,7 @@ Using JUnit 5 Platform
     <dependency>
         <groupId>org.junit.vintage</groupId>
         <artifactId>junit-vintage-engine</artifactId>
-        <version>5.3.2</version>
+        <version>5.4.0</version>
         <scope>test</scope>
     </dependency>
     [...]
@@ -74,6 +74,112 @@ Using JUnit 5 Platform
   see the {{{https://junit.org/junit5/}JUnit 5 web site}}
   and the {{{https://junit.org/junit5/docs/current/user-guide/}JUnit 5 User Guide}}.
 
+* Smart Resolution of Jupiter Engine and Vintage Engine for JUnit4 (since plugin version 3.0.0-M4)
+
+  JUnit5 API artifact and your test sources become isolated from engine.
+
+  Normally the developer does not want to access internal classes of JUnit5 engine (e.g. <<<5.4.0>>>). In this example
+  the POM has only Jupiter API dependency in test classpath.
+
++---+
+<dependencies>
+    [...]
+    <dependency>
+        <groupId>org.junit.jupiter</groupId>
+        <artifactId>junit-jupiter-api</artifactId>
+        <version>5.4.0</version>
+        <scope>test</scope>
+    </dependency>
+    [...]
+</dependencies>
+...
+<build>
+    <plugins>
+        [...]
+        <plugin>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>${project.artifactId}</artifactId>
+            <version>${project.version}</version>
+            [... configuration or goals and executions ...]
+        </plugin>
+        [...]
+    </plugins>
+</build>
+...
++---+
+
+  In the following example the engine artifact appears in plugin dependencies and the engine is resolved by the plugin
+  and downloaded from a remote repository for plugins. You may want to update the version of engine with fixed bugs in
+  <<<5.3.2>>> but the API version <<<5.3.0>>> stays intact!
+
++---+
+<dependencies>
+    [...]
+    <dependency>
+        <groupId>org.junit.jupiter</groupId>
+        <artifactId>junit-jupiter-api</artifactId>
+        <version>5.3.0</version>
+        <scope>test</scope>
+    </dependency>
+    [...]
+</dependencies>
+...
+<build>
+    <plugins>
+        [...]
+        <plugin>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>${project.artifactId}</artifactId>
+            <version>${project.version}</version>
+            <dependencies>
+                <dependency>
+                    <groupId>org.junit.jupiter</groupId>
+                    <artifactId>junit-jupiter-engine</artifactId>
+                    <version>5.3.2</version>
+                </dependency>
+            </dependencies>
+        </plugin>
+        [...]
+    </plugins>
+</build>
+...
++---+
+
+
+  Similar with JUnit4 in test dependencies of your project POM. The Vintage engine artifact is in plugin dependencies.
+
++---+
+<dependencies>
+    [...]
+    <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+        <version>4.12</version>
+        <scope>test</scope>
+    </dependency>
+    [...]
+</dependencies>
+...
+<build>
+    <plugins>
+        ...
+        <plugin>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>${project.artifactId}</artifactId>
+            <version>${project.version}</version>
+            <dependencies>
+                <dependency>
+                    <groupId>org.junit.vintage</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                    <version>5.4.0</version>
+                </dependency>
+            </dependencies>
+        </plugin>
+    </plugins>
+</build>
+...
++---+
+
 * Provider Selection
 
    If nothing is configured, Surefire detects which JUnit version to use by the following algorithm:
diff --git a/maven-surefire-report-plugin/pom.xml b/maven-surefire-report-plugin/pom.xml
index 4fee9b4..18da79f 100644
--- a/maven-surefire-report-plugin/pom.xml
+++ b/maven-surefire-report-plugin/pom.xml
@@ -59,11 +59,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
-            <artifactId>maven-model</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.maven</groupId>
             <artifactId>maven-plugin-api</artifactId>
             <!--<scope>provided</scope>-->
         </dependency>
diff --git a/pom.xml b/pom.xml
index 5f841c4..ebba975 100644
--- a/pom.xml
+++ b/pom.xml
@@ -191,16 +191,53 @@
         <groupId>org.apache.maven</groupId>
         <artifactId>maven-toolchain</artifactId>
         <version>3.0-alpha-2</version>
+        <exclusions>
+          <exclusion>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-compat</artifactId>
+          </exclusion>
+        </exclusions>
       </dependency>
       <dependency>
         <groupId>org.apache.maven.shared</groupId>
         <artifactId>maven-common-artifact-filters</artifactId>
-        <version>3.0.1</version>
+        <version>3.1.0</version>
         <exclusions>
           <exclusion>
             <groupId>org.apache.maven.shared</groupId>
             <artifactId>maven-plugin-testing-harness</artifactId>
           </exclusion>
+          <exclusion>
+            <groupId>org.apache.maven.shared</groupId>
+            <artifactId>maven-shared-utils</artifactId>
+          </exclusion>
+        </exclusions>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.shared</groupId>
+        <artifactId>maven-artifact-transfer</artifactId>
+        <version>0.10.1</version>
+        <exclusions>
+          <exclusion>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-artifact</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-component-annotations</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-utils</artifactId>
+          </exclusion>
         </exclusions>
       </dependency>
       <dependency>
diff --git a/surefire-api/pom.xml b/surefire-api/pom.xml
index e751eae..eee3879 100644
--- a/surefire-api/pom.xml
+++ b/surefire-api/pom.xml
@@ -82,8 +82,8 @@
               </artifactSet>
               <relocations>
                 <relocation>
-                  <pattern>org.apache.maven.shared</pattern>
-                  <shadedPattern>org.apache.maven.surefire.shade.api.org.apache.maven.shared</shadedPattern>
+                  <pattern>org.apache.maven.shared.utils</pattern>
+                  <shadedPattern>org.apache.maven.surefire.shade.api.org.apache.maven.shared.utils</shadedPattern>
                 </relocation>
               </relocations>
             </configuration>
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java
index 6147c2f..af8f366 100644
--- a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java
@@ -59,13 +59,13 @@ public class JUnitPlatformEnginesIT
     @Parameters(name = "{0}")
     public static Iterable<Object[]> regexVersions()
     {
-        ArrayList<Object[]> args = new ArrayList<Object[]>();
+        ArrayList<Object[]> args = new ArrayList<>();
         args.add( new Object[] { "1.0.3", "5.0.3", "1.0.0", "1.0.0" } );
         args.add( new Object[] { "1.1.1", "5.1.1", "1.0.0", "1.0.0" } );
         args.add( new Object[] { "1.2.0", "5.2.0", "1.1.0", "1.0.0" } );
         args.add( new Object[] { "1.3.2", "5.3.2", "1.1.1", "1.0.0" } );
         args.add( new Object[] { "1.4.0-SNAPSHOT", "5.4.0-SNAPSHOT", "1.1.1", "1.0.0" } );
-        args.add( new Object[] { "1.4.0-RC1", "5.4.0-RC1", "1.1.1", "1.0.0" } );
+        args.add( new Object[] { "1.4.0-RC2", "5.4.0-RC2", "1.1.1", "1.0.0" } );
         return args;
     }
 
@@ -124,7 +124,7 @@ public class JUnitPlatformEnginesIT
                 regex( toRegex( "*surefire-api-*.jar*" ) ),
                 regex( toRegex( "*surefire-logger-api-*.jar*" ) ),
                 regex( toRegex( "*common-java5-*.jar*" ) ),
-                regex( toRegex( "*junit-platform-launcher-1.3.2.jar*" ) )
+                regex( toRegex( "*junit-platform-launcher-" + platform + ".jar*" ) )
         ) );
 
         lines = validator.loadLogLines( startsWith( "[DEBUG] boot(compact) classpath" ) );
@@ -148,7 +148,7 @@ public class JUnitPlatformEnginesIT
                 regex( toRegex( "*opentest4j-" + opentest + ".jar*" ) ),
                 regex( toRegex( "*junit-jupiter-api-" + jupiter + ".jar*" ) ),
                 regex( toRegex( "*surefire-junit-platform-*.jar*" ) ),
-                regex( toRegex(  "*junit-platform-launcher-1.3.2.jar*" ) )
+                regex( toRegex(  "*junit-platform-launcher-" + platform + ".jar*" ) )
         ) );
     }
 
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1585IT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1585IT.java
new file mode 100644
index 0000000..12e175f
--- /dev/null
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1585IT.java
@@ -0,0 +1,54 @@
+package org.apache.maven.surefire.its.jiras;
+
+/*
+ * 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.SurefireJUnit4IntegrationTestCase;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaVersion;
+
+public class Surefire1585IT
+        extends SurefireJUnit4IntegrationTestCase
+{
+    @Before
+    public void setUp()
+    {
+        assumeJavaVersion( 1.8d );
+    }
+
+    @Test
+    public void shouldRunWithJupiterApi()
+    {
+        unpack( "surefire-1585-jupiter-api" )
+                .debugLogging()
+                .executeTest()
+                .verifyErrorFree( 1 );
+    }
+
+    @Test
+    public void shouldRunWithVintage()
+    {
+        unpack( "surefire-1585-junit4-vintage" )
+                .debugLogging()
+                .executeTest()
+                .verifyErrorFree( 1 );
+    }
+}
diff --git a/surefire-its/src/test/resources/surefire-1585-junit4-vintage/pom.xml b/surefire-its/src/test/resources/surefire-1585-junit4-vintage/pom.xml
new file mode 100644
index 0000000..7bdf148
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1585-junit4-vintage/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.maven.plugins.surefire</groupId>
+    <artifactId>surefire-1585-junit4-vintage-it</artifactId>
+    <version>1.0</version>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <junit.vintage.version>5.4.0</junit.vintage.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${surefire.version}</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.junit.vintage</groupId>
+                        <artifactId>junit-vintage-engine</artifactId>
+                        <version>${junit.vintage.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java b/surefire-its/src/test/resources/surefire-1585-junit4-vintage/src/test/java/JUnit4Test.java
similarity index 64%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
copy to surefire-its/src/test/resources/surefire-1585-junit4-vintage/src/test/java/JUnit4Test.java
index cb63cc1..d0c1ff4 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
+++ b/surefire-its/src/test/resources/surefire-1585-junit4-vintage/src/test/java/JUnit4Test.java
@@ -1,5 +1,3 @@
-package org.apache.maven.plugin.surefire;
-
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -19,24 +17,12 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.MojoExecutionException;
-
-import javax.annotation.Nonnull;
-import java.util.Set;
+import org.junit.Test;
 
-/**
- * @author Kristian Rosenvold
- */
-public interface ProviderInfo
+public class JUnit4Test
 {
-    @Nonnull
-    String getProviderName();
-
-    boolean isApplicable();
-
-    @Nonnull
-    Set<Artifact> getProviderClasspath();
-
-    void addProviderProperties() throws MojoExecutionException;
+    @Test
+    public void test()
+    {
+    }
 }
diff --git a/surefire-its/src/test/resources/surefire-1585-jupiter-api/pom.xml b/surefire-its/src/test/resources/surefire-1585-jupiter-api/pom.xml
new file mode 100644
index 0000000..652ae49
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1585-jupiter-api/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.maven.plugins.surefire</groupId>
+    <artifactId>surefire-1585-jupiter-api-it</artifactId>
+    <version>1.0</version>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <junit.jupiter.version>5.4.0</junit.jupiter.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit.jupiter.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>
+</project>
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java b/surefire-its/src/test/resources/surefire-1585-jupiter-api/src/test/java/JupiterTest.java
similarity index 64%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
copy to surefire-its/src/test/resources/surefire-1585-jupiter-api/src/test/java/JupiterTest.java
index cb63cc1..398141c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
+++ b/surefire-its/src/test/resources/surefire-1585-jupiter-api/src/test/java/JupiterTest.java
@@ -1,5 +1,3 @@
-package org.apache.maven.plugin.surefire;
-
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -19,24 +17,16 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.MojoExecutionException;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import javax.annotation.Nonnull;
-import java.util.Set;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
 
-/**
- * @author Kristian Rosenvold
- */
-public interface ProviderInfo
+class JupiterTest
 {
-    @Nonnull
-    String getProviderName();
-
-    boolean isApplicable();
-
-    @Nonnull
-    Set<Artifact> getProviderClasspath();
-
-    void addProviderProperties() throws MojoExecutionException;
+    @Test
+    void test( TestInfo info )
+    {
+        assertEquals( "test(TestInfo)", info.getDisplayName(), "display name mismatch" );
+    }
 }
diff --git a/surefire-providers/common-java5/pom.xml b/surefire-providers/common-java5/pom.xml
index 361e1d6..ba44a98 100644
--- a/surefire-providers/common-java5/pom.xml
+++ b/surefire-providers/common-java5/pom.xml
@@ -57,8 +57,8 @@
               </artifactSet>
               <relocations>
                 <relocation>
-                  <pattern>org.apache.maven.shared</pattern>
-                  <shadedPattern>org.apache.maven.surefire.shade.java5.org.apache.maven.shared</shadedPattern>
+                  <pattern>org.apache.maven.shared.utils</pattern>
+                  <shadedPattern>org.apache.maven.surefire.shade.java5.org.apache.maven.shared.utils</shadedPattern>
                 </relocation>
               </relocations>
             </configuration>
diff --git a/surefire-report-parser/pom.xml b/surefire-report-parser/pom.xml
index a700523..8c3f767 100644
--- a/surefire-report-parser/pom.xml
+++ b/surefire-report-parser/pom.xml
@@ -85,8 +85,8 @@
               </artifactSet>
               <relocations>
                 <relocation>
-                  <pattern>org.apache.maven.shared</pattern>
-                  <shadedPattern>org.apache.maven.surefire.shade.report.org.apache.maven.shared</shadedPattern>
+                  <pattern>org.apache.maven.shared.utils</pattern>
+                  <shadedPattern>org.apache.maven.surefire.shade.report.org.apache.maven.shared.utils</shadedPattern>
                 </relocation>
               </relocations>
             </configuration>


Re: [maven-surefire] branch master updated: [SUREFIRE-1585] Align JUnit Platform version at runtime

Posted by Enrico Olivelli <eo...@gmail.com>.
This is a great feature!



Il ven 1 mar 2019, 20:12 <ti...@apache.org> ha scritto:

> This is an automated email from the ASF dual-hosted git repository.
>
> tibordigana pushed a commit to branch master
> in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
>
>
> The following commit(s) were added to refs/heads/master by this push:
>      new de50626  [SUREFIRE-1585] Align JUnit Platform version at runtime
> de50626 is described below
>
> commit de506262225c43e43be4fd64cf25940e5a807aaa
> Author: tibordigana <ti...@apache.org>
> AuthorDate: Fri Feb 1 06:03:27 2019 +0100
>
>     [SUREFIRE-1585] Align JUnit Platform version at runtime
> ---
>  maven-failsafe-plugin/pom.xml                      |   13 +-
>  maven-surefire-common/pom.xml                      |   23 +-
>  .../plugin/surefire/AbstractSurefireMojo.java      |  352 ++++--
>  .../apache/maven/plugin/surefire/ProviderInfo.java |    2 +-
>  .../surefire/SurefireDependencyResolver.java       |  103 +-
>  .../maven/plugin/surefire/TestClassPath.java       |   34 +-
>  .../AbstractSurefireMojoJava7PlusTest.java         |    5 +-
>  .../plugin/surefire/AbstractSurefireMojoTest.java  | 1271
> +++++++++++++++++++-
>  .../surefire/SurefireDependencyResolverTest.java   |    8 +-
>  maven-surefire-plugin/pom.xml                      |    5 +
>  .../src/site/apt/examples/junit-platform.apt.vm    |  110 +-
>  maven-surefire-report-plugin/pom.xml               |    5 -
>  pom.xml                                            |   39 +-
>  surefire-api/pom.xml                               |    4 +-
>  .../maven/surefire/its/JUnitPlatformEnginesIT.java |    8 +-
>  .../maven/surefire/its/jiras/Surefire1585IT.java   |   54 +
>  .../resources/surefire-1585-junit4-vintage/pom.xml |   61 +
>  .../src/test/java/JUnit4Test.java                  |   26 +-
>  .../resources/surefire-1585-jupiter-api/pom.xml    |   54 +
>  .../src/test/java/JupiterTest.java                 |   28 +-
>  surefire-providers/common-java5/pom.xml            |    4 +-
>  surefire-report-parser/pom.xml                     |    4 +-
>  22 files changed, 1970 insertions(+), 243 deletions(-)
>
> diff --git a/maven-failsafe-plugin/pom.xml b/maven-failsafe-plugin/pom.xml
> index c947b54..31dcfbf 100644
> --- a/maven-failsafe-plugin/pom.xml
> +++ b/maven-failsafe-plugin/pom.xml
> @@ -60,6 +60,11 @@
>              <classifier>site-source</classifier>
>          </dependency>
>          <dependency>
> +            <groupId>org.apache.maven</groupId>
> +            <artifactId>maven-core</artifactId>
> +            <scope>provided</scope>
> +        </dependency>
> +        <dependency>
>              <groupId>org.apache.maven.plugin-tools</groupId>
>              <artifactId>maven-plugin-annotations</artifactId>
>              <scope>provided</scope>
> @@ -208,16 +213,16 @@
>                              </artifactSet>
>                              <relocations>
>                                  <relocation>
> -
> <pattern>org.apache.maven.shared</pattern>
> -                                    <shadedPattern>org.apache.
> maven.surefire.shade.org.apache.maven.shared</shadedPattern>
> +
> <pattern>org.apache.maven.shared.utils</pattern>
> +
> <shadedPattern>org.apache.maven.surefire.shade.failsafe.org.apache.maven.shared.utils</shadedPattern>
>                                  </relocation>
>                                  <relocation>
>                                      <pattern>org.apache.commons.io
> </pattern>
> -                                    <shadedPattern>
> org.apache.maven.surefire.shade.org.apache.commons.io</shadedPattern>
> +                                    <shadedPattern>
> org.apache.maven.surefire.shade.failsafe.org.apache.commons.io
> </shadedPattern>
>                                  </relocation>
>                                  <relocation>
>
>  <pattern>org.apache.commons.lang3</pattern>
> -                                    <shadedPattern>org.apache.
> maven.surefire.shade.org.apache.commons.lang3</shadedPattern>
> +
> <shadedPattern>org.apache.maven.surefire.shade.failsafe.org.apache.commons.lang3</shadedPattern>
>                                  </relocation>
>                              </relocations>
>                          </configuration>
> diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml
> index 57adfa9..770642e 100644
> --- a/maven-surefire-common/pom.xml
> +++ b/maven-surefire-common/pom.xml
> @@ -53,27 +53,12 @@
>              <scope>provided</scope>
>          </dependency>
>          <dependency>
> -            <groupId>org.apache.maven</groupId>
> -            <artifactId>maven-plugin-api</artifactId>
> -            <scope>provided</scope>
> -        </dependency>
> -        <dependency>
>              <groupId>org.apache.maven.plugin-tools</groupId>
>              <artifactId>maven-plugin-annotations</artifactId>
>              <scope>provided</scope>
>          </dependency>
>          <dependency>
>              <groupId>org.apache.maven</groupId>
> -            <artifactId>maven-artifact</artifactId>
> -            <scope>provided</scope>
> -        </dependency>
> -        <dependency>
> -            <groupId>org.apache.maven</groupId>
> -            <artifactId>maven-model</artifactId>
> -            <scope>provided</scope>
> -        </dependency>
> -        <dependency>
> -            <groupId>org.apache.maven</groupId>
>              <artifactId>maven-toolchain</artifactId>
>          </dependency>
>          <dependency>
> @@ -81,6 +66,10 @@
>              <artifactId>maven-common-artifact-filters</artifactId>
>          </dependency>
>          <dependency>
> +            <groupId>org.apache.maven.shared</groupId>
> +            <artifactId>maven-artifact-transfer</artifactId>
> +        </dependency>
> +        <dependency>
>              <groupId>org.codehaus.plexus</groupId>
>              <artifactId>plexus-java</artifactId>
>          </dependency>
> @@ -165,8 +154,8 @@
>                              </artifactSet>
>                              <relocations>
>                                  <relocation>
> -
> <pattern>org.apache.maven.shared</pattern>
> -
> <shadedPattern>org.apache.maven.surefire.shade.common.org.apache.maven.shared</shadedPattern>
> +
> <pattern>org.apache.maven.shared.utils</pattern>
> +
> <shadedPattern>org.apache.maven.surefire.shade.common.org.apache.maven.shared.utils</shadedPattern>
>                                  </relocation>
>                                  <relocation>
>                                      <pattern>org.apache.commons.io
> </pattern>
> 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 7c76ef1..ba7f1e0 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
> @@ -21,8 +21,12 @@ package org.apache.maven.plugin.surefire;
>   */
>
>  import org.apache.maven.artifact.Artifact;
> +import org.apache.maven.artifact.DefaultArtifact;
> +import org.apache.maven.artifact.handler.ArtifactHandler;
>  import org.apache.maven.artifact.repository.ArtifactRepository;
> +import org.apache.maven.model.Dependency;
>  import org.apache.maven.plugins.annotations.Component;
> +import org.apache.maven.project.ProjectBuildingRequest;
>  import org.apache.maven.repository.RepositorySystem;
>  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
>  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
> @@ -50,6 +54,7 @@ import
> org.apache.maven.plugin.surefire.util.DirectoryScanner;
>  import org.apache.maven.plugins.annotations.Parameter;
>  import org.apache.maven.project.MavenProject;
>  import
> org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
> +import
> org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
>  import org.apache.maven.shared.utils.io.FileUtils;
>  import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
>  import org.apache.maven.surefire.booter.Classpath;
> @@ -90,6 +95,7 @@ import java.math.BigDecimal;
>  import java.nio.file.Files;
>  import java.nio.file.Paths;
>  import java.util.ArrayList;
> +import java.util.Collection;
>  import java.util.Collections;
>  import java.util.Enumeration;
>  import java.util.HashMap;
> @@ -112,6 +118,7 @@ import static java.util.Collections.singletonList;
>  import static java.util.Collections.singletonMap;
>  import static org.apache.commons.lang3.StringUtils.substringBeforeLast;
>  import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
> +import static
> org.apache.maven.artifact.ArtifactUtils.artifactMapByVersionlessId;
>  import static
> org.apache.maven.plugin.surefire.SurefireDependencyResolver.isWithinVersionSpec;
>  import static
> org.apache.maven.plugin.surefire.util.DependencyScanner.filter;
>  import static
> org.apache.maven.plugin.surefire.SurefireHelper.replaceThreadNumberPlaceholders;
> @@ -478,15 +485,6 @@ public abstract class AbstractSurefireMojo
>      private String junitArtifactName;
>
>      /**
> -     * Allows you to specify the name of the JUnit Platform artifact.
> -     * If not set, {@code org.junit.platform:junit-platform-engine} will
> be used.
> -     *
> -     * @since 2.22.0
> -     */
> -    @Parameter( property = "junitPlatformArtifactName", defaultValue =
> "org.junit.platform:junit-platform-engine" )
> -    private String junitPlatformArtifactName;
> -
> -    /**
>       * Allows you to specify the name of the TestNG artifact. If not set,
> {@code org.testng:testng} will be used.
>       *
>       * @since 2.3.1
> @@ -663,6 +661,9 @@ public abstract class AbstractSurefireMojo
>      @Parameter( defaultValue = "${project.pluginArtifactRepositories}",
> readonly = true, required = true )
>      private List<ArtifactRepository> remoteRepositories;
>
> +    @Parameter( defaultValue = "${project.remoteArtifactRepositories}",
> required = true, readonly = true )
> +    private List<ArtifactRepository> projectRemoteRepositories;
> +
>      /**
>       * Flag to disable the generation of report files in xml format.
>       *
> @@ -737,6 +738,9 @@ public abstract class AbstractSurefireMojo
>      @Component
>      private RepositorySystem repositorySystem;
>
> +    @Component
> +    private DependencyResolver dependencyResolver;
> +
>      private Artifact surefireBooterArtifact;
>
>      private Toolchain toolchain;
> @@ -798,7 +802,7 @@ public abstract class AbstractSurefireMojo
>       */
>      protected Artifact getMojoArtifact()
>      {
> -        return pluginDescriptor.getPluginArtifact();
> +        return getPluginDescriptor().getPluginArtifact();
>      }
>
>      private String getDefaultExcludes()
> @@ -806,7 +810,7 @@ public abstract class AbstractSurefireMojo
>          return "**/*$*";
>      }
>
> -    private SurefireDependencyResolver dependencyResolver;
> +    private SurefireDependencyResolver surefireDependencyResolver;
>
>      private TestListResolver specificTests;
>
> @@ -842,6 +846,11 @@ public abstract class AbstractSurefireMojo
>          }
>      }
>
> +    void setLogger( Logger logger )
> +    {
> +        this.logger = logger;
> +    }
> +
>      @Nonnull
>      protected final PluginConsoleLogger getConsoleLogger()
>      {
> @@ -860,9 +869,22 @@ public abstract class AbstractSurefireMojo
>
>      private void setupStuff()
>      {
> -        createDependencyResolver();
> -        surefireBooterArtifact = getSurefireBooterArtifact();
> -        toolchain = getToolchain();
> +        surefireDependencyResolver = new SurefireDependencyResolver(
> getRepositorySystem(),
> +                getConsoleLogger(), getLocalRepository(),
> +                getRemoteRepositories(),
> +                getProjectRemoteRepositories(),
> +                getPluginName(), getDependencyResolver() );
> +
> +        surefireBooterArtifact = getBooterArtifact();
> +        if ( surefireBooterArtifact == null )
> +        {
> +            throw new RuntimeException( "Unable to locate surefire-booter
> in the list of plugin artifacts" );
> +        }
> +
> +        if ( getToolchainManager() != null )
> +        {
> +            toolchain =
> getToolchainManager().getToolchainFromBuildContext( "jdk", getSession() );
> +        }
>      }
>
>      @Nonnull
> @@ -983,7 +1005,8 @@ public abstract class AbstractSurefireMojo
>      private void executeAfterPreconditionsChecked( @Nonnull
> DefaultScanResult scanResult )
>          throws MojoExecutionException, MojoFailureException
>      {
> -        List<ProviderInfo> providers = createProviders();
> +        TestClassPath testClasspath = generateTestClasspath();
> +        List<ProviderInfo> providers = createProviders( testClasspath );
>
>          RunResult current = noTestsRun();
>
> @@ -992,7 +1015,7 @@ public abstract class AbstractSurefireMojo
>          {
>              try
>              {
> -                current = current.aggregate( executeProvider( provider,
> scanResult ) );
> +                current = current.aggregate( executeProvider( provider,
> scanResult, testClasspath ) );
>              }
>              catch ( SurefireBooterForkException |
> SurefireExecutionException | TestSetFailedException e )
>              {
> @@ -1011,21 +1034,13 @@ public abstract class AbstractSurefireMojo
>          handleSummary( current, firstForkException );
>      }
>
> -    private void createDependencyResolver()
> -    {
> -        dependencyResolver = new SurefireDependencyResolver(
> getRepositorySystem(),
> -
>  getConsoleLogger(), getLocalRepository(),
> -
>  getRemoteRepositories(),
> -
>  getPluginName() );
> -    }
> -
> -    protected List<ProviderInfo> createProviders()
> +    protected List<ProviderInfo> createProviders( TestClassPath
> testClasspath )
>          throws MojoExecutionException
>      {
>          Artifact junitDepArtifact = getJunitDepArtifact();
>          return new ProviderList( new DynamicProviderInfo( null ),
>                                new TestNgProviderInfo( getTestNgArtifact()
> ),
> -                              new JUnitPlatformProviderInfo(
> getJunitPlatformArtifact() ),
> +                              new JUnitPlatformProviderInfo(
> getJunitPlatformArtifact(), testClasspath ),
>                                new JUnitCoreProviderInfo(
> getJunitArtifact(), junitDepArtifact ),
>                                new JUnit4ProviderInfo( getJunitArtifact(),
> junitDepArtifact ),
>                                new JUnit3ProviderInfo() )
> @@ -1117,7 +1132,8 @@ public abstract class AbstractSurefireMojo
>      }
>
>      @Nonnull
> -    private RunResult executeProvider( @Nonnull ProviderInfo provider,
> @Nonnull DefaultScanResult scanResult )
> +    private RunResult executeProvider( @Nonnull ProviderInfo provider,
> @Nonnull DefaultScanResult scanResult,
> +                                       @Nonnull TestClassPath
> testClasspathWrapper )
>          throws MojoExecutionException, MojoFailureException,
> SurefireExecutionException, SurefireBooterForkException,
>          TestSetFailedException
>      {
> @@ -1132,8 +1148,8 @@ public abstract class AbstractSurefireMojo
>          {
>              createCopyAndReplaceForkNumPlaceholder( effectiveProperties,
> 1 ).copyToSystemProperties();
>
> -            InPluginVMSurefireStarter surefireStarter =
> -                createInprocessStarter( provider,
> classLoaderConfiguration, runOrderParameters, scanResult, platform );
> +            InPluginVMSurefireStarter surefireStarter =
> createInprocessStarter( provider, classLoaderConfiguration,
> +                    runOrderParameters, scanResult, platform,
> testClasspathWrapper );
>              return surefireStarter.runSuitesInProcess( scanResult );
>          }
>          else
> @@ -1149,7 +1165,8 @@ public abstract class AbstractSurefireMojo
>              try
>              {
>                  forkStarter = createForkStarter( provider,
> forkConfiguration, classLoaderConfiguration,
> -
>  runOrderParameters, getConsoleLogger(), scanResult, platform );
> +
>  runOrderParameters, getConsoleLogger(), scanResult, platform,
> +
>  testClasspathWrapper );
>
>                  return forkStarter.run( effectiveProperties, scanResult );
>              }
> @@ -1214,16 +1231,14 @@ public abstract class AbstractSurefireMojo
>          this.repositorySystem = repositorySystem;
>      }
>
> -    final Toolchain getToolchain()
> +    public DependencyResolver getDependencyResolver()
>      {
> -        Toolchain tc = null;
> -
> -        if ( getToolchainManager() != null )
> -        {
> -            tc = getToolchainManager().getToolchainFromBuildContext(
> "jdk", getSession() );
> -        }
> +        return dependencyResolver;
> +    }
>
> -        return tc;
> +    public void setDependencyResolver( DependencyResolver
> dependencyResolver )
> +    {
> +        this.dependencyResolver = dependencyResolver;
>      }
>
>      private boolean existsModuleDescriptor()
> @@ -1688,7 +1703,7 @@ public abstract class AbstractSurefireMojo
>          return h;
>      }
>
> -    public File getStatisticsFile( String configurationHash )
> +    private File getStatisticsFile( String configurationHash )
>      {
>          return new File( getBasedir(), ".surefire-" + configurationHash );
>      }
> @@ -1696,7 +1711,8 @@ public abstract class AbstractSurefireMojo
>      private StartupConfiguration createStartupConfiguration( @Nonnull
> ProviderInfo provider, boolean isInprocess,
>                                                               @Nonnull
> ClassLoaderConfiguration classLoaderConfiguration,
>                                                               @Nonnull
> DefaultScanResult scanResult,
> -                                                             @Nonnull
> Platform platform )
> +                                                             @Nonnull
> Platform platform,
> +                                                             @Nonnull
> TestClassPath testClasspathWrapper )
>          throws MojoExecutionException
>      {
>          try
> @@ -1714,11 +1730,12 @@ public abstract class AbstractSurefireMojo
>                          .toString();
>
>                  return newStartupConfigWithModularPath(
> classLoaderConfiguration, providerArtifacts, providerName,
> -                        getModuleDescriptor(), scanResult, javaHome );
> +                        getModuleDescriptor(), scanResult, javaHome,
> testClasspathWrapper );
>              }
>              else
>              {
> -                return newStartupConfigWithClasspath(
> classLoaderConfiguration, providerArtifacts, providerName );
> +                return newStartupConfigWithClasspath(
> classLoaderConfiguration, providerArtifacts, providerName,
> +                        testClasspathWrapper );
>              }
>          }
>          catch ( IOException e )
> @@ -1729,13 +1746,10 @@ public abstract class AbstractSurefireMojo
>
>      private StartupConfiguration newStartupConfigWithClasspath(
>              @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
> @Nonnull Set<Artifact> providerArtifacts,
> -            @Nonnull String providerName )
> +            @Nonnull String providerName, @Nonnull TestClassPath
> testClasspathWrapper )
>      {
> -        TestClassPath testClasspathWrapper = generateTestClasspath();
>          Classpath testClasspath = testClasspathWrapper.toClasspath();
>
> -        testClasspathWrapper.avoidArtifactDuplicates( providerArtifacts );
> -
>          Classpath providerClasspath = ClasspathCache.getCachedClassPath(
> providerName );
>          if ( providerClasspath == null )
>          {
> @@ -1769,8 +1783,8 @@ public abstract class AbstractSurefireMojo
>              boolean contains = false;
>              for ( Artifact providerArtifact : providerArtifacts )
>              {
> -                if ( providerArtifact.getGroupId().equals(
> inPluginArtifact.getGroupId() )
> -                        && providerArtifact.getArtifactId().equals(
> inPluginArtifact.getArtifactId() ) )
> +                if ( hasGroupArtifactId( providerArtifact.getGroupId(),
> providerArtifact.getArtifactId(),
> +                        inPluginArtifact ) )
>                  {
>                      contains = true;
>                      break;
> @@ -1784,6 +1798,11 @@ public abstract class AbstractSurefireMojo
>          return result;
>      }
>
> +    private static boolean hasGroupArtifactId( String groupId, String
> artifactId, Artifact artifact )
> +    {
> +        return groupId.equals( artifact.getGroupId() ) &&
> artifactId.equals( artifact.getArtifactId() );
> +    }
> +
>      private static Classpath createInProcClasspath( Classpath
> providerClasspath, Set<Artifact> newArtifacts )
>      {
>          Classpath inprocClasspath = providerClasspath.clone();
> @@ -1806,14 +1825,11 @@ public abstract class AbstractSurefireMojo
>      private StartupConfiguration newStartupConfigWithModularPath(
>              @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
> @Nonnull Set<Artifact> providerArtifacts,
>              @Nonnull String providerName, @Nonnull File moduleDescriptor,
> @Nonnull DefaultScanResult scanResult,
> -            @Nonnull String javaHome )
> +            @Nonnull String javaHome, @Nonnull TestClassPath
> testClasspathWrapper )
>              throws IOException
>      {
> -        TestClassPath testClasspathWrapper = generateTestClasspath();
>          Classpath testClasspath = testClasspathWrapper.toClasspath();
>
> -        testClasspathWrapper.avoidArtifactDuplicates( providerArtifacts );
> -
>          Classpath providerClasspath = ClasspathCache.getCachedClassPath(
> providerName );
>          if ( providerClasspath == null )
>          {
> @@ -2123,14 +2139,19 @@ public abstract class AbstractSurefireMojo
>          return getProjectArtifactMap().get( "junit:junit-dep" );
>      }
>
> -
>      private Artifact getJunitPlatformArtifact()
>      {
> -        Artifact artifact = getProjectArtifactMap().get(
> getJunitPlatformArtifactName() );
> -        Artifact projectArtifact = project.getArtifact();
> -        String projectArtifactName = projectArtifact.getGroupId() + ":" +
> projectArtifact.getArtifactId();
> +        Artifact artifact = getProjectArtifactMap().get(
> "org.junit.platform:junit-platform-commons" );
> +        if ( artifact == null )
> +        {
> +            artifact = getPluginArtifactMap().get(
> "org.junit.platform:junit-platform-engine" );
> +        }
>
> -        if ( artifact == null && projectArtifactName.equals(
> getJunitPlatformArtifactName() ) )
> +        Artifact projectArtifact = project.getArtifact();
> +        String projectGroupId = projectArtifact.getGroupId();
> +        if ( artifact == null && ( "org.junit.platform".equals(
> projectGroupId )
> +                || "org.junit.jupiter".equals( projectGroupId )
> +                || "org.junit.vintage".equals( projectGroupId ) ) )
>          {
>              artifact = projectArtifact;
>          }
> @@ -2141,11 +2162,12 @@ public abstract class AbstractSurefireMojo
>      private ForkStarter createForkStarter( @Nonnull ProviderInfo
> provider, @Nonnull ForkConfiguration forkConfiguration,
>                                             @Nonnull
> ClassLoaderConfiguration classLoaderConfiguration,
>                                             @Nonnull RunOrderParameters
> runOrderParameters, @Nonnull ConsoleLogger log,
> -                                           @Nonnull DefaultScanResult
> scanResult, @Nonnull Platform platform )
> +                                           @Nonnull DefaultScanResult
> scanResult, @Nonnull Platform platform,
> +                                           @Nonnull TestClassPath
> testClasspathWrapper )
>          throws MojoExecutionException, MojoFailureException
>      {
> -        StartupConfiguration startupConfiguration =
> -                createStartupConfiguration( provider, false,
> classLoaderConfiguration, scanResult, platform );
> +        StartupConfiguration startupConfiguration =
> createStartupConfiguration( provider, false,
> +                classLoaderConfiguration, scanResult, platform,
> testClasspathWrapper );
>          String configChecksum = getConfigChecksum();
>          StartupReportConfiguration startupReportConfiguration =
> getStartupReportConfiguration( configChecksum, true );
>          ProviderConfiguration providerConfiguration =
> createProviderConfiguration( runOrderParameters );
> @@ -2157,11 +2179,12 @@ public abstract class AbstractSurefireMojo
>                                                                @Nonnull
> ClassLoaderConfiguration classLoaderConfig,
>                                                                @Nonnull
> RunOrderParameters runOrderParameters,
>                                                                @Nonnull
> DefaultScanResult scanResult,
> -                                                              @Nonnull
> Platform platform )
> +                                                              @Nonnull
> Platform platform,
> +                                                              @Nonnull
> TestClassPath testClasspathWrapper )
>          throws MojoExecutionException, MojoFailureException
>      {
> -        StartupConfiguration startupConfiguration =
> -                createStartupConfiguration( provider, true,
> classLoaderConfig, scanResult, platform );
> +        StartupConfiguration startupConfiguration =
> createStartupConfiguration( provider, true, classLoaderConfig,
> +                scanResult, platform, testClasspathWrapper );
>          String configChecksum = getConfigChecksum();
>          StartupReportConfiguration startupReportConfiguration =
> getStartupReportConfiguration( configChecksum, false );
>          ProviderConfiguration providerConfiguration =
> createProviderConfiguration( runOrderParameters );
> @@ -2353,16 +2376,6 @@ public abstract class AbstractSurefireMojo
>          return new JdkAttributes( jvmToUse, isBuiltInJava9AtLeast() );
>      }
>
> -    private Artifact getSurefireBooterArtifact()
> -    {
> -        Artifact artifact = getBooterArtifact();
> -        if ( artifact == null )
> -        {
> -            throw new RuntimeException( "Unable to locate surefire-booter
> in the list of plugin artifacts" );
> -        }
> -        return artifact;
> -    }
> -
>      /**
>       * Where surefire stores its own temp files
>       *
> @@ -2451,6 +2464,7 @@ public abstract class AbstractSurefireMojo
>          checksum.add( isParallelOptimized() );
>          checksum.add( isTrimStackTrace() );
>          checksum.add( getRemoteRepositories() );
> +        checksum.add( getProjectRemoteRepositories() );
>          checksum.add( isDisableXmlReport() );
>          checksum.add( isUseSystemClassLoader() );
>          checksum.add( isUseManifestOnlyJar() );
> @@ -2504,7 +2518,7 @@ public abstract class AbstractSurefireMojo
>       */
>      private TestClassPath generateTestClasspath()
>      {
> -        @SuppressWarnings( "unchecked" ) Set<Artifact> classpathArtifacts
> = getProject().getArtifacts();
> +        Set<Artifact> classpathArtifacts = getProject().getArtifacts();
>
>          if ( getClasspathDependencyScopeExclude() != null &&
> !getClasspathDependencyScopeExclude().isEmpty() )
>          {
> @@ -2520,7 +2534,7 @@ public abstract class AbstractSurefireMojo
>          }
>
>          return new TestClassPath( classpathArtifacts,
> getClassesDirectory(),
> -                getTestClassesDirectory(),
> getAdditionalClasspathElements(), getConsoleLogger() );
> +                getTestClassesDirectory(),
> getAdditionalClasspathElements() );
>      }
>
>      /**
> @@ -2561,7 +2575,9 @@ public abstract class AbstractSurefireMojo
>          if ( existing == null )
>          {
>              List<String> items = new ArrayList<>();
> -            for ( Artifact artifact : dependencyResolver.resolveArtifact(
> surefireArtifact ).getArtifacts() )
> +            Set<Artifact> booterArtifacts =
> +                    surefireDependencyResolver.resolvePluginArtifact(
> surefireArtifact ).getArtifacts();
> +            for ( Artifact artifact : booterArtifacts )
>              {
>                  getConsoleLogger().debug(
>                      "Adding to " + getPluginName() + " booter test
> classpath: " + artifact.getFile().getAbsolutePath()
> @@ -2665,7 +2681,7 @@ public abstract class AbstractSurefireMojo
>                                                              + "is picking
> up an old junit version" );
>                      }
>                      throw new MojoFailureException(
> "groups/excludedGroups require TestNG, JUnit48+ or JUnit 5 "
> -                            + "on project test classpath" );
> +                            + "(a specific engine required on classpath)
> on project test classpath" );
>                  }
>              }
>
> @@ -2789,7 +2805,7 @@ public abstract class AbstractSurefireMojo
>          {
>              Artifact surefireArtifact = getBooterArtifact();
>              String version = surefireArtifact.getBaseVersion();
> -            return dependencyResolver.getProviderClasspath(
> "surefire-testng", version );
> +            return surefireDependencyResolver.getProviderClasspath(
> "surefire-testng", version );
>          }
>      }
>
> @@ -2820,7 +2836,7 @@ public abstract class AbstractSurefireMojo
>              // add the JUnit provider as default - it doesn't require
> JUnit to be present,
>              // since it supports POJO tests.
>              String version = surefireBooterArtifact.getBaseVersion();
> -            return dependencyResolver.getProviderClasspath(
> "surefire-junit3", version );
> +            return surefireDependencyResolver.getProviderClasspath(
> "surefire-junit3", version );
>          }
>      }
>
> @@ -2859,18 +2875,23 @@ public abstract class AbstractSurefireMojo
>          public Set<Artifact> getProviderClasspath()
>          {
>              String version = surefireBooterArtifact.getBaseVersion();
> -            return dependencyResolver.getProviderClasspath(
> "surefire-junit4", version );
> +            return surefireDependencyResolver.getProviderClasspath(
> "surefire-junit4", version );
>          }
>      }
>
>      final class JUnitPlatformProviderInfo
>          implements ProviderInfo
>      {
> -        private final Artifact junitArtifact;
> +        private static final String PROVIDER_DEP_GID =
> "org.junit.platform";
> +        private static final String PROVIDER_DEP_AID =
> "junit-platform-launcher";
>
> -        JUnitPlatformProviderInfo( Artifact junitArtifact )
> +        private final Artifact junitPlatformArtifact;
> +        private final TestClassPath testClasspath;
> +
> +        JUnitPlatformProviderInfo( Artifact junitPlatformArtifact,
> TestClassPath testClasspath )
>          {
> -            this.junitArtifact = junitArtifact;
> +            this.junitPlatformArtifact = junitPlatformArtifact;
> +            this.testClasspath = testClasspath;
>          }
>
>          @Override
> @@ -2883,7 +2904,7 @@ public abstract class AbstractSurefireMojo
>          @Override
>          public boolean isApplicable()
>          {
> -            return junitArtifact != null;
> +            return junitPlatformArtifact != null;
>          }
>
>          @Override
> @@ -2894,10 +2915,134 @@ public abstract class AbstractSurefireMojo
>
>          @Override
>          @Nonnull
> -        public Set<Artifact> getProviderClasspath()
> +        public Set<Artifact> getProviderClasspath() throws
> MojoExecutionException
>          {
> -            String version = surefireBooterArtifact.getBaseVersion();
> -            return dependencyResolver.getProviderClasspath(
> "surefire-junit-platform", version );
> +            String surefireVersion =
> surefireBooterArtifact.getBaseVersion();
> +            Map<String, Artifact> providerArtifacts =
> +                    surefireDependencyResolver.getProviderClasspathAsMap(
> "surefire-junit-platform", surefireVersion );
> +            Map<String, Artifact> testDependencies =
> testClasspath.getTestDependencies();
> +
> +            if ( hasDependencyPlatformEngine( testDependencies ) )
> +            {
> +                String filterTestDependency =
> "org.junit.platform:junit-platform-engine";
> +                getConsoleLogger().debug( "Test dependencies contain " +
> filterTestDependency );
> +                narrowProviderDependencies( filterTestDependency,
> providerArtifacts, testDependencies );
> +            }
> +            else
> +            {
> +                ProjectBuildingRequest request =
> getSession().getProjectBuildingRequest();
> +                Collection<Dependency> pluginDependencies =
> getPluginDescriptor().getPlugin().getDependencies();
> +                Set<Artifact> engines =
> +
> surefireDependencyResolver.resolvePluginDependencies( request,
> pluginDependencies );
> +                if ( hasDependencyPlatformEngine( engines ) )
> +                {
> +                    Map<String, Artifact> engineArtifacts =
> artifactMapByVersionlessId( engines );
> +                    providerArtifacts.putAll( engineArtifacts );
> +                    alignVersions( providerArtifacts, engineArtifacts );
> +                }
> +                else if ( hasDependencyJupiterAPI( testDependencies ) )
> +                {
> +                    String engineGroupId = "org.junit.jupiter";
> +                    String engineArtifactId = "junit-jupiter-engine";
> +                    String engineCoordinates = engineGroupId + ":" +
> engineArtifactId;
> +                    String api = "org.junit.jupiter:junit-jupiter-api";
> +                    getConsoleLogger().debug( "Test dependencies contain
> " + api + ". Resolving " + engineCoordinates );
> +                    String engineVersion = testDependencies.get( api
> ).getBaseVersion();
> +                    addEngineByApi( engineGroupId, engineArtifactId,
> engineVersion,
> +                            providerArtifacts, testDependencies );
> +                }
> +            }
> +            providerArtifacts.keySet().removeAll(
> testDependencies.keySet() );
> +            return new LinkedHashSet<>( providerArtifacts.values() );
> +        }
> +
> +        private void addEngineByApi( String engineGroupId, String
> engineArtifactId, String engineVersion,
> +                                     Map<String, Artifact>
> providerArtifacts, Map<String, Artifact> testDependencies )
> +        {
> +            providerArtifacts.keySet().removeAll(
> testDependencies.keySet() );
> +            for ( Artifact dep : resolve( engineGroupId,
> engineArtifactId, engineVersion, null, "jar" ) )
> +            {
> +                String key = dep.getGroupId() + ":" + dep.getArtifactId();
> +                if ( !testDependencies.containsKey( key ) )
> +                {
> +                    providerArtifacts.put( key, dep );
> +                }
> +            }
> +            alignVersions( providerArtifacts, testDependencies );
> +        }
> +
> +        private void narrowProviderDependencies( String
> filterTestDependency,
> +                                                 Map<String, Artifact>
> providerArtifacts,
> +                                                 Map<String, Artifact>
> testDependencies )
> +        {
> +            Artifact engine = testDependencies.get( filterTestDependency
> );
> +            String groupId = engine.getGroupId();
> +            String artifactId = engine.getArtifactId();
> +            String version = engine.getBaseVersion();
> +            String classifier = engine.getClassifier();
> +            String type = engine.getType();
> +            for ( Artifact engineDep : resolve( groupId, artifactId,
> version, classifier, type ) )
> +            {
> +                providerArtifacts.remove( engineDep.getGroupId() + ":" +
> engineDep.getArtifactId() );
> +                getConsoleLogger().debug( "Removed artifact " + engineDep
> +                        + " from provider. Already appears in test
> classpath." );
> +            }
> +            alignVersions( providerArtifacts, testDependencies );
> +        }
> +
> +        private void alignVersions( Map<String, Artifact>
> providerArtifacts,
> +                                    Map<String, Artifact>
> referencedDependencies )
> +        {
> +            String version = referencedDependencies.get(
> "org.junit.platform:junit-platform-commons" ).getBaseVersion();
> +            for ( Artifact launcherArtifact : resolve( PROVIDER_DEP_GID,
> PROVIDER_DEP_AID, version, null, "jar" ) )
> +            {
> +                String key = launcherArtifact.getGroupId() + ":" +
> launcherArtifact.getArtifactId();
> +                if ( providerArtifacts.containsKey( key ) )
> +                {
> +                    providerArtifacts.put( key, launcherArtifact );
> +                }
> +            }
> +        }
> +
> +        private Set<Artifact> resolve( String g, String a, String v,
> String c, String t )
> +        {
> +            ArtifactHandler handler =
> junitPlatformArtifact.getArtifactHandler();
> +            Artifact artifact = new DefaultArtifact( g, a, v, null, t, c,
> handler );
> +            getConsoleLogger().debug( "Resolving artifact " + g + ":" + a
> + ":" + v );
> +            Set<Artifact> r =
> surefireDependencyResolver.resolveProjectArtifact( artifact
> ).getArtifacts();
> +            getConsoleLogger().debug( "Resolved artifact " + g + ":" + a
> + ":" + v + " to " + r );
> +            return r;
> +        }
> +
> +        private boolean hasDependencyJupiterAPI( Map<String, Artifact>
> dependencies )
> +        {
> +            return dependencies.containsKey(
> "org.junit.jupiter:junit-jupiter-api" )
> +                    || hasGroupArtifactId( "org.junit.jupiter",
> "junit-jupiter-api", getProject().getArtifact() );
> +        }
> +
> +        private boolean hasDependencyPlatformEngine( Map<String,
> Artifact> dependencies )
> +        {
> +            return dependencies.containsKey(
> "org.junit.platform:junit-platform-engine" )
> +                    || hasGroupArtifactId( "org.junit.platform",
> "junit-platform-engine", getProject().getArtifact() );
> +        }
> +
> +        private boolean hasDependencyPlatformEngine( Collection<Artifact>
> dependencies )
> +        {
> +            if ( hasGroupArtifactId( "org.junit.platform",
> "junit-platform-engine", getProject().getArtifact() ) )
> +            {
> +                return true;
> +            }
> +
> +            for ( Artifact dependency : dependencies )
> +            {
> +                if ( dependency.getGroupId().equals( "org.junit.platform"
> )
> +                        && dependency.getArtifactId().equals(
> "junit-platform-engine" ) )
> +                {
> +                    return true;
> +                }
> +            }
> +
> +            return false;
>          }
>      }
>
> @@ -2945,7 +3090,7 @@ public abstract class AbstractSurefireMojo
>          public Set<Artifact> getProviderClasspath()
>          {
>              String version = surefireBooterArtifact.getBaseVersion();
> -            return dependencyResolver.getProviderClasspath(
> "surefire-junit47", version );
> +            return surefireDependencyResolver.getProviderClasspath(
> "surefire-junit47", version );
>          }
>      }
>
> @@ -2993,7 +3138,7 @@ public abstract class AbstractSurefireMojo
>          @Nonnull
>          public Set<Artifact> getProviderClasspath()
>          {
> -            return dependencyResolver.addProviderToClasspath(
> getPluginArtifactMap(), getMojoArtifact(),
> +            return surefireDependencyResolver.addProviderToClasspath(
> getPluginArtifactMap(), getMojoArtifact(),
>                      getCommonArtifact(), getApiArtifact(),
> getLoggerApiArtifact() );
>          }
>      }
> @@ -3176,7 +3321,6 @@ public abstract class AbstractSurefireMojo
>          this.projectArtifactMap = projectArtifactMap;
>      }
>
> -
>      public String getReportNameSuffix()
>      {
>          return reportNameSuffix;
> @@ -3305,17 +3449,6 @@ public abstract class AbstractSurefireMojo
>          this.junitArtifactName = junitArtifactName;
>      }
>
> -    public String getJunitPlatformArtifactName()
> -    {
> -        return junitPlatformArtifactName;
> -    }
> -
> -    @SuppressWarnings( "UnusedDeclaration" )
> -    public void setJunitPlatformArtifactName( String
> junitPlatformArtifactName )
> -    {
> -        this.junitPlatformArtifactName = junitPlatformArtifactName;
> -    }
> -
>      public String getTestNGArtifactName()
>      {
>          return testNGArtifactName;
> @@ -3423,6 +3556,17 @@ public abstract class AbstractSurefireMojo
>          this.trimStackTrace = trimStackTrace;
>      }
>
> +    public List<ArtifactRepository> getProjectRemoteRepositories()
> +    {
> +        return projectRemoteRepositories;
> +    }
> +
> +    @SuppressWarnings( "UnusedDeclaration" )
> +    public void setProjectRemoteRepositories( List<ArtifactRepository>
> projectRemoteRepositories )
> +    {
> +        this.projectRemoteRepositories = projectRemoteRepositories;
> +    }
> +
>      public List<ArtifactRepository> getRemoteRepositories()
>      {
>          return remoteRepositories;
> @@ -3518,6 +3662,12 @@ public abstract class AbstractSurefireMojo
>          this.dependenciesToScan = dependenciesToScan;
>      }
>
> +    @SuppressWarnings( "UnusedDeclaration" )
> +    void setPluginDescriptor( PluginDescriptor pluginDescriptor )
> +    {
> +        this.pluginDescriptor = pluginDescriptor;
> +    }
> +
>      public PluginDescriptor getPluginDescriptor()
>      {
>          return pluginDescriptor;
> diff --git
> a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
> b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
> index cb63cc1..fea74fd 100644
> ---
> a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
> +++
> b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
> @@ -36,7 +36,7 @@ public interface ProviderInfo
>      boolean isApplicable();
>
>      @Nonnull
> -    Set<Artifact> getProviderClasspath();
> +    Set<Artifact> getProviderClasspath() throws MojoExecutionException;
>
>      void addProviderProperties() throws MojoExecutionException;
>  }
> diff --git
> a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
> b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
> index b255f38..170107c 100644
> ---
> a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
> +++
> b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
> @@ -19,6 +19,7 @@ package org.apache.maven.plugin.surefire;
>   * under the License.
>   */
>
> +import java.util.Collection;
>  import java.util.Iterator;
>  import java.util.LinkedHashSet;
>  import java.util.List;
> @@ -29,20 +30,29 @@ import org.apache.maven.artifact.Artifact;
>  import org.apache.maven.artifact.repository.ArtifactRepository;
>  import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
>  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
> -import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
> +import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
>  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
>  import
> org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
>  import
> org.apache.maven.artifact.versioning.OverConstrainedVersionException;
>  import org.apache.maven.artifact.versioning.VersionRange;
>  import org.apache.maven.model.Dependency;
> +import org.apache.maven.plugin.MojoExecutionException;
>  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
> +import org.apache.maven.project.ProjectBuildingRequest;
>  import org.apache.maven.repository.RepositorySystem;
> +import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
> +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
> +import
> org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
> +import
> org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException;
>
>  import javax.annotation.Nonnull;
>  import javax.annotation.Nullable;
>
> -import static java.util.Collections.singletonList;
> -import static org.apache.maven.artifact.Artifact.SCOPE_TEST;
> +import static java.util.Arrays.asList;
> +import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;
> +import static
> org.apache.maven.artifact.Artifact.SCOPE_COMPILE_PLUS_RUNTIME;
> +import static org.apache.maven.artifact.Artifact.SCOPE_RUNTIME;
> +import static
> org.apache.maven.artifact.ArtifactUtils.artifactMapByVersionlessId;
>  import static
> org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
>
>  /**
> @@ -76,19 +86,27 @@ final class SurefireDependencyResolver
>
>      private final ArtifactRepository localRepository;
>
> -    private final List<ArtifactRepository> remoteRepositories;
> +    private final List<ArtifactRepository> pluginRemoteRepositories;
> +
> +    private final List<ArtifactRepository> projectRemoteRepositories;
>
>      private final String pluginName;
>
> +    private final DependencyResolver depencencyResolver;
> +
>      SurefireDependencyResolver( RepositorySystem repositorySystem,
> ConsoleLogger log,
>                                  ArtifactRepository localRepository,
> -                                List<ArtifactRepository>
> remoteRepositories, String pluginName )
> +                                List<ArtifactRepository>
> pluginRemoteRepositories,
> +                                List<ArtifactRepository>
> projectRemoteRepositories, String pluginName,
> +                                DependencyResolver depencencyResolver )
>      {
>          this.repositorySystem = repositorySystem;
>          this.log = log;
>          this.localRepository = localRepository;
> -        this.remoteRepositories = remoteRepositories;
> +        this.pluginRemoteRepositories = pluginRemoteRepositories;
> +        this.projectRemoteRepositories = projectRemoteRepositories;
>          this.pluginName = pluginName;
> +        this.depencencyResolver = depencencyResolver;
>      }
>
>      static boolean isWithinVersionSpec( @Nullable Artifact artifact,
> @Nonnull String versionSpec )
> @@ -115,23 +133,46 @@ final class SurefireDependencyResolver
>          }
>      }
>
> -    ArtifactResolutionResult resolveArtifact( Artifact providerArtifact )
> +    Set<Artifact> resolvePluginDependencies( ProjectBuildingRequest
> request, Collection<Dependency> pluginDependencies )
> +            throws MojoExecutionException
>      {
> -        return resolveArtifact( providerArtifact, null );
> +        try
> +        {
> +            Iterable<ArtifactResult> resolvedPluginDependencies =
> depencencyResolver.resolveDependencies( request,
> +                    pluginDependencies, null, ScopeFilter.including(
> SCOPE_COMPILE, SCOPE_RUNTIME ) );
> +
> +            Set<Artifact> resolved = new LinkedHashSet<>();
> +            for ( ArtifactResult resolvedPluginDependency :
> resolvedPluginDependencies )
> +            {
> +                resolved.add( resolvedPluginDependency.getArtifact() );
> +            }
> +            return resolved;
> +        }
> +        catch ( DependencyResolverException e )
> +        {
> +            throw new MojoExecutionException( e.getLocalizedMessage(), e
> );
> +        }
>      }
>
> -    private ArtifactResolutionResult resolveArtifact( Artifact
> providerArtifact, @Nullable Artifact excludeArtifact )
> +    ArtifactResolutionResult resolvePluginArtifact( Artifact artifact )
> +    {
> +        return resolveArtifact( artifact, pluginRemoteRepositories );
> +    }
> +
> +    ArtifactResolutionResult resolveProjectArtifact( Artifact artifact )
> +    {
> +        return resolveArtifact( artifact, projectRemoteRepositories );
> +    }
> +
> +    private ArtifactResolutionResult resolveArtifact( Artifact artifact,
> List<ArtifactRepository> repositories )
>      {
>          ArtifactResolutionRequest request = new
> ArtifactResolutionRequest()
> -                                                    .setArtifact(
> providerArtifact )
> -
> .setRemoteRepositories( remoteRepositories )
> -                                                    .setLocalRepository(
> localRepository )
> -
> .setResolveTransitively( true );
> -        if ( excludeArtifact != null )
> -        {
> -            String pattern = excludeArtifact.getGroupId() + ":" +
> excludeArtifact.getArtifactId();
> -            request.setCollectionFilter( new ExcludesArtifactFilter(
> singletonList( pattern ) ) );
> -        }
> +                .setArtifact( artifact )
> +                .setLocalRepository( localRepository )
> +                .setResolveTransitively( true )
> +                .setCollectionFilter( new RuntimeArtifactFilter() )
> +                .setRemoteRepositories( repositories );
> +
>          return repositorySystem.resolve( request );
>      }
>
> @@ -142,7 +183,7 @@ final class SurefireDependencyResolver
>
>          Artifact providerArtifact =
> repositorySystem.createDependencyArtifact( provider );
>
> -        ArtifactResolutionResult result = resolveArtifact(
> providerArtifact );
> +        ArtifactResolutionResult result = resolvePluginArtifact(
> providerArtifact );
>
>          if ( log.isDebugEnabled() )
>          {
> @@ -157,17 +198,23 @@ final class SurefireDependencyResolver
>          return orderProviderArtifacts( result.getArtifacts() );
>      }
>
> +    @Nonnull
> +    Map<String, Artifact> getProviderClasspathAsMap( String
> providerArtifactId, String providerVersion )
> +    {
> +        return artifactMapByVersionlessId( getProviderClasspath(
> providerArtifactId, providerVersion ) );
> +    }
> +
>      Set<Artifact> addProviderToClasspath( Map<String, Artifact>
> pluginArtifactMap, Artifact mojoPluginArtifact,
>                                            Artifact surefireCommon,
> Artifact surefireApi, Artifact surefireLoggerApi )
>      {
>          Set<Artifact> providerArtifacts = new LinkedHashSet<>();
> -        ArtifactResolutionResult artifactResolutionResult =
> resolveArtifact( mojoPluginArtifact );
> +        ArtifactResolutionResult artifactResolutionResult =
> resolvePluginArtifact( mojoPluginArtifact );
>          for ( Artifact artifact : pluginArtifactMap.values() )
>          {
>              if ( !artifactResolutionResult.getArtifacts().contains(
> artifact ) )
>              {
>                  providerArtifacts.add( artifact );
> -                for ( Artifact dependency : resolveArtifact( artifact
> ).getArtifacts() )
> +                for ( Artifact dependency : resolvePluginArtifact(
> artifact ).getArtifacts() )
>                  {
>                      String groupId = dependency.getGroupId();
>                      String artifactId = dependency.getArtifactId();
> @@ -219,7 +266,19 @@ final class SurefireDependencyResolver
>          dependency.setArtifactId( providerArtifactId );
>          dependency.setVersion( providerVersion );
>          dependency.setType( "jar" );
> -        dependency.setScope( SCOPE_TEST );
>          return dependency;
>      }
> +
> +    static class RuntimeArtifactFilter implements ArtifactFilter
> +    {
> +        private static final Collection<String> SCOPES =
> +                asList( SCOPE_COMPILE, SCOPE_COMPILE_PLUS_RUNTIME,
> SCOPE_RUNTIME );
> +
> +        @Override
> +        public boolean include( Artifact artifact )
> +        {
> +            String scope = artifact.getScope();
> +            return !artifact.isOptional() && ( scope == null ||
> SCOPES.contains( scope ) );
> +        }
> +    }
>  }
> diff --git
> a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java
> b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java
> index ee8fadb..3a37816 100644
> ---
> a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java
> +++
> b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java
> @@ -20,14 +20,13 @@ package org.apache.maven.plugin.surefire;
>   */
>
>  import org.apache.maven.artifact.Artifact;
> -import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
>  import org.apache.maven.surefire.booter.Classpath;
>
>  import java.io.File;
>  import java.util.ArrayList;
> -import java.util.Iterator;
> +import java.util.LinkedHashMap;
>  import java.util.List;
> -import java.util.Set;
> +import java.util.Map;
>
>  import static java.util.Collections.addAll;
>  import static org.apache.maven.shared.utils.StringUtils.split;
> @@ -38,45 +37,26 @@ final class TestClassPath
>      private final File classesDirectory;
>      private final File testClassesDirectory;
>      private final String[] additionalClasspathElements;
> -    private final ConsoleLogger logger;
>
>      TestClassPath( Iterable<Artifact> artifacts,
>                     File classesDirectory,
>                     File testClassesDirectory,
> -                   String[] additionalClasspathElements,
> -                   ConsoleLogger logger )
> +                   String[] additionalClasspathElements )
>      {
>          this.artifacts = artifacts;
>          this.classesDirectory = classesDirectory;
>          this.testClassesDirectory = testClassesDirectory;
>          this.additionalClasspathElements = additionalClasspathElements;
> -        this.logger = logger;
>      }
>
> -    void avoidArtifactDuplicates( Set<Artifact> providerArtifacts )
> +    Map<String, Artifact> getTestDependencies()
>      {
> +        Map<String, Artifact> artifactMapping = new LinkedHashMap<>();
>          for ( Artifact artifact : artifacts )
>          {
> -            Iterator<Artifact> it = providerArtifacts.iterator();
> -            while ( it.hasNext() )
> -            {
> -                Artifact providerArtifact = it.next();
> -                String classifier1 = providerArtifact.getClassifier();
> -                String classifier2 = artifact.getClassifier();
> -                if ( providerArtifact.getGroupId().equals(
> artifact.getGroupId() )
> -                        && providerArtifact.getArtifactId().equals(
> artifact.getArtifactId() )
> -                        && providerArtifact.getType().equals(
> artifact.getType() )
> -                        && ( classifier1 == null ? classifier2 == null :
> classifier1.equals( classifier2 ) ) )
> -                {
> -                    it.remove();
> -                    if ( logger.isDebugEnabled() )
> -                    {
> -                        logger.debug( "Removed artifact " +
> providerArtifact + " from provider. "
> -                                + "Already appears in test classpath." );
> -                    }
> -                }
> -            }
> +            artifactMapping.put( artifact.getGroupId() + ":" +
> artifact.getArtifactId(), artifact );
>          }
> +        return artifactMapping;
>      }
>
>      Classpath toClasspath()
> diff --git
> a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
> b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
> index 0f9ee80..2d268bc 100644
> ---
> a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
> +++
> b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
> @@ -108,7 +108,7 @@ public class AbstractSurefireMojoJava7PlusTest
>
>          TestClassPath testClasspath =
>                  new TestClassPath( asList( modular, nonModular, junit,
> hamcrest ), classesDir, testClassesDir,
> -                        null, null );
> +                        null );
>
>          doReturn( testClasspath ).when( mojo, "generateTestClasspath" );
>          doReturn( 1 ).when( mojo, "getEffectiveForkCount" );
> @@ -171,11 +171,10 @@ public class AbstractSurefireMojoJava7PlusTest
>
>          StartupConfiguration conf = invokeMethod( mojo,
> "newStartupConfigWithModularPath",
>                  classLoaderConfiguration, providerClasspath,
> "org.asf.Provider", moduleInfo, scanResult,
> -                "" );
> +                "", testClasspath );
>
>          verify( mojo, times( 1 ) ).effectiveIsEnableAssertions();
>          verify( mojo, times( 1 ) ).isChildDelegation();
> -        verifyPrivate( mojo, times( 1 ) ).invoke( "generateTestClasspath"
> );
>          verify( mojo, times( 1 ) ).getEffectiveForkCount();
>          verify( mojo, times( 1 ) ).getTestClassesDirectory();
>          verify( scanResult, times( 1 ) ).getClasses();
> diff --git
> a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
> b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
> index a6fc708..080f2b5 100644
> ---
> a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
> +++
> b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
> @@ -22,9 +22,22 @@ package org.apache.maven.plugin.surefire;
>  import org.apache.maven.artifact.Artifact;
>  import org.apache.maven.artifact.DefaultArtifact;
>  import org.apache.maven.artifact.handler.ArtifactHandler;
> +import org.apache.maven.artifact.repository.ArtifactRepository;
> +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
> +import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
>  import org.apache.maven.artifact.versioning.VersionRange;
> +import org.apache.maven.execution.MavenSession;
> +import org.apache.maven.model.Dependency;
> +import org.apache.maven.model.Plugin;
> +import org.apache.maven.plugin.descriptor.PluginDescriptor;
> +import
> org.apache.maven.plugin.surefire.AbstractSurefireMojo.JUnitPlatformProviderInfo;
>  import org.apache.maven.plugin.surefire.log.PluginConsoleLogger;
>  import org.apache.maven.project.MavenProject;
> +import org.apache.maven.project.ProjectBuildingRequest;
> +import org.apache.maven.repository.RepositorySystem;
> +import
> org.apache.maven.shared.artifact.filter.resolve.TransformableFilter;
> +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
> +import
> org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
>  import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
>  import org.apache.maven.surefire.booter.Classpath;
>  import org.apache.maven.surefire.booter.StartupConfiguration;
> @@ -33,7 +46,10 @@ import org.codehaus.plexus.logging.Logger;
>  import org.junit.Test;
>  import org.junit.runner.RunWith;
>  import org.mockito.ArgumentCaptor;
> +import org.mockito.ArgumentMatchers;
>  import org.mockito.Mock;
> +import org.mockito.invocation.InvocationOnMock;
> +import org.mockito.stubbing.Answer;
>  import org.powermock.core.classloader.annotations.PrepareForTest;
>  import org.powermock.modules.junit4.PowerMockRunner;
>
> @@ -41,6 +57,7 @@ import java.io.File;
>  import java.io.IOException;
>  import java.util.ArrayList;
>  import java.util.Collection;
> +import java.util.Collections;
>  import java.util.HashMap;
>  import java.util.HashSet;
>  import java.util.LinkedHashSet;
> @@ -50,13 +67,14 @@ import java.util.Set;
>
>  import static java.io.File.separatorChar;
>  import static java.util.Arrays.asList;
> -import static java.util.Collections.singleton;
> +import static java.util.Collections.*;
>  import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
>  import static
> org.apache.maven.artifact.versioning.VersionRange.createFromVersion;
>  import static
> org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
>  import static org.fest.assertions.Assertions.assertThat;
> -import static org.mockito.ArgumentMatchers.any;
> -import static org.mockito.ArgumentMatchers.anyString;
> +import static org.fest.assertions.MapAssert.entry;
> +import static org.junit.Assert.fail;
> +import static org.mockito.ArgumentMatchers.*;
>  import static org.mockito.Mockito.times;
>  import static org.mockito.Mockito.when;
>  import static org.mockito.Mockito.verify;
> @@ -247,7 +265,7 @@ public class AbstractSurefireMojoTest
>          File classesDir = mockFile( "classes" );
>          File testClassesDir = mockFile( "test-classes" );
>          TestClassPath testClasspath =
> -                new TestClassPath( asList( junit, hamcrest ), classesDir,
> testClassesDir, null, null );
> +                new TestClassPath( asList( junit, hamcrest ), classesDir,
> testClassesDir, null );
>
>          doReturn( testClasspath ).when( mojo, "generateTestClasspath" );
>          doReturn( 1 ).when( mojo, "getEffectiveForkCount" );
> @@ -267,11 +285,10 @@ public class AbstractSurefireMojoTest
>          when( mojo.getConsoleLogger() ).thenReturn( new
> PluginConsoleLogger( logger ) );
>
>          StartupConfiguration conf = invokeMethod( mojo,
> "newStartupConfigWithClasspath",
> -                classLoaderConfiguration, providerArtifacts,
> "org.asf.Provider" );
> +                classLoaderConfiguration, providerArtifacts,
> "org.asf.Provider", testClasspath );
>
>          verify( mojo, times( 1 ) ).effectiveIsEnableAssertions();
>          verify( mojo, times( 1 ) ).isChildDelegation();
> -        verifyPrivate( mojo, times( 1 ) ).invoke( "generateTestClasspath"
> );
>          verify( mojo, times( 1 ) ).getEffectiveForkCount();
>          ArgumentCaptor<String> argument = ArgumentCaptor.forClass(
> String.class );
>          verify( logger, times( 6 ) ).debug( argument.capture() );
> @@ -360,9 +377,1214 @@ public class AbstractSurefireMojoTest
>                  .isDirectory();
>      }
>
> +    @Test
> +    public void shouldSmartlyResolveJUnit5ProviderWithJUnit4() throws
> Exception
> +    {
> +        MavenProject mavenProject = new MavenProject();
> +        mavenProject.setArtifact( new DefaultArtifact( "dummy", "pom",
> createFromVersion( "1.0.0" ),
> +                null, "jar", null, mock( ArtifactHandler.class ) ) );
> +        mojo.setProject( mavenProject );
> +
> +        final VersionRange surefireVersion = createFromVersion( "1" );
> +
> +        Artifact junitPlatformArtifact = new DefaultArtifact(
> "org.apache.maven.surefire",
> +                "surefire-junit-platform", surefireVersion, null, "jar",
> null, mock( ArtifactHandler.cla