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 2020/05/31 11:34:42 UTC

[maven-surefire] 03/05: [SUREFIRE-1733] Surefire and Failsafe JPMS additions for JUnit 5.x execution

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

commit ed70a5683398f5de55b958e5c8579db89dc7f594
Author: tibordigana <ti...@apache.org>
AuthorDate: Wed May 6 01:31:12 2020 +0200

    [SUREFIRE-1733] Surefire and Failsafe JPMS additions for JUnit 5.x execution
---
 .../plugin/surefire/AbstractSurefireMojo.java      | 172 +++++++++++----
 .../apache/maven/plugin/surefire/ProviderInfo.java |   4 +
 ...ProviderInfo.java => ProviderRequirements.java} |  40 ++--
 .../booterclient/DefaultForkConfiguration.java     |   2 +-
 .../ModularClasspathForkConfiguration.java         |  70 ++++--
 .../AbstractSurefireMojoJava7PlusTest.java         | 243 ++++++++++++++++++++-
 .../plugin/surefire/AbstractSurefireMojoTest.java  |  24 +-
 ...ooterDeserializerProviderConfigurationTest.java |   3 +-
 ...BooterDeserializerStartupConfigurationTest.java |   6 +-
 .../booterclient/DefaultForkConfigurationTest.java |   7 +-
 .../booterclient/ForkConfigurationTest.java        | 187 +++++++++++++++-
 .../ModularClasspathForkConfigurationTest.java     |   8 +-
 .../src/site/apt/examples/jpms.apt.vm              | 205 +++++++++++++++++
 maven-surefire-plugin/src/site/site.xml            |   1 +
 .../maven/surefire/booter/ModularClasspath.java    |  18 +-
 .../surefire/booter/StartupConfiguration.java      |  15 +-
 .../surefire/its/MultiModuleProjectWithJPMSIT.java |  70 ++++++
 .../its/jiras/Surefire1570ModularFailsafeIT.java   |  14 +-
 ...larFailsafeIT.java => Surefire1733JUnitIT.java} |  39 ++--
 ...arFailsafeIT.java => Surefire1733TestngIT.java} |  39 ++--
 .../com.foo.api/pom.xml                            |  51 +++++
 .../src/main/java/com/foo/api/SomeInterface.java}  |  11 +-
 .../com.foo.api/src/main}/java/module-info.java    |  11 +-
 .../com.foo.impl/pom.xml                           |  85 +++++++
 .../src/main/java/com/foo/impl/Bar.java            |  51 +++++
 .../com.foo.impl/src/main}/java/module-info.java   |   9 +-
 .../src/test/java/com/foo/implt/BarIT.java         |  31 +--
 .../src/test/java/com/foo/implt/BarTest.java       |  31 +--
 .../com.foo.impl}/src/test/java/module-info.java   |  11 +-
 .../maven-multimodule-project-with-jpms/pom.xml    | 101 +++++++++
 .../src/test/java/module-info.java                 |   4 +-
 .../test/resources/surefire-1733-junit4/pom.xml    | 102 +++++++++
 .../src/main/java/main/Service.java}               |  10 +-
 .../src/main}/java/module-info.java                |   9 +-
 .../src/test/java/module-info.java                 |  12 +-
 .../src/test/java/test/MyIT.java                   |  33 +--
 .../src/test/java/test/MyTest.java                 |  33 +--
 .../test/resources/surefire-1733-testng/pom.xml    | 102 +++++++++
 .../src/main/java/main/Service.java}               |  10 +-
 .../src/main}/java/module-info.java                |   9 +-
 .../src/test/java/module-info.java                 |  13 +-
 .../src/test/java/test/MyIT.java                   |  33 +--
 .../src/test/java/test/MyTest.java                 |  34 +--
 43 files changed, 1666 insertions(+), 297 deletions(-)

diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index 8ab5d64..38d34fb 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
@@ -90,6 +90,7 @@ import org.apache.maven.toolchain.DefaultToolchain;
 import org.apache.maven.toolchain.Toolchain;
 import org.apache.maven.toolchain.ToolchainManager;
 import org.apache.maven.toolchain.java.DefaultJavaToolChain;
+import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
 import org.codehaus.plexus.languages.java.jpms.ResolvePathRequest;
 import org.codehaus.plexus.languages.java.jpms.ResolvePathResult;
 import org.codehaus.plexus.logging.Logger;
@@ -124,8 +125,10 @@ import static java.lang.Integer.parseInt;
 import static java.lang.Thread.currentThread;
 import static java.util.Arrays.asList;
 import static java.util.Collections.addAll;
+import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonMap;
+import static org.apache.maven.surefire.booter.Classpath.emptyClasspath;
 import static org.apache.maven.surefire.shared.lang3.StringUtils.substringBeforeLast;
 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
 import static org.apache.maven.plugin.surefire.SurefireDependencyResolver.isWithinVersionSpec;
@@ -1388,27 +1391,32 @@ public abstract class AbstractSurefireMojo
 
     private ResolvePathResultWrapper findModuleDescriptor( File jdkHome )
     {
-        File mainBuildPath = getMainBuildPath();
+        ResolvePathResultWrapper test = findModuleDescriptor( jdkHome, getTestClassesDirectory(), false );
+        return test.getResolvePathResult() == null ? findModuleDescriptor( jdkHome, getMainBuildPath(), true ) : test;
+    }
 
-        if ( mainBuildPath.isDirectory() && !new File( mainBuildPath, "module-info.class" ).exists() )
+    private ResolvePathResultWrapper findModuleDescriptor( File jdkHome, File buildPath, boolean isMainDescriptor )
+    {
+        if ( buildPath.isDirectory() && !new File( buildPath, "module-info.class" ).exists() )
         {
-            return new ResolvePathResultWrapper( null, true );
+            return new ResolvePathResultWrapper( null, isMainDescriptor );
         }
 
         try
         {
-            ResolvePathRequest<?> request = ResolvePathRequest.ofFile( mainBuildPath ).setJdkHome( jdkHome );
+            ResolvePathRequest<?> request = ResolvePathRequest.ofFile( buildPath ).setJdkHome( jdkHome );
             ResolvePathResult result = getLocationManager().resolvePath( request );
-            return new ResolvePathResultWrapper( result.getModuleNameSource() == null ? null : result, true );
+            boolean isEmpty = result.getModuleNameSource() == null;
+            return new ResolvePathResultWrapper( isEmpty ? null : result, isMainDescriptor );
         }
         catch ( Exception e )
         {
-            return new ResolvePathResultWrapper( null, true );
+            return new ResolvePathResultWrapper( null, isMainDescriptor );
         }
     }
 
-    private boolean canExecuteProviderWithModularPath( Platform platform,
-                                                       ResolvePathResultWrapper resolvedJavaModularityResult )
+    private boolean canExecuteProviderWithModularPath( @Nonnull Platform platform,
+                                                       @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult )
     {
         return useModulePath()
                 && platform.getJdkExecAttributesForTests().isJava9AtLeast()
@@ -1875,18 +1883,15 @@ public abstract class AbstractSurefireMojo
     {
         try
         {
-            Set<Artifact> providerArtifacts = provider.getProviderClasspath();
-            String providerName = provider.getProviderName();
             if ( isForking && canExecuteProviderWithModularPath( platform, resolvedJavaModularity ) )
             {
                 File jdkHome = platform.getJdkExecAttributesForTests().getJdkHome();
-                return newStartupConfigWithModularPath( classLoaderConfiguration, providerArtifacts, providerName,
-                    resolvedJavaModularity, scanResult, jdkHome.getAbsolutePath(), testClasspathWrapper );
+                return newStartupConfigWithModularPath( classLoaderConfiguration, provider, resolvedJavaModularity,
+                    scanResult, jdkHome.getAbsolutePath(), testClasspathWrapper );
             }
             else
             {
-                return newStartupConfigWithClasspath( classLoaderConfiguration, providerArtifacts, providerName,
-                        testClasspathWrapper );
+                return newStartupConfigWithClasspath( classLoaderConfiguration, provider, testClasspathWrapper );
             }
         }
         catch ( IOException e )
@@ -1896,11 +1901,12 @@ public abstract class AbstractSurefireMojo
     }
 
     private StartupConfiguration newStartupConfigWithClasspath(
-            @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull Set<Artifact> providerArtifacts,
-            @Nonnull String providerName, @Nonnull TestClassPath testClasspathWrapper )
+        @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull ProviderInfo providerInfo,
+        @Nonnull TestClassPath testClasspathWrapper ) throws MojoExecutionException
     {
         Classpath testClasspath = testClasspathWrapper.toClasspath();
-
+        Set<Artifact> providerArtifacts = providerInfo.getProviderClasspath();
+        String providerName = providerInfo.getProviderName();
         Classpath providerClasspath = classpathCache.getCachedClassPath( providerName );
         if ( providerClasspath == null )
         {
@@ -1921,9 +1927,9 @@ public abstract class AbstractSurefireMojo
 
         ClasspathConfiguration classpathConfiguration = new ClasspathConfiguration( testClasspath, providerClasspath,
                 inProcClasspath, effectiveIsEnableAssertions(), isChildDelegation() );
-
+        ProviderRequirements forkRequirements = new ProviderRequirements( false, false, false );
         return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration,
-            ProcessCheckerType.toEnum( getEnableProcessChecker() ) );
+            ProcessCheckerType.toEnum( getEnableProcessChecker() ), providerInfo.getJpmsArguments( forkRequirements ) );
     }
 
     private static Set<Artifact> retainInProcArtifactsUnique( Set<Artifact> providerArtifacts,
@@ -1975,45 +1981,61 @@ public abstract class AbstractSurefireMojo
     }
 
     private StartupConfiguration newStartupConfigWithModularPath(
-        @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull Set<Artifact> providerArtifacts,
-        @Nonnull String providerName, @Nonnull ResolvePathResultWrapper moduleDescriptor,
-        @Nonnull DefaultScanResult scanResult, @Nonnull String javaHome, @Nonnull TestClassPath testClasspathWrapper )
-            throws IOException
+        @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull ProviderInfo providerInfo,
+        @Nonnull ResolvePathResultWrapper moduleDescriptor, @Nonnull DefaultScanResult scanResult,
+        @Nonnull String javaHome, @Nonnull TestClassPath testClasspathWrapper )
+            throws MojoExecutionException, IOException
     {
-        Classpath testClasspath = testClasspathWrapper.toClasspath();
+        boolean isMainDescriptor = moduleDescriptor.isMainModuleDescriptor();
+        JavaModuleDescriptor javaModuleDescriptor = moduleDescriptor.getResolvePathResult().getModuleDescriptor();
+        SortedSet<String> packages = new TreeSet<>();
 
+        Classpath testClasspath = testClasspathWrapper.toClasspath();
+        Set<Artifact> providerArtifacts = providerInfo.getProviderClasspath();
+        String providerName = providerInfo.getProviderName();
         Classpath providerClasspath = classpathCache.getCachedClassPath( providerName );
         if ( providerClasspath == null )
         {
             providerClasspath = classpathCache.setCachedClasspath( providerName, providerArtifacts );
         }
 
-        ResolvePathsRequest<String> req = ResolvePathsRequest.ofStrings( testClasspath.getClassPath() )
-                .setJdkHome( javaHome )
-                .setModuleDescriptor( moduleDescriptor.getResolvePathResult().getModuleDescriptor() );
-
-        ResolvePathsResult<String> result = getLocationManager().resolvePaths( req );
-        for ( Entry<String, Exception> entry : result.getPathExceptions().entrySet() )
+        final ProviderRequirements providerRequirements;
+        final Classpath testModulepath;
+        if ( isMainDescriptor )
         {
-            // Probably JDK version < 9. Other known causes: passing a non-jar or a corrupted jar.
-            getConsoleLogger()
-                    .warning( "Exception for '" + entry.getKey() + "'.", entry.getValue() );
-        }
+            providerRequirements = new ProviderRequirements( true, true, false );
+            ResolvePathsRequest<String> req = ResolvePathsRequest.ofStrings( testClasspath.getClassPath() )
+                    .setJdkHome( javaHome )
+                    .setModuleDescriptor( javaModuleDescriptor );
 
-        testClasspath = new Classpath( result.getClasspathElements() );
-        Classpath testModulepath = new Classpath( result.getModulepathElements().keySet() );
+            ResolvePathsResult<String> result = getLocationManager().resolvePaths( req );
+            for ( Entry<String, Exception> entry : result.getPathExceptions().entrySet() )
+            {
+                // Probably JDK version < 9. Other known causes: passing a non-jar or a corrupted jar.
+                getConsoleLogger()
+                        .warning( "Exception for '" + entry.getKey() + "'.", entry.getValue() );
+            }
 
-        SortedSet<String> packages = new TreeSet<>();
+            testClasspath = new Classpath( result.getClasspathElements() );
+            testModulepath = new Classpath( result.getModulepathElements().keySet() );
 
-        for ( String className : scanResult.getClasses() )
+            for ( String className : scanResult.getClasses() )
+            {
+                packages.add( substringBeforeLast( className, "." ) );
+            }
+        }
+        else
         {
-            packages.add( substringBeforeLast( className, "." ) );
+            providerRequirements = new ProviderRequirements( true, false, true );
+            testModulepath = testClasspath;
+            testClasspath = emptyClasspath();
         }
 
-        getConsoleLogger().debug( "main module descriptor name: " + result.getMainModuleDescriptor().name() );
+        getConsoleLogger().debug( "main module descriptor name: " + javaModuleDescriptor.name() );
 
-        ModularClasspath modularClasspath = new ModularClasspath( result.getMainModuleDescriptor().name(),
-                testModulepath.getClassPath(), packages, getTestClassesDirectory() );
+        ModularClasspath modularClasspath = new ModularClasspath( javaModuleDescriptor.name(),
+            testModulepath.getClassPath(), packages, isMainDescriptor ? getTestClassesDirectory() : null,
+            isMainDescriptor );
 
         Artifact[] additionalInProcArtifacts = { getCommonArtifact(), getBooterArtifact(), getExtensionsArtifact(),
             getApiArtifact(), getSpiArtifact(), getLoggerApiArtifact(), getSurefireSharedUtilsArtifact() };
@@ -2032,8 +2054,10 @@ public abstract class AbstractSurefireMojo
         getConsoleLogger().debug( inProcClasspath.getLogMessage( "in-process classpath:" ) );
         getConsoleLogger().debug( inProcClasspath.getCompactLogMessage( "in-process(compact) classpath:" ) );
 
+        ProcessCheckerType processCheckerType = ProcessCheckerType.toEnum( getEnableProcessChecker() );
+        List<String[]> jpmsArgs = providerInfo.getJpmsArguments( providerRequirements );
         return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration,
-            ProcessCheckerType.toEnum( getEnableProcessChecker() ) );
+            processCheckerType, jpmsArgs );
     }
 
     private Artifact getCommonArtifact()
@@ -2344,7 +2368,7 @@ public abstract class AbstractSurefireMojo
                                            @Nonnull RunOrderParameters runOrderParameters, @Nonnull ConsoleLogger log,
                                            @Nonnull DefaultScanResult scanResult,
                                            @Nonnull TestClassPath testClasspathWrapper, @Nonnull Platform platform,
-                                           ResolvePathResultWrapper resolvedJavaModularityResult )
+                                           @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult )
         throws MojoExecutionException, MojoFailureException
     {
         StartupConfiguration startupConfiguration = createStartupConfiguration( provider, true,
@@ -3039,6 +3063,13 @@ public abstract class AbstractSurefireMojo
             convertTestNGParameters();
         }
 
+        @Nonnull
+        @Override
+        public List<String[]> getJpmsArguments( @Nonnull ProviderRequirements forkRequirements )
+        {
+            return emptyList();
+        }
+
         @Override
         @Nonnull
         public Set<Artifact> getProviderClasspath()
@@ -3069,6 +3100,13 @@ public abstract class AbstractSurefireMojo
         {
         }
 
+        @Nonnull
+        @Override
+        public List<String[]> getJpmsArguments( @Nonnull ProviderRequirements forkRequirements )
+        {
+            return emptyList();
+        }
+
         @Override
         @Nonnull
         public Set<Artifact> getProviderClasspath()
@@ -3110,6 +3148,13 @@ public abstract class AbstractSurefireMojo
         {
         }
 
+        @Nonnull
+        @Override
+        public List<String[]> getJpmsArguments( @Nonnull ProviderRequirements forkRequirements )
+        {
+            return emptyList();
+        }
+
         @Override
         @Nonnull
         public Set<Artifact> getProviderClasspath()
@@ -3153,6 +3198,14 @@ public abstract class AbstractSurefireMojo
             convertGroupParameters();
         }
 
+        @Nonnull
+        @Override
+        public List<String[]> getJpmsArguments( @Nonnull ProviderRequirements forkRequirements )
+        {
+            boolean hasTestDescriptor = forkRequirements.isModularPath() && forkRequirements.hasTestModuleDescriptor();
+            return hasTestDescriptor ? getJpmsArgs() : Collections.<String[]>emptyList();
+        }
+
         @Override
         @Nonnull
         public Set<Artifact> getProviderClasspath() throws MojoExecutionException
@@ -3209,6 +3262,23 @@ public abstract class AbstractSurefireMojo
             return new LinkedHashSet<>( providerArtifacts.values() );
         }
 
+        private List<String[]> getJpmsArgs()
+        {
+            List<String[]> args = new ArrayList<>();
+
+            args.add( new String[] {
+                "--add-opens",
+                "org.junit.platform.commons/org.junit.platform.commons.util=ALL-UNNAMED"
+            } );
+
+            args.add( new String[] {
+                "--add-opens",
+                "org.junit.platform.commons/org.junit.platform.commons.logging=ALL-UNNAMED"
+            } );
+
+            return args;
+        }
+
         private void addEngineByApi( String engineGroupId, String engineArtifactId, String engineVersion,
                                      Map<String, Artifact> providerArtifacts )
         {
@@ -3306,6 +3376,13 @@ public abstract class AbstractSurefireMojo
             convertGroupParameters();
         }
 
+        @Nonnull
+        @Override
+        public List<String[]> getJpmsArguments( @Nonnull ProviderRequirements forkRequirements )
+        {
+            return emptyList();
+        }
+
         @Override
         @Nonnull
         public Set<Artifact> getProviderClasspath()
@@ -3355,6 +3432,13 @@ public abstract class AbstractSurefireMojo
             convertTestNGParameters();
         }
 
+        @Nonnull
+        @Override
+        public List<String[]> getJpmsArguments( @Nonnull ProviderRequirements forkRequirements )
+        {
+            return emptyList();
+        }
+
         @Override
         @Nonnull
         public Set<Artifact> getProviderClasspath() throws MojoExecutionException
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 fea74fd..66dd493 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
@@ -23,6 +23,7 @@ import org.apache.maven.artifact.Artifact;
 import org.apache.maven.plugin.MojoExecutionException;
 
 import javax.annotation.Nonnull;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -39,4 +40,7 @@ public interface ProviderInfo
     Set<Artifact> getProviderClasspath() throws MojoExecutionException;
 
     void addProviderProperties() throws MojoExecutionException;
+
+    @Nonnull
+    List<String[]> getJpmsArguments( @Nonnull ProviderRequirements forkRequirements );
 }
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/ProviderRequirements.java
similarity index 51%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
copy to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderRequirements.java
index fea74fd..793667a 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/ProviderRequirements.java
@@ -19,24 +19,36 @@ 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;
-
 /**
- * @author Kristian Rosenvold
+ * Used to get additional provider-specific JVM arguments.
+ *
+ * @see ProviderInfo#getJpmsArguments(ProviderRequirements)
  */
-public interface ProviderInfo
+final class ProviderRequirements
 {
-    @Nonnull
-    String getProviderName();
+    private final boolean modularPath;
+    private final boolean mainModuleDescriptor;
+    private final boolean testModuleDescriptor;
+
+    ProviderRequirements( boolean modularPath, boolean mainModuleDescriptor, boolean testModuleDescriptor )
+    {
+        this.modularPath = modularPath;
+        this.mainModuleDescriptor = mainModuleDescriptor;
+        this.testModuleDescriptor = testModuleDescriptor;
+    }
 
-    boolean isApplicable();
+    boolean isModularPath()
+    {
+        return modularPath;
+    }
 
-    @Nonnull
-    Set<Artifact> getProviderClasspath() throws MojoExecutionException;
+    boolean hasMainModuleDescriptor()
+    {
+        return mainModuleDescriptor;
+    }
 
-    void addProviderProperties() throws MojoExecutionException;
+    boolean hasTestModuleDescriptor()
+    {
+        return testModuleDescriptor;
+    }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
index 5336ac6..a2c38b8 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
@@ -170,7 +170,7 @@ public abstract class DefaultForkConfiguration
     }
 
     @Nonnull
-    protected List<String> toCompleteClasspath( StartupConfiguration conf ) throws SurefireBooterForkException
+    protected List<String> toCompleteClasspath( @Nonnull StartupConfiguration conf ) throws SurefireBooterForkException
     {
         AbstractPathConfiguration pathConfig = conf.getClasspathConfiguration();
         if ( pathConfig.isClassPathConfig() == pathConfig.isModularPathConfig() )
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
index 52729c3..16e8905 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
@@ -45,8 +45,8 @@ import java.util.Properties;
 import static java.io.File.createTempFile;
 import static java.io.File.pathSeparatorChar;
 import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
-import static org.apache.maven.surefire.shared.utils.StringUtils.replace;
 import static org.apache.maven.surefire.api.util.internal.StringUtils.NL;
+import static org.apache.maven.surefire.shared.utils.StringUtils.replace;
 
 /**
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
@@ -90,13 +90,15 @@ public class ModularClasspathForkConfiguration
 
             ModularClasspath modularClasspath = modularClasspathConfiguration.getModularClasspath();
 
+            boolean isMainDescriptor = modularClasspath.isMainDescriptor();
             String moduleName = modularClasspath.getModuleNameFromDescriptor();
             List<String> modulePath = modularClasspath.getModulePath();
             Collection<String> packages = modularClasspath.getPackages();
             File patchFile = modularClasspath.getPatchFile();
             List<String> classpath = toCompleteClasspath( config );
 
-            File argsFile = createArgsFile( moduleName, modulePath, classpath, packages, patchFile, startClass );
+            File argsFile = createArgsFile( moduleName, modulePath, classpath, packages, patchFile, startClass,
+                isMainDescriptor, config.getJpmsArguments() );
 
             cli.createArg().setValue( "@" + escapeToPlatformPath( argsFile.getAbsolutePath() ) );
         }
@@ -112,7 +114,8 @@ public class ModularClasspathForkConfiguration
     @Nonnull
     File createArgsFile( @Nonnull String moduleName, @Nonnull List<String> modulePath,
                          @Nonnull List<String> classPath, @Nonnull Collection<String> packages,
-                         @Nonnull File patchFile, @Nonnull String startClassName )
+                         File patchFile, @Nonnull String startClassName, boolean isMainDescriptor,
+                         @Nonnull List<String[]> providerJpmsArguments )
             throws IOException
     {
         File surefireArgs = createTempFile( "surefireargs", "", getTempDirectory() );
@@ -167,38 +170,57 @@ public class ModularClasspathForkConfiguration
                         .append( NL );
             }
 
-            args.append( "--patch-module" )
-                    .append( NL )
-                    .append( moduleName )
-                    .append( '=' )
-                    .append( '"' )
-                    .append( replace( patchFile.getPath(), "\\", "\\\\" ) )
-                    .append( '"' )
-                    .append( NL );
-
-            for ( String pkg : packages )
+            if ( isMainDescriptor )
             {
-                args.append( "--add-exports" )
+                args.append( "--patch-module" )
+                        .append( NL )
+                        .append( moduleName )
+                        .append( '=' )
+                        .append( '"' )
+                        .append( replace( patchFile.getPath(), "\\", "\\\\" ) )
+                        .append( '"' )
+                        .append( NL );
+
+                for ( String pkg : packages )
+                {
+                    args.append( "--add-exports" )
+                            .append( NL )
+                            .append( moduleName )
+                            .append( '/' )
+                            .append( pkg )
+                            .append( '=' )
+                            .append( "ALL-UNNAMED" )
+                            .append( NL );
+                }
+
+                args.append( "--add-modules" )
+                        .append( NL )
+                        .append( moduleName )
+                        .append( NL );
+
+                args.append( "--add-reads" )
                         .append( NL )
                         .append( moduleName )
-                        .append( '/' )
-                        .append( pkg )
                         .append( '=' )
                         .append( "ALL-UNNAMED" )
                         .append( NL );
             }
+            else
+            {
+                for ( String[] entries : providerJpmsArguments )
+                {
+                    for ( String entry : entries )
+                    {
+                        args.append( entry )
+                            .append( NL );
+                    }
+                }
 
-            args.append( "--add-modules" )
-                    .append( NL )
-                    .append( moduleName )
-                    .append( NL );
-
-            args.append( "--add-reads" )
+                args.append( "--add-modules" )
                     .append( NL )
                     .append( moduleName )
-                    .append( '=' )
-                    .append( "ALL-UNNAMED" )
                     .append( NL );
+            }
 
             args.append( startClassName );
 
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 d02c7fd..7d5343c 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
@@ -58,8 +58,10 @@ import static java.util.Collections.singleton;
 import static java.util.Collections.singletonMap;
 import static org.apache.maven.artifact.versioning.VersionRange.createFromVersion;
 import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Index.atIndex;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -134,10 +136,10 @@ public class AbstractSurefireMojoJava7PlusTest
         ClassLoaderConfiguration classLoaderConfiguration = new ClassLoaderConfiguration( false, true );
 
         VersionRange v5 = createFromVersion( "1" );
-        Artifact provider = new DefaultArtifact( "org.apache.maven.surefire", "surefire-provider", v5, "runtime",
-                "jar", "", handler );
-        provider.setFile( mockFile( "surefire-provider.jar" ) );
-        Set<Artifact> providerClasspath = singleton( provider );
+        Artifact providerArtifact = new DefaultArtifact( "org.apache.maven.surefire", "surefire-provider",
+            v5, "runtime", "jar", "", handler );
+        providerArtifact.setFile( mockFile( "surefire-provider.jar" ) );
+        Set<Artifact> providerClasspath = singleton( providerArtifact );
 
         ResolvePathResult moduleInfo = mock( ResolvePathResult.class );
         when( moduleInfo.getModuleDescriptor() ).thenReturn( descriptor );
@@ -205,8 +207,12 @@ public class AbstractSurefireMojoJava7PlusTest
         artifacts.put( "org.apache.maven.surefire:surefire-shared-utils", utils );
         when( mojo.getPluginArtifactMap() ).thenReturn( artifacts );
 
+        ProviderInfo providerInfo = mock( ProviderInfo.class );
+        when( providerInfo.getProviderName() ).thenReturn( "org.asf.Provider" );
+        when( providerInfo.getProviderClasspath() ).thenReturn( providerClasspath );
+
         StartupConfiguration conf = invokeMethod( mojo, "newStartupConfigWithModularPath",
-            classLoaderConfiguration, providerClasspath, "org.asf.Provider",
+            classLoaderConfiguration, providerInfo,
             new ResolvePathResultWrapper( moduleInfo, true ), scanResult, "", testClasspath );
 
         verify( mojo, times( 1 ) ).effectiveIsEnableAssertions();
@@ -262,6 +268,204 @@ public class AbstractSurefireMojoJava7PlusTest
                 .containsExactly( "modular.jar", "classes" );
     }
 
+    @Test
+    @SuppressWarnings( "checkstyle:linelength" )
+    public void shouldHaveStartupConfigForModularClasspathAndTestDescriptor()
+        throws Exception
+    {
+        AbstractSurefireMojo mojo = spy( new Mojo() );
+        doReturn( locationManager )
+            .when( mojo, "getLocationManager" );
+
+        when( handler.isAddedToClasspath() ).thenReturn( true );
+
+        VersionRange v1 = createFromVersion( "1" );
+        Artifact modular = new DefaultArtifact( "x", "modular", v1, "compile", "jar", "", handler );
+        modular.setFile( mockFile( "modular.jar" ) );
+
+        VersionRange v2 = createFromVersion( "1" );
+        Artifact nonModular = new DefaultArtifact( "x", "non-modular", v2, "test", "jar", "", handler );
+        nonModular.setFile( mockFile( "non-modular.jar" ) );
+
+        VersionRange v3 = createFromVersion( "4.12" );
+        Artifact junit = new DefaultArtifact( "junit", "junit", v3, "test", "jar", "", handler );
+        junit.setFile( mockFile( "junit.jar" ) );
+
+        VersionRange v4 = createFromVersion( "1.3.0" );
+        Artifact hamcrest = new DefaultArtifact( "org.hamcrest", "hamcrest-core", v4, "test", "jar", "", handler );
+        hamcrest.setFile( mockFile( "hamcrest.jar" ) );
+
+        File classesDir = mockFile( "classes" );
+        File testClassesDir = mockFile( "test-classes" );
+
+        TestClassPath testClasspath =
+            new TestClassPath( asList( modular, nonModular, junit, hamcrest ), classesDir, testClassesDir,
+                null );
+
+        doReturn( testClasspath ).when( mojo, "generateTestClasspath" );
+        doReturn( 1 ).when( mojo, "getEffectiveForkCount" );
+        doReturn( true ).when( mojo, "effectiveIsEnableAssertions" );
+        when( mojo.isChildDelegation() ).thenReturn( false );
+        when( mojo.getTestClassesDirectory() ).thenReturn( testClassesDir );
+
+        DefaultScanResult scanResult = mock( DefaultScanResult.class );
+        when( scanResult.getClasses() ).thenReturn( asList( "org.apache.A", "org.apache.B" ) );
+
+        ClassLoaderConfiguration classLoaderConfiguration = new ClassLoaderConfiguration( false, true );
+
+        VersionRange v5 = createFromVersion( "1" );
+        Artifact providerArtifact = new DefaultArtifact( "org.apache.maven.surefire", "surefire-provider",
+            v5, "runtime", "jar", "", handler );
+        providerArtifact.setFile( mockFile( "surefire-provider.jar" ) );
+        Set<Artifact> providerClasspath = singleton( providerArtifact );
+
+        ResolvePathResult moduleInfo = mock( ResolvePathResult.class );
+        when( moduleInfo.getModuleDescriptor() ).thenReturn( descriptor );
+
+        when( descriptor.name() ).thenReturn( "abc" );
+
+        Logger logger = mock( Logger.class );
+        when( logger.isDebugEnabled() ).thenReturn( true );
+        doNothing().when( logger ).debug( anyString() );
+        when( mojo.getConsoleLogger() ).thenReturn( new PluginConsoleLogger( logger ) );
+
+        Artifact common = new DefaultArtifact( "org.apache.maven.surefire", "maven-surefire-common", v5, "runtime",
+            "jar", "", handler );
+        common.setFile( mockFile( "maven-surefire-common.jar" ) );
+
+        Artifact ext = new DefaultArtifact( "org.apache.maven.surefire", "surefire-extensions-api", v5, "runtime",
+            "jar", "", handler );
+        ext.setFile( mockFile( "surefire-extensions-api.jar" ) );
+
+        Artifact api = new DefaultArtifact( "org.apache.maven.surefire", "surefire-api", v5, "runtime",
+            "jar", "", handler );
+        api.setFile( mockFile( "surefire-api.jar" ) );
+
+        Artifact loggerApi = new DefaultArtifact( "org.apache.maven.surefire", "surefire-logger-api", v5, "runtime",
+            "jar", "", handler );
+        loggerApi.setFile( mockFile( "surefire-logger-api.jar" ) );
+
+        Artifact spi = new DefaultArtifact( "org.apache.maven.surefire", "surefire-extensions-spi",
+            createFromVersion( "1" ), "runtime", "jar", "", handler );
+        spi.setFile( mockFile( "surefire-extensions-spi.jar" ) );
+
+        Artifact booter = new DefaultArtifact( "org.apache.maven.surefire", "surefire-booter",
+            createFromVersion( "1" ), "runtime", "jar", "", handler );
+        booter.setFile( mockFile( "surefire-booter.jar" ) );
+
+        Artifact utils = new DefaultArtifact( "org.apache.maven.surefire", "surefire-shared-utils",
+            createFromVersion( "1" ), "runtime", "jar", "", handler );
+        utils.setFile( mockFile( "surefire-shared-utils.jar" ) );
+
+        Map<String, Artifact> artifacts = new HashMap<>();
+        artifacts.put( "org.apache.maven.surefire:maven-surefire-common", common );
+        artifacts.put( "org.apache.maven.surefire:surefire-extensions-api", ext );
+        artifacts.put( "org.apache.maven.surefire:surefire-api", api );
+        artifacts.put( "org.apache.maven.surefire:surefire-logger-api", loggerApi );
+        artifacts.put( "org.apache.maven.surefire:surefire-extensions-spi", spi );
+        artifacts.put( "org.apache.maven.surefire:surefire-booter", booter );
+        artifacts.put( "org.apache.maven.surefire:surefire-shared-utils", utils );
+        when( mojo.getPluginArtifactMap() ).thenReturn( artifacts );
+
+        ProviderInfo providerInfo = mock( ProviderInfo.class );
+        when( providerInfo.getProviderName() ).thenReturn( "org.asf.Provider" );
+        when( providerInfo.getProviderClasspath() ).thenReturn( providerClasspath );
+
+        StartupConfiguration conf = invokeMethod( mojo, "newStartupConfigWithModularPath",
+            classLoaderConfiguration, providerInfo,
+            new ResolvePathResultWrapper( moduleInfo, false ), scanResult, "", testClasspath );
+
+        verify( mojo, times( 1 ) ).effectiveIsEnableAssertions();
+        verify( mojo, times( 1 ) ).isChildDelegation();
+        verify( mojo, never() ).getTestClassesDirectory();
+        ArgumentCaptor<String> argument = ArgumentCaptor.forClass( String.class );
+        verify( logger, times( 9 ) ).debug( argument.capture() );
+        assertThat( argument.getAllValues() )
+            .containsExactly( "main module descriptor name: abc",
+                "test classpath:",
+                "test modulepath:  test-classes  classes  modular.jar  non-modular.jar  junit.jar  hamcrest.jar",
+                "provider classpath:  surefire-provider.jar",
+                "test(compact) classpath:",
+                "test(compact) modulepath:  test-classes  classes  modular.jar  non-modular.jar  junit.jar  hamcrest.jar",
+                "provider(compact) classpath:  surefire-provider.jar",
+                "in-process classpath:  surefire-provider.jar  maven-surefire-common.jar  surefire-booter.jar  surefire-extensions-api.jar  surefire-api.jar  surefire-extensions-spi.jar  surefire-logger-api.jar  surefire-shared-utils.jar",
+                "in-process(compact) classpath:  surefire-provider.jar  maven-surefire-common.jar  surefire-booter.jar  surefire-extensions-api.jar  surefire-api.jar  surefire-extensions-spi.jar  surefire-logger-api.jar  surefire-shared-utils.jar"
+            );
+
+        assertThat( conf ).isNotNull();
+        assertThat( conf.isShadefire() ).isFalse();
+        assertThat( conf.isProviderMainClass() ).isFalse();
+        assertThat( conf.isManifestOnlyJarRequestedAndUsable() ).isFalse();
+        assertThat( conf.getClassLoaderConfiguration() ).isSameAs( classLoaderConfiguration );
+        assertThat( conf.getProviderClassName() ).isEqualTo( "org.asf.Provider" );
+        assertThat( conf.getActualClassName() ).isEqualTo( "org.asf.Provider" );
+        assertThat( conf.getClasspathConfiguration() ).isNotNull();
+        assertThat( ( Object ) conf.getClasspathConfiguration().getTestClasspath() )
+            .isEqualTo( Classpath.emptyClasspath() );
+        assertThat( ( Object ) conf.getClasspathConfiguration().getProviderClasspath() )
+            .isEqualTo( new Classpath( singleton( "surefire-provider.jar" ) ) );
+        assertThat( conf.getClasspathConfiguration() ).isInstanceOf( ModularClasspathConfiguration.class );
+        ModularClasspathConfiguration mcc = ( ModularClasspathConfiguration ) conf.getClasspathConfiguration();
+        assertThat( mcc.getModularClasspath().getModuleNameFromDescriptor() ).isEqualTo( "abc" );
+        assertThat( mcc.getModularClasspath().getPackages() ).isEmpty();
+        assertThat( mcc.getModularClasspath().getPatchFile() )
+            .isNull();
+        assertThat( mcc.getModularClasspath().getModulePath() )
+            .hasSize( 6 )
+            .containsSequence( "test-classes", "classes", "modular.jar", "non-modular.jar",
+                "junit.jar", "hamcrest.jar" );
+    }
+
+    @Test
+    public void testAllProviderInfo()
+    {
+        Mojo mojo = new Mojo();
+
+        ProviderRequirements providerRequirements = new ProviderRequirements( true, false, true );
+
+        ProviderInfo providerInfo = mojo.newDynamicProviderInfo();
+        assertThat( providerInfo.getProviderName() )
+            .isEqualTo( "custom.Provider" );
+        assertThat( providerInfo.getJpmsArguments( providerRequirements ) )
+            .isEmpty();
+
+        providerInfo = mojo.newJUnit3ProviderInfo();
+        assertThat( providerInfo.getProviderName() )
+            .isEqualTo( "org.apache.maven.surefire.junit.JUnit3Provider" );
+        assertThat( providerInfo.getJpmsArguments( providerRequirements ) )
+            .isEmpty();
+
+        providerInfo = mojo.newJUnit4ProviderInfo();
+        assertThat( providerInfo.getProviderName() )
+            .isEqualTo( "org.apache.maven.surefire.junit4.JUnit4Provider" );
+        assertThat( providerInfo.getJpmsArguments( providerRequirements ) )
+            .isEmpty();
+
+        providerInfo = mojo.newJUnit47ProviderInfo();
+        assertThat( providerInfo.getProviderName() )
+            .isEqualTo( "org.apache.maven.surefire.junitcore.JUnitCoreProvider" );
+        assertThat( providerInfo.getJpmsArguments( providerRequirements ) )
+            .isEmpty();
+
+        providerInfo = mojo.newTestNgProviderInfo();
+        assertThat( providerInfo.getProviderName() )
+            .isEqualTo( "org.apache.maven.surefire.testng.TestNGProvider" );
+        assertThat( providerInfo.getJpmsArguments( providerRequirements ) )
+            .isEmpty();
+
+        providerInfo = mojo.newJUnitPlatformProviderInfo();
+        assertThat( providerInfo.getProviderName() )
+            .isEqualTo( "org.apache.maven.surefire.junitplatform.JUnitPlatformProvider" );
+        List<String[]> args = providerInfo.getJpmsArguments( providerRequirements );
+        assertThat( args )
+            .isNotEmpty()
+            .hasSize( 2 )
+            .contains( new String[] {
+            "--add-opens", "org.junit.platform.commons/org.junit.platform.commons.util=ALL-UNNAMED"}, atIndex( 0 ) )
+            .contains( new String[] {
+            "--add-opens", "org.junit.platform.commons/org.junit.platform.commons.logging=ALL-UNNAMED"}, atIndex( 1 ) );
+    }
+
     private static File mockFile( String absolutePath )
     {
         File f = mock( File.class );
@@ -275,6 +479,35 @@ public class AbstractSurefireMojoJava7PlusTest
     public static class Mojo
             extends AbstractSurefireMojo
     {
+        ProviderInfo newDynamicProviderInfo()
+        {
+            return new DynamicProviderInfo( "custom.Provider" );
+        }
+
+        ProviderInfo newJUnit3ProviderInfo()
+        {
+            return new JUnit3ProviderInfo();
+        }
+
+        ProviderInfo newJUnit4ProviderInfo()
+        {
+            return new JUnit4ProviderInfo( null, null );
+        }
+
+        ProviderInfo newJUnit47ProviderInfo()
+        {
+            return new JUnitCoreProviderInfo( null, null );
+        }
+
+        ProviderInfo newTestNgProviderInfo()
+        {
+            return new TestNgProviderInfo( null );
+        }
+
+        ProviderInfo newJUnitPlatformProviderInfo()
+        {
+            return new JUnitPlatformProviderInfo( null, null );
+        }
 
         @Override
         protected String getPluginName()
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 0440816..a08224c 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
@@ -165,6 +165,8 @@ public class AbstractSurefireMojoTest
     {
         AbstractSurefireMojo mojo = spy( new Mojo() );
         mojo.setMainBuildPath( tempFolder.newFolder() );
+        File testClassesDir = tempFolder.newFolder();
+        mojo.setTestClassesDirectory( testClassesDir );
         File jdkHome = new File( System.getProperty( "java.home" ) );
         ResolvePathResultWrapper wrapper = invokeMethod( mojo, "findModuleDescriptor", jdkHome );
 
@@ -200,6 +202,8 @@ public class AbstractSurefireMojoTest
             .when( mojo, "getLocationManager" );
         File classesDir = tempFolder.newFolder();
         mojo.setMainBuildPath( classesDir );
+        File testClassesDir = tempFolder.newFolder();
+        mojo.setTestClassesDirectory( testClassesDir );
         File descriptorFile = new File( classesDir, "module-info.class" );
         assertThat( descriptorFile.createNewFile() ).isTrue();
         File jdkHome = new File( System.getProperty( "java.home" ) );
@@ -255,6 +259,8 @@ public class AbstractSurefireMojoTest
             .when( mojo, "getLocationManager" );
         File classesDir = tempFolder.newFolder();
         mojo.setMainBuildPath( classesDir );
+        File testClassesDir = tempFolder.newFolder();
+        mojo.setTestClassesDirectory( testClassesDir );
 
         File descriptorFile = new File( classesDir, "module-info.class" );
         assertThat( descriptorFile.createNewFile() ).isTrue();
@@ -523,8 +529,12 @@ public class AbstractSurefireMojoTest
         doNothing().when( logger ).debug( anyString() );
         when( mojo.getConsoleLogger() ).thenReturn( new PluginConsoleLogger( logger ) );
 
+        ProviderInfo providerInfo = mock( ProviderInfo.class );
+        when( providerInfo.getProviderName() ).thenReturn( "org.asf.Provider" );
+        when( providerInfo.getProviderClasspath() ).thenReturn( providerArtifacts );
+
         StartupConfiguration conf = invokeMethod( mojo, "newStartupConfigWithClasspath",
-                classLoaderConfiguration, providerArtifacts, "org.asf.Provider", testClasspath );
+                classLoaderConfiguration, providerInfo, testClasspath );
 
         verify( mojo, times( 1 ) ).effectiveIsEnableAssertions();
         verify( mojo, times( 1 ) ).isChildDelegation();
@@ -661,9 +671,6 @@ public class AbstractSurefireMojoTest
         when( resolvePathsResult.getPathExceptions() ).thenReturn( emptyMap() );
         when( resolvePathsResult.getClasspathElements() ).thenReturn( emptyList() );
         when( resolvePathsResult.getModulepathElements() ).thenReturn( emptyMap() );
-        JavaModuleDescriptor desc = mock( JavaModuleDescriptor.class );
-        when( desc.name() ).thenReturn( "" );
-        when( resolvePathsResult.getMainModuleDescriptor() ).thenReturn( desc );
 
         mojo.setLogger( mock( Logger.class ) );
         mojo.setUseModulePath( true );
@@ -676,6 +683,7 @@ public class AbstractSurefireMojoTest
 
         File classesDirectory = new File( baseDir, "mock-dir" );
         File testClassesDirectory = new File( baseDir, "mock-dir" );
+        mojo.setTestClassesDirectory( testClassesDirectory );
         TestClassPath testClassPath = new TestClassPath( Collections.<Artifact>emptySet(),
             classesDirectory, testClassesDirectory, new String[0] );
 
@@ -703,6 +711,9 @@ public class AbstractSurefireMojoTest
         mojo.setPluginArtifactMap( artifacts );
 
         ResolvePathResult resolvePathResult = mock( ResolvePathResult.class );
+        JavaModuleDescriptor desc = mock( JavaModuleDescriptor.class );
+        when( desc.name() ).thenReturn( "" );
+        when( resolvePathResult.getModuleDescriptor() ).thenReturn( desc );
         ResolvePathResultWrapper wrapper = new ResolvePathResultWrapper( resolvePathResult, true );
         // ### END
 
@@ -2013,6 +2024,7 @@ public class AbstractSurefireMojoTest
             extends AbstractSurefireMojo implements SurefireReportParameters
     {
         private File mainBuildPath;
+        private File testClassesDirectory;
         private boolean useModulePath;
 
         private JUnitPlatformProviderInfo createJUnitPlatformProviderInfo( Artifact junitPlatformArtifact,
@@ -2102,13 +2114,13 @@ public class AbstractSurefireMojoTest
         @Override
         public File getTestClassesDirectory()
         {
-            return null;
+            return testClassesDirectory;
         }
 
         @Override
         public void setTestClassesDirectory( File testClassesDirectory )
         {
-
+            this.testClassesDirectory = testClassesDirectory;
         }
 
         @Override
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
index ac3eba5..7a08390 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
@@ -286,7 +286,8 @@ public class BooterDeserializerProviderConfigurationTest
     {
         ClasspathConfiguration classpathConfiguration = new ClasspathConfiguration( true, true );
 
-        return new StartupConfiguration( "com.provider", classpathConfiguration, classLoaderConfiguration, ALL );
+        return new StartupConfiguration( "com.provider", classpathConfiguration, classLoaderConfiguration, ALL,
+            Collections.<String[]>emptyList() );
     }
 
     private File getTestSourceDirectory()
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
index 5a2d6cf..6fe2ddc 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
@@ -46,6 +46,7 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 
@@ -142,7 +143,7 @@ public class BooterDeserializerStartupConfigurationTest
     public void testProcessCheckerNull() throws IOException
     {
         StartupConfiguration startupConfiguration = new StartupConfiguration( "com.provider", classpathConfiguration,
-                getManifestOnlyJarForkConfiguration(), null );
+                getManifestOnlyJarForkConfiguration(), null, Collections.<String[]>emptyList() );
         assertNull( saveAndReload( startupConfiguration ).getProcessChecker() );
     }
 
@@ -204,7 +205,8 @@ public class BooterDeserializerStartupConfigurationTest
 
     private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration )
     {
-        return new StartupConfiguration( "com.provider", classpathConfiguration, classLoaderConfiguration, ALL );
+        return new StartupConfiguration( "com.provider", classpathConfiguration, classLoaderConfiguration, ALL,
+            Collections.<String[]>emptyList() );
     }
 
     private File getTestSourceDirectory()
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java
index 45a6b4a..1ed7a10 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java
@@ -37,6 +37,7 @@ import org.powermock.modules.junit4.PowerMockRunner;
 
 import javax.annotation.Nonnull;
 import java.io.File;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
@@ -307,7 +308,7 @@ public class DefaultForkConfigurationTest
         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
         ClasspathConfiguration cc = new ClasspathConfiguration( true, true );
         StartupConfiguration conf = new StartupConfiguration( "org.apache.maven.shadefire.surefire.MyProvider",
-                cc, clc, null );
+                cc, clc, null, Collections.<String[]>emptyList() );
         StartupConfiguration confMock = spy( conf );
         mockStatic( Relocator.class );
         when( Relocator.relocate( anyString() ) ).thenCallRealMethod();
@@ -327,8 +328,8 @@ public class DefaultForkConfigurationTest
     {
         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
         ClasspathConfiguration cc = new ClasspathConfiguration( true, true );
-        StartupConfiguration conf =
-                new StartupConfiguration( "org.apache.maven.surefire.MyProvider", cc, clc, null );
+        StartupConfiguration conf = new StartupConfiguration( "org.apache.maven.surefire.MyProvider",
+            cc, clc, null, Collections.<String[]>emptyList() );
         StartupConfiguration confMock = spy( conf );
         mockStatic( Relocator.class );
         when( Relocator.relocate( anyString() ) ).thenCallRealMethod();
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
index bc01ee8..b1db277 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
@@ -19,31 +19,44 @@ package org.apache.maven.plugin.surefire.booterclient;
  * under the License.
  */
 
-import org.apache.maven.surefire.shared.io.FileUtils;
-import org.apache.maven.surefire.shared.lang3.SystemUtils;
 import org.apache.maven.plugin.surefire.JdkAttributes;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
-import org.apache.maven.surefire.shared.utils.StringUtils;
-import org.apache.maven.surefire.shared.utils.cli.Commandline;
 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
 import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.ClasspathConfiguration;
+import org.apache.maven.surefire.booter.ModularClasspath;
+import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
 import org.apache.maven.surefire.extensions.ForkNodeFactory;
+import org.apache.maven.surefire.shared.io.FileUtils;
+import org.apache.maven.surefire.shared.lang3.SystemUtils;
+import org.apache.maven.surefire.shared.utils.StringUtils;
+import org.apache.maven.surefire.shared.utils.cli.Commandline;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import javax.annotation.Nonnull;
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 
+import static java.nio.file.Files.readAllBytes;
 import static java.util.Collections.singletonList;
+import static org.apache.maven.surefire.api.util.internal.StringUtils.NL;
 import static org.apache.maven.surefire.booter.Classpath.emptyClasspath;
 import static org.apache.maven.surefire.booter.ProcessCheckerType.ALL;
+import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.util.Files.temporaryFolder;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -57,7 +70,7 @@ public class ForkConfigurationTest
 {
     private static final StartupConfiguration STARTUP_CONFIG = new StartupConfiguration( "",
             new ClasspathConfiguration( true, true ),
-            new ClassLoaderConfiguration( true, true ), ALL );
+            new ClassLoaderConfiguration( true, true ), ALL, Collections.<String[]>emptyList() );
 
     private static int idx = 0;
 
@@ -79,6 +92,161 @@ public class ForkConfigurationTest
     }
 
     @Test
+    public void testEnv() throws Exception
+    {
+        Map<String, String> env = new HashMap<>();
+        env.put( "key1", "val1" );
+        env.put( "key2", "val2" );
+        env.put( "key3", "val3" );
+        String[] exclEnv = {"PATH"};
+
+        String jvm = new File( new File( System.getProperty( "java.home" ), "bin" ), "java" ).getCanonicalPath();
+        Platform platform = new Platform().withJdkExecAttributesForTests( new JdkAttributes( jvm, false ) );
+
+        ForkConfiguration config = new DefaultForkConfiguration( emptyClasspath(), basedir, "", basedir,
+            new Properties(), "", env, exclEnv, false, 1, true,
+            platform, new NullConsoleLogger(), mock( ForkNodeFactory.class ) )
+        {
+
+            @Override
+            protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
+                                             @Nonnull String booterThatHasMainMethod,
+                                             @Nonnull StartupConfiguration config,
+                                             @Nonnull File dumpLogDirectory )
+            {
+
+            }
+        };
+
+        List<String[]> providerJpmsArgs = new ArrayList<>();
+        providerJpmsArgs.add( new String[]{ "arg2", "arg3" } );
+
+        File cpElement = getTempClasspathFile();
+        List<String> cp = singletonList( cpElement.getAbsolutePath() );
+
+        ClasspathConfiguration cpConfig = new ClasspathConfiguration( new Classpath( cp ), emptyClasspath(),
+            emptyClasspath(), true, true );
+        ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
+        StartupConfiguration startup = new StartupConfiguration( "cls", cpConfig, clc, ALL, providerJpmsArgs );
+
+        Commandline cli = config.createCommandLine( startup, 1, temporaryFolder() );
+
+        assertThat( cli.getEnvironmentVariables() )
+            .contains( "key1=val1", "key2=val2", "key3=val3" )
+            .excludes( "PATH=" )
+            .doesNotHaveDuplicates();
+    }
+
+    @Test
+    public void testCliArgs() throws Exception
+    {
+        String jvm = new File( new File( System.getProperty( "java.home" ), "bin" ), "java" ).getCanonicalPath();
+        Platform platform = new Platform().withJdkExecAttributesForTests( new JdkAttributes( jvm, false ) );
+
+        ModularClasspathForkConfiguration config = new ModularClasspathForkConfiguration( emptyClasspath(), basedir,
+            "", basedir, new Properties(), "arg1", Collections.<String, String>emptyMap(), new String[0], false, 1,
+            true, platform, new NullConsoleLogger(), mock( ForkNodeFactory.class ) );
+
+        assertThat( config.isDebug() ).isFalse();
+
+        List<String[]> providerJpmsArgs = new ArrayList<>();
+        providerJpmsArgs.add( new String[]{ "arg2", "arg3" } );
+
+        ModularClasspath modulepath = new ModularClasspath( "test.module", Collections.<String>emptyList(),
+            Collections.<String>emptyList(), null, false );
+        ModularClasspathConfiguration cpConfig = new ModularClasspathConfiguration( modulepath, emptyClasspath(),
+            emptyClasspath(), emptyClasspath(), false, true );
+        ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
+        StartupConfiguration startup = new StartupConfiguration( "cls", cpConfig, clc, ALL, providerJpmsArgs );
+
+        Commandline cli = config.createCommandLine( startup, 1, temporaryFolder() );
+        String cliAsString = cli.toString();
+
+        assertThat( cliAsString )
+            .contains( "arg1" );
+
+        // "/path/to/java arg1 @/path/to/argfile"
+        int beginOfFileArg = cliAsString.indexOf( '@', cliAsString.lastIndexOf( "arg1" ) );
+        assertThat( beginOfFileArg ).isPositive();
+        int endOfFileArg = cliAsString.indexOf( '"', beginOfFileArg );
+        if ( endOfFileArg == -1 )
+        {
+            endOfFileArg = cliAsString.length();
+        }
+        assertThat( endOfFileArg ).isPositive();
+        Path argFile = Paths.get( cliAsString.substring( beginOfFileArg + 1, endOfFileArg ) );
+        String argFileText = new String( readAllBytes( argFile ) );
+        assertThat( argFileText )
+            .contains( "arg2" )
+            .contains( "arg3" )
+            .contains( "--add-modules" + NL + "test.module" );
+    }
+
+    @Test
+    public void testDebugLine() throws Exception
+    {
+        String jvm = new File( new File( System.getProperty( "java.home" ), "bin" ), "java" ).getCanonicalPath();
+        Platform platform = new Platform().withJdkExecAttributesForTests( new JdkAttributes( jvm, false ) );
+
+        ConsoleLogger logger = mock( ConsoleLogger.class );
+        ForkNodeFactory forkNodeFactory = mock( ForkNodeFactory.class );
+
+        ForkConfiguration config = new DefaultForkConfiguration( emptyClasspath(), basedir,
+            "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", basedir, new Properties(), "",
+            Collections.<String, String>emptyMap(), new String[0], true, 1, true,
+            platform, logger, forkNodeFactory )
+        {
+
+            @Override
+            protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
+                                             @Nonnull String booterThatHasMainMethod,
+                                             @Nonnull StartupConfiguration config,
+                                             @Nonnull File dumpLogDirectory )
+            {
+
+            }
+        };
+
+        assertThat( config.isDebug() )
+            .isTrue();
+
+        assertThat( config.getDebugLine() )
+            .isEqualTo( "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" );
+
+        assertThat( config.getForkCount() )
+            .isEqualTo( 1 );
+
+        assertThat( config.isReuseForks() )
+            .isTrue();
+
+        assertThat( config.getForkNodeFactory() )
+            .isSameAs( forkNodeFactory );
+
+        File cpElement = getTempClasspathFile();
+        List<String> cp = singletonList( cpElement.getAbsolutePath() );
+
+        ClasspathConfiguration cpConfig = new ClasspathConfiguration( new Classpath( cp ), emptyClasspath(),
+            emptyClasspath(), true, true );
+        ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
+        StartupConfiguration startup = new StartupConfiguration( "org.apache.maven.surefire.JUnitProvider#main",
+            cpConfig, clc, ALL, Collections.<String[]>emptyList() );
+
+        assertThat( startup.isProviderMainClass() )
+            .isTrue();
+
+        assertThat( startup.getProviderClassName() )
+            .isEqualTo( "org.apache.maven.surefire.JUnitProvider#main" );
+
+        assertThat( startup.isShadefire() )
+            .isFalse();
+
+        Commandline cli = config.createCommandLine( startup, 1, temporaryFolder() );
+
+        assertThat( cli.toString() )
+            .contains( "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" );
+    }
+
+    @Test
     @SuppressWarnings( { "checkstyle:methodname", "checkstyle:magicnumber" } )
     public void testCreateCommandLine_UseSystemClassLoaderForkOnce_ShouldConstructManifestOnlyJar()
         throws IOException, SurefireBooterForkException
@@ -90,7 +258,8 @@ public class ForkConfigurationTest
         ClasspathConfiguration cpConfig = new ClasspathConfiguration( new Classpath( cp ), emptyClasspath(),
                 emptyClasspath(), true, true );
         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
-        StartupConfiguration startup = new StartupConfiguration( "", cpConfig, clc, ALL );
+        StartupConfiguration startup =
+            new StartupConfiguration( "", cpConfig, clc, ALL, Collections.<String[]>emptyList() );
 
         Commandline cli = config.createCommandLine( startup, 1, temporaryFolder() );
 
@@ -110,7 +279,8 @@ public class ForkConfigurationTest
         ClasspathConfiguration cpConfig = new ClasspathConfiguration( new Classpath( cp ), emptyClasspath(),
                 emptyClasspath(), true, true );
         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
-        StartupConfiguration startup = new StartupConfiguration( "", cpConfig, clc, ALL );
+        StartupConfiguration startup =
+            new StartupConfiguration( "", cpConfig, clc, ALL, Collections.<String[]>emptyList() );
 
         Commandline commandLine = config.createCommandLine( startup, 1, temporaryFolder() );
         assertTrue( commandLine.toString().contains( "abc def" ) );
@@ -125,7 +295,8 @@ public class ForkConfigurationTest
         ClasspathConfiguration cpConfig = new ClasspathConfiguration( emptyClasspath(), emptyClasspath(),
                 emptyClasspath(), true, true );
         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
-        StartupConfiguration startup = new StartupConfiguration( "", cpConfig, clc, ALL );
+        StartupConfiguration startup =
+            new StartupConfiguration( "", cpConfig, clc, ALL, Collections.<String[]>emptyList() );
         ForkConfiguration config = getForkConfiguration( cwd.getCanonicalFile() );
         Commandline commandLine = config.createCommandLine( startup, 1, temporaryFolder() );
 
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfigurationTest.java
index cfa7dce..b1320d5 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfigurationTest.java
@@ -79,8 +79,8 @@ public class ModularClasspathForkConfigurationTest
         Collection<String> packages = singleton( "org.apache.abc" );
         String startClassName = ForkedBooter.class.getName();
 
-        File jigsawArgsFile =
-                config.createArgsFile( "abc", modulePath, classPath, packages, patchFile, startClassName );
+        File jigsawArgsFile = config.createArgsFile( "abc", modulePath, classPath, packages, patchFile,
+            startClassName, true, Collections.<String[]>emptyList() );
 
         assertThat( jigsawArgsFile )
                 .isNotNull();
@@ -136,7 +136,7 @@ public class ModularClasspathForkConfigurationTest
         assertThat( argsFileLines.get( 12 ) )
                 .isEqualTo( ForkedBooter.class.getName() );
 
-        ModularClasspath modularClasspath = new ModularClasspath( "abc", modulePath, packages, patchFile );
+        ModularClasspath modularClasspath = new ModularClasspath( "abc", modulePath, packages, patchFile, true );
         Classpath testClasspathUrls = new Classpath( singleton( "target" + separator + "test-classes" ) );
         Classpath surefireClasspathUrls = Classpath.emptyClasspath();
         ModularClasspathConfiguration modularClasspathConfiguration =
@@ -144,7 +144,7 @@ public class ModularClasspathForkConfigurationTest
                         emptyClasspath(), true, true );
         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
         StartupConfiguration startupConfiguration = new StartupConfiguration( "JUnitCoreProvider",
-            modularClasspathConfiguration, clc, null );
+            modularClasspathConfiguration, clc, null, Collections.<String[]>emptyList() );
         OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
         config.resolveClasspath( cli, ForkedBooter.class.getName(), startupConfiguration,
                 createTempFile( "surefire", "surefire-reports" ) );
diff --git a/maven-surefire-plugin/src/site/apt/examples/jpms.apt.vm b/maven-surefire-plugin/src/site/apt/examples/jpms.apt.vm
new file mode 100644
index 0000000..5168d12
--- /dev/null
+++ b/maven-surefire-plugin/src/site/apt/examples/jpms.apt.vm
@@ -0,0 +1,205 @@
+ ------
+ Java Modularity (JPMS) in Tests
+ ------
+ dev@maven.apache.org
+ ------
+ 2020-05-29
+ ------
+
+ ~~ 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.
+
+ ~~ NOTE: For help with the syntax of this file, see:
+ ~~ http://maven.apache.org/doxia/references/apt-format.html
+
+Using Java Modularity (JPMS) in Tests
+
+* Examples using TestNG
+
+  The Surefire project provides the integration tests with
+  {{{https://github.com/apache/maven-surefire/tree/master/surefire-its/src/test/resources/surefire-1733-testng}TestNG}}
+  demonstrating the Java Modularity (JPMS).
+
+  The JDK version must be 9 or higher. The POM contains the dependency <<<org.testng:testng:7.1.0>>> which is
+  an automatic module. It activates the internal provider <<<surefire-testng>>> in the plugin. The frameworks with
+  assertions API are used, i.e. <<<org.hamcrest:hamcrest:7.1.0>>> (automatic module)
+  and <<<org.assertj:assertj-core:3.16.1>>> (named module).
+
++---+
+<properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <maven.compiler.release>${java.specification.version}</maven.compiler.release>
+</properties>
+
+<dependencies>
+    <dependency>
+        <groupId>org.testng</groupId>
+        <artifactId>testng</artifactId>
+        <version>7.1.0</version>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.hamcrest</groupId>
+        <artifactId>hamcrest</artifactId>
+        <version>2.2</version>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.assertj</groupId>
+        <artifactId>assertj-core</artifactId>
+        <version>3.16.1</version>
+        <scope>test</scope>
+    </dependency>
+</dependencies>
++---+
+
+  The most important parts are the module descriptors. This is the module descriptor located in <<<src/main/java>>>:
+
++---+
+module main
+{
+    exports main;
+}
++---+
+
+  This is the module descriptor in <<<src/test/java>>>:
+
++---+
+module test
+{
+    requires main;
+    requires org.testng;
+    requires org.hamcrest;
+    requires org.assertj.core;
+    exports test to org.testng;
+}
++---+
+
+* Examples using JUnit4
+
+  The Surefire project provides the integration tests with
+  {{{https://github.com/apache/maven-surefire/tree/master/surefire-its/src/test/resources/surefire-1733-junit4}JUnit4}}
+  demonstrating the Java Modularity (JPMS).
+
+  The JDK version must be 9 or higher. The POM contains the dependency <<<junit:junit:4.13>>> which is
+  an automatic module. It activates the internal provider <<<surefire-junit4>>> or <<<surefire-junit47>>> in the plugin.
+
++---+
+<properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <maven.compiler.release>${java.specification.version}</maven.compiler.release>
+</properties>
+
+<dependencies>
+    <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+        <version>4.13</version>
+        <scope>test</scope>
+        <exclusions>
+            <exclusion>
+                <groupId>org.hamcrest</groupId>
+                <artifactId>hamcrest-core</artifactId>
+            </exclusion>
+        </exclusions>
+    </dependency>
+    <dependency>
+        <groupId>org.hamcrest</groupId>
+        <artifactId>hamcrest</artifactId>
+        <version>2.2</version>
+        <scope>test</scope>
+    </dependency>
+</dependencies>
++---+
+
+  The most important parts are the module descriptors. This is the module descriptor located in <<<src/main/java>>>:
+
++---+
+module main
+{
+    exports main;
+}
++---+
+
+  This is the module descriptor in <<<src/test/java>>>:
+
++---+
+module test
+{
+    requires main;
+    requires junit;
+    requires org.hamcrest;
+    exports test to junit;
+}
++---+
+
+* Examples using JUnit5
+
+  The Surefire project provides the integration tests with
+  {{{https://github.com/apache/maven-surefire/tree/master/surefire-its/src/test/resources/maven-multimodule-project-with-jpms}JUnit5}}
+  demonstrating the Java Modularity (JPMS) in Maven multi-module project.
+
+  The JDK version must be 9 or higher. The POM contains the dependency
+  <<<org.junit.jupiter:junit-jupiter-engine:5.6.2>>> which is named module. It activates the internal provider
+  <<<surefire-junit-platform>>> in the plugin.
+
++---+
+<artifactId>com.foo.impl</artifactId>
+
+<dependencies>
+    <dependency>
+        <groupId>com.foo</groupId>
+        <artifactId>com.foo.api</artifactId>
+        <version>${project.version}</version>
+    </dependency>
+    <dependency>
+        <groupId>org.slf4j</groupId>
+        <artifactId>slf4j-simple</artifactId>
+    </dependency>
+    <dependency>
+        <groupId>org.junit.jupiter</groupId>
+        <artifactId>junit-jupiter-engine</artifactId>
+        <scope>test</scope>
+    </dependency>
+</dependencies>
++---+
+
+  The most important parts are the module descriptors. This is the module descriptor located in <<<src/main/java>>>:
+
++---+
+module com.foo.impl
+{
+    exports com.foo.impl;
+    requires com.foo.api;
+    requires org.slf4j;
+    requires org.slf4j.simple;
+}
++---+
+
+  This is the module descriptor in <<<src/test/java>>>:
+
++---+
+open module com.foo.test
+{
+    exports com.foo.implt;
+    requires com.foo.impl;
+    requires org.slf4j;
+    requires org.slf4j.simple;
+    requires transitive org.junit.jupiter.engine;
+    requires transitive org.junit.jupiter.api;
+}
++---+
diff --git a/maven-surefire-plugin/src/site/site.xml b/maven-surefire-plugin/src/site/site.xml
index b1f722c..0768638 100644
--- a/maven-surefire-plugin/src/site/site.xml
+++ b/maven-surefire-plugin/src/site/site.xml
@@ -38,6 +38,7 @@
       <item name="Download" href="../download.html"/>
     </menu>
     <menu name="Examples">
+      <item name="Java Modularity (JPMS) in Tests" href="examples/jpms.html"/>
       <item name="TCP/IP Communication between Forks" href="examples/process-communication.html"/>
       <item name="Using TestNG" href="examples/testng.html"/>
       <item name="Using JUnit" href="examples/junit.html"/>
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ModularClasspath.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ModularClasspath.java
index 7bf8bd0..89aa131 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ModularClasspath.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ModularClasspath.java
@@ -26,6 +26,7 @@ import java.util.List;
 
 import static java.util.Collections.unmodifiableCollection;
 import static java.util.Collections.unmodifiableList;
+import static java.util.Objects.requireNonNull;
 
 /**
  * Jigsaw class-path and module-path.
@@ -39,15 +40,20 @@ public final class ModularClasspath
     private final List<String> modulePath;
     private final Collection<String> packages;
     private final File patchFile;
+    private final boolean isMainDescriptor;
 
-    public ModularClasspath( @Nonnull String moduleNameFromDescriptor, @Nonnull List<String> modulePath,
+    public ModularClasspath( @Nonnull String moduleNameFromDescriptor,
+                             @Nonnull List<String> modulePath,
                              @Nonnull Collection<String> packages,
-                             @Nonnull File patchFile )
+                             File patchFile,
+                             boolean isMainDescriptor )
     {
         this.moduleNameFromDescriptor = moduleNameFromDescriptor;
         this.modulePath = modulePath;
         this.packages = packages;
-        this.patchFile = patchFile;
+        this.patchFile =
+            isMainDescriptor ? requireNonNull( patchFile, "patchFile should not be null" ) : patchFile;
+        this.isMainDescriptor = isMainDescriptor;
     }
 
     @Nonnull
@@ -68,9 +74,13 @@ public final class ModularClasspath
         return unmodifiableCollection( packages );
     }
 
-    @Nonnull
     public File getPatchFile()
     {
         return patchFile;
     }
+
+    public boolean isMainDescriptor()
+    {
+        return isMainDescriptor;
+    }
 }
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/StartupConfiguration.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/StartupConfiguration.java
index d6a3dd0..7a9d1e4 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/StartupConfiguration.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/StartupConfiguration.java
@@ -20,6 +20,8 @@ package org.apache.maven.surefire.booter;
  */
 
 import javax.annotation.Nonnull;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Configuration that is used by the SurefireStarter but does not make it into the provider itself.
@@ -34,16 +36,19 @@ public class StartupConfiguration
     private final AbstractPathConfiguration classpathConfiguration;
     private final ClassLoaderConfiguration classLoaderConfiguration;
     private final ProcessCheckerType processChecker;
+    private final List<String[]> jpmsArguments;
 
     public StartupConfiguration( @Nonnull String providerClassName,
                                  @Nonnull AbstractPathConfiguration classpathConfiguration,
                                  @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
-                                 ProcessCheckerType processChecker )
+                                 ProcessCheckerType processChecker,
+                                 @Nonnull List<String[]> jpmsArguments )
     {
         this.classpathConfiguration = classpathConfiguration;
         this.classLoaderConfiguration = classLoaderConfiguration;
         this.providerClassName = providerClassName;
         this.processChecker = processChecker;
+        this.jpmsArguments = jpmsArguments;
     }
 
     public boolean isProviderMainClass()
@@ -56,7 +61,8 @@ public class StartupConfiguration
                                                    ClassLoaderConfiguration classLoaderConfig,
                                                    ProcessCheckerType processChecker )
     {
-        return new StartupConfiguration( providerClassName, classpathConfig, classLoaderConfig, processChecker );
+        return new StartupConfiguration( providerClassName, classpathConfig, classLoaderConfig,
+            processChecker, Collections.<String[]>emptyList() );
     }
 
     public AbstractPathConfiguration getClasspathConfiguration()
@@ -133,4 +139,9 @@ public class StartupConfiguration
     {
         return processChecker;
     }
+
+    public List<String[]> getJpmsArguments()
+    {
+        return jpmsArguments;
+    }
 }
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/MultiModuleProjectWithJPMSIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/MultiModuleProjectWithJPMSIT.java
new file mode 100644
index 0000000..8b011ae
--- /dev/null
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/MultiModuleProjectWithJPMSIT.java
@@ -0,0 +1,70 @@
+package org.apache.maven.surefire.its;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.its.fixture.OutputValidator;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Integration test for <a href="https://issues.apache.org/jira/browse/SUREFIRE-1733">SUREFIRE-1733</a>.
+ */
+public class MultiModuleProjectWithJPMSIT extends AbstractJigsawIT
+{
+    @Test
+    public void test() throws Exception
+    {
+        OutputValidator validator = assumeJava9()
+            .debugLogging()
+            .executeVerify()
+            .verifyErrorFreeLog()
+            .assertThatLogLine( containsString( "Lets see JDKModulePath" ), is( 2 ) )
+            .assertThatLogLine( containsString( "Lets see JDKModulePath: null" ), is( 0 ) );
+
+        List<String> lines = validator.loadLogLines( containsString( "Lets see JDKModulePath" ) );
+        int i = 0;
+        for ( String line : lines )
+        {
+            assertThat( line )
+                .contains( "com.foo.api" )
+                .contains( "junit-jupiter-api" )
+                .contains( "junit-jupiter-engine" )
+                .contains( "slf4j-simple" )
+                .contains( "slf4j-api" )
+                .contains( "jakarta.xml.bind-api" )
+                .contains( "jakarta.ws.rs-api" )
+                .contains( "jakarta.persistence-api" );
+
+            assertThat( line )
+                .contains( i++ == 0 ? "test-classes" : "com.foo.impl" );
+        }
+    }
+
+    @Override
+    protected String getProjectDirectoryName()
+    {
+        return "maven-multimodule-project-with-jpms";
+    }
+}
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java
index eb1ff06..bb575b0 100644
--- a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java
@@ -19,11 +19,10 @@ package org.apache.maven.surefire.its.jiras;
  * under the License.
  */
 
-import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
+import org.apache.maven.surefire.its.AbstractJigsawIT;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaVersion;
 import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaVersionExcluded;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
@@ -34,23 +33,28 @@ import static org.hamcrest.Matchers.is;
  */
 @SuppressWarnings( "checkstyle:magicnumber" )
 public class Surefire1570ModularFailsafeIT
-    extends SurefireJUnit4IntegrationTestCase
+    extends AbstractJigsawIT
 {
     @Before
     public void setUp()
     {
-        assumeJavaVersion( 9d );
         assumeJavaVersionExcluded( 11d );
     }
 
     @Test
     public void shouldRunWithJupiterApi() throws Exception
     {
-        unpack( "surefire-1570" )
+        assumeJava9()
             .debugLogging()
             .executeVerify()
             .verifyErrorFreeLog()
             .assertThatLogLine( containsString( "Lets see JDKModulePath" ), is( 2 ) )
             .assertThatLogLine( containsString( "Lets see JDKModulePath: null" ), is( 0 ) );
     }
+
+    @Override
+    protected String getProjectDirectoryName()
+    {
+        return "surefire-1570";
+    }
 }
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1733JUnitIT.java
similarity index 52%
copy from surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java
copy to surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1733JUnitIT.java
index eb1ff06..242fede 100644
--- a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1733JUnitIT.java
@@ -19,38 +19,35 @@ package org.apache.maven.surefire.its.jiras;
  * under the License.
  */
 
-import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
-import org.junit.Before;
+import org.apache.maven.surefire.its.AbstractJigsawIT;
 import org.junit.Test;
 
-import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaVersion;
-import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaVersionExcluded;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
 
 /**
- *
- * @see <a href="https://issues.apache.org/jira/browse/SUREFIRE-1570">SUREFIRE-1570</a>
+ * Integration test for <a href="https://issues.apache.org/jira/browse/SUREFIRE-1733">SUREFIRE-1733</a>.
  */
-@SuppressWarnings( "checkstyle:magicnumber" )
-public class Surefire1570ModularFailsafeIT
-    extends SurefireJUnit4IntegrationTestCase
+public class Surefire1733JUnitIT extends AbstractJigsawIT
 {
-    @Before
-    public void setUp()
-    {
-        assumeJavaVersion( 9d );
-        assumeJavaVersionExcluded( 11d );
-    }
-
     @Test
-    public void shouldRunWithJupiterApi() throws Exception
+    public void test() throws Exception
     {
-        unpack( "surefire-1570" )
+        assumeJava9()
             .debugLogging()
             .executeVerify()
-            .verifyErrorFreeLog()
-            .assertThatLogLine( containsString( "Lets see JDKModulePath" ), is( 2 ) )
-            .assertThatLogLine( containsString( "Lets see JDKModulePath: null" ), is( 0 ) );
+            .assertTestSuiteResults( 1, 0, 0, 0 )
+            .assertIntegrationTestSuiteResults( 1, 0, 0, 0 )
+            .assertThatLogLine( containsString( "Running test.MyTest" ), is( 1 ) )
+            .assertThatLogLine( containsString( "Running test.MyIT" ), is( 1 ) )
+            .assertThatLogLine( containsString( "class main.Service in the module \"main\"" ), is( 2 ) )
+            .assertThatLogLine( containsString( "class test.MyTest in the module \"test\"" ), is( 1 ) )
+            .assertThatLogLine( containsString( "class test.MyIT in the module \"test\"" ), is( 1 ) );
+    }
+
+    @Override
+    protected String getProjectDirectoryName()
+    {
+        return "surefire-1733-junit4";
     }
 }
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1733TestngIT.java
similarity index 52%
copy from surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java
copy to surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1733TestngIT.java
index eb1ff06..53210c7 100644
--- a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1733TestngIT.java
@@ -19,38 +19,35 @@ package org.apache.maven.surefire.its.jiras;
  * under the License.
  */
 
-import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
-import org.junit.Before;
+import org.apache.maven.surefire.its.AbstractJigsawIT;
 import org.junit.Test;
 
-import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaVersion;
-import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaVersionExcluded;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
 
 /**
- *
- * @see <a href="https://issues.apache.org/jira/browse/SUREFIRE-1570">SUREFIRE-1570</a>
+ * Integration test for <a href="https://issues.apache.org/jira/browse/SUREFIRE-1733">SUREFIRE-1733</a>.
  */
-@SuppressWarnings( "checkstyle:magicnumber" )
-public class Surefire1570ModularFailsafeIT
-    extends SurefireJUnit4IntegrationTestCase
+public class Surefire1733TestngIT extends AbstractJigsawIT
 {
-    @Before
-    public void setUp()
-    {
-        assumeJavaVersion( 9d );
-        assumeJavaVersionExcluded( 11d );
-    }
-
     @Test
-    public void shouldRunWithJupiterApi() throws Exception
+    public void test() throws Exception
     {
-        unpack( "surefire-1570" )
+        assumeJava9()
             .debugLogging()
             .executeVerify()
-            .verifyErrorFreeLog()
-            .assertThatLogLine( containsString( "Lets see JDKModulePath" ), is( 2 ) )
-            .assertThatLogLine( containsString( "Lets see JDKModulePath: null" ), is( 0 ) );
+            .assertTestSuiteResults( 1, 0, 0, 0 )
+            .assertIntegrationTestSuiteResults( 1, 0, 0, 0 )
+            .assertThatLogLine( containsString( "Running test.MyTest" ), is( 1 ) )
+            .assertThatLogLine( containsString( "Running test.MyIT" ), is( 1 ) )
+            .assertThatLogLine( containsString( "class main.Service in the module \"main\"" ), is( 2 ) )
+            .assertThatLogLine( containsString( "class test.MyTest in the module \"test\"" ), is( 1 ) )
+            .assertThatLogLine( containsString( "class test.MyIT in the module \"test\"" ), is( 1 ) );
+    }
+
+    @Override
+    protected String getProjectDirectoryName()
+    {
+        return "surefire-1733-testng";
     }
 }
diff --git a/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.api/pom.xml b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.api/pom.xml
new file mode 100644
index 0000000..6e1883c
--- /dev/null
+++ b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.api/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.foo</groupId>
+        <artifactId>com.foo</artifactId>
+        <version>1.0.0</version>
+    </parent>
+
+    <artifactId>com.foo.api</artifactId>
+    
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.persistence</groupId>
+            <artifactId>jakarta.persistence-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.ws.rs</groupId>
+            <artifactId>jakarta.ws.rs-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.api/src/main/java/com/foo/api/SomeInterface.java
similarity index 86%
copy from surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
copy to surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.api/src/main/java/com/foo/api/SomeInterface.java
index 7bbf986..43ecf73 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.api/src/main/java/com/foo/api/SomeInterface.java
@@ -1,3 +1,5 @@
+package com.foo.api;
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -17,9 +19,10 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+/**
+ *
+ */
+public interface SomeInterface
 {
-    requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
-    requires transitive org.hamcrest;
+    void doItNow( Class<?> observer );
 }
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.api/src/main/java/module-info.java
similarity index 81%
copy from surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
copy to surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.api/src/main/java/module-info.java
index 7bbf986..95b05f7 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.api/src/main/java/module-info.java
@@ -17,9 +17,12 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+module com.foo.api
 {
-    requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
-    requires transitive org.hamcrest;
+    exports com.foo.api;
+    requires org.slf4j;
+    requires java.persistence;
+    requires java.ws.rs;
+    requires java.xml.bind;
+    requires jakarta.activation;
 }
diff --git a/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/pom.xml b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/pom.xml
new file mode 100644
index 0000000..fd160fe
--- /dev/null
+++ b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.foo</groupId>
+        <artifactId>com.foo</artifactId>
+        <version>1.0.0</version>
+    </parent>
+
+    <artifactId>com.foo.impl</artifactId>
+    
+    <dependencies>
+        <dependency>
+            <groupId>com.foo</groupId>
+            <artifactId>com.foo.api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+<!--                    <multiReleaseOutput>true</multiReleaseOutput>-->
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestEntries>
+<!--                            <Multi-Release>true</Multi-Release>-->
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>integration-tests</id>
+                        <goals>
+                            <goal>integration-test</goal>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/main/java/com/foo/impl/Bar.java b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/main/java/com/foo/impl/Bar.java
new file mode 100644
index 0000000..50009e3
--- /dev/null
+++ b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/main/java/com/foo/impl/Bar.java
@@ -0,0 +1,51 @@
+package com.foo.impl;
+
+/*
+ * 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 com.foo.api.SomeInterface;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class Bar implements SomeInterface
+{
+    private static final Logger LOG = LoggerFactory.getLogger( Bar.class );
+
+    private boolean weAreAmongModules;
+
+    @Override
+    public void doItNow( Class<?> observer )
+    {
+        ModuleLayer.boot().modules().forEach( m -> {
+            if ( m == observer.getModule() || m == Bar.class.getModule() || m == Logger.class.getModule() )
+            {
+                weAreAmongModules = true;
+            }
+        } );
+        LOG.info( "" );
+        LOG.info( "Let's see if I or SLF4J are among boot layer modules: {}", weAreAmongModules );
+        if ( !weAreAmongModules )
+        {
+            LOG.info( "Maybe we are in child layer? Or this is not module path?" );
+        }
+    }
+}
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/main/java/module-info.java
similarity index 86%
copy from surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
copy to surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/main/java/module-info.java
index 7bbf986..13ca174 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/main/java/module-info.java
@@ -17,9 +17,10 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+module com.foo.impl
 {
-    requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
-    requires transitive org.hamcrest;
+    exports com.foo.impl;
+    requires com.foo.api;
+    requires org.slf4j;
+    requires org.slf4j.simple;
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/test/java/com/foo/implt/BarIT.java
similarity index 62%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
copy to surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/test/java/com/foo/implt/BarIT.java
index fea74fd..acb4c7f 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
+++ b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/test/java/com/foo/implt/BarIT.java
@@ -1,4 +1,4 @@
-package org.apache.maven.plugin.surefire;
+package com.foo.implt;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,24 +19,25 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.MojoExecutionException;
+import com.foo.impl.Bar;
 
-import javax.annotation.Nonnull;
-import java.util.Set;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * @author Kristian Rosenvold
+ *
  */
-public interface ProviderInfo
+public class BarIT
 {
-    @Nonnull
-    String getProviderName();
-
-    boolean isApplicable();
-
-    @Nonnull
-    Set<Artifact> getProviderClasspath() throws MojoExecutionException;
+    private static final Logger LOG = LoggerFactory.getLogger( BarIT.class );
 
-    void addProviderProperties() throws MojoExecutionException;
+    @Test
+    void shouldPrintModulePath()
+    {
+        Bar bar = new Bar();
+        LOG.info( "======INTEGRATION TEST=======" );
+        LOG.info( "Lets see JDKModulePath: {}", System.getProperty( "jdk.module.path" ) );
+        bar.doItNow( getClass() );
+    }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/test/java/com/foo/implt/BarTest.java
similarity index 62%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
copy to surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/test/java/com/foo/implt/BarTest.java
index fea74fd..f639198 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
+++ b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/test/java/com/foo/implt/BarTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.plugin.surefire;
+package com.foo.implt;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,24 +19,25 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.MojoExecutionException;
+import com.foo.impl.Bar;
 
-import javax.annotation.Nonnull;
-import java.util.Set;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * @author Kristian Rosenvold
+ *
  */
-public interface ProviderInfo
+public class BarTest
 {
-    @Nonnull
-    String getProviderName();
-
-    boolean isApplicable();
-
-    @Nonnull
-    Set<Artifact> getProviderClasspath() throws MojoExecutionException;
+    private static final Logger LOG = LoggerFactory.getLogger( BarTest.class );
 
-    void addProviderProperties() throws MojoExecutionException;
+    @Test
+    void shouldPrintModulePath()
+    {
+        Bar bar = new Bar();
+        LOG.info( "======UNIT TEST=======" );
+        LOG.info( "Lets see JDKModulePath: {}", System.getProperty( "jdk.module.path" ) );
+        bar.doItNow( getClass() );
+    }
 }
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/test/java/module-info.java
similarity index 77%
copy from surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
copy to surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/test/java/module-info.java
index 7bbf986..1906b6a 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/com.foo.impl/src/test/java/module-info.java
@@ -17,9 +17,12 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+open module com.foo.test
 {
-    requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
-    requires transitive org.hamcrest;
+    exports com.foo.implt;
+    requires com.foo.impl;
+    requires org.slf4j;
+    requires org.slf4j.simple;
+    requires transitive org.junit.jupiter.engine;
+    requires transitive org.junit.jupiter.api;
 }
diff --git a/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/pom.xml b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/pom.xml
new file mode 100644
index 0000000..00e047f
--- /dev/null
+++ b/surefire-its/src/test/resources/maven-multimodule-project-with-jpms/pom.xml
@@ -0,0 +1,101 @@
+<?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>com.foo</groupId>
+    <artifactId>com.foo</artifactId>
+    <version>1.0.0</version>
+    <packaging>pom</packaging>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.release>${java.specification.version}</maven.compiler.release>
+    </properties>
+
+    <modules>
+        <module>com.foo.api</module>
+        <module>com.foo.impl</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>jakarta.persistence</groupId>
+                <artifactId>jakarta.persistence-api</artifactId>
+                <version>2.2.3</version>
+            </dependency>
+            <dependency>
+                <groupId>jakarta.ws.rs</groupId>
+                <artifactId>jakarta.ws.rs-api</artifactId>
+                <version>2.1.6</version>
+            </dependency>
+            <dependency>
+                <groupId>jakarta.xml.bind</groupId>
+                <artifactId>jakarta.xml.bind-api</artifactId>
+                <version>2.3.2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-api</artifactId>
+                <version>1.8.0-beta2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-simple</artifactId>
+                <version>1.8.0-beta2</version>
+            </dependency>
+            <dependency>
+               <groupId>org.junit.jupiter</groupId>
+               <artifactId>junit-jupiter-engine</artifactId>
+               <version>5.6.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>3.8.1</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${surefire.version}</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>3.2.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-failsafe-plugin</artifactId>
+                    <version>${surefire.version}</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+</project>
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
index 7bbf986..5291a22 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
@@ -17,9 +17,9 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+open module wtf.g4s8.oot.test
 {
     requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
+    requires transitive org.junit.jupiter.engine;
     requires transitive org.hamcrest;
 }
diff --git a/surefire-its/src/test/resources/surefire-1733-junit4/pom.xml b/surefire-its/src/test/resources/surefire-1733-junit4/pom.xml
new file mode 100644
index 0000000..c309b7f
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1733-junit4/pom.xml
@@ -0,0 +1,102 @@
+<?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.example</groupId>
+    <artifactId>surefire-1733-junit4</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.release>${java.specification.version}</maven.compiler.release>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.hamcrest</groupId>
+                    <artifactId>hamcrest-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <version>2.2</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>3.8.1</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${surefire.version}</version>
+                <configuration>
+                    <test>MyTest</test>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <version>${surefire.version}</version>
+                <executions>
+                    <execution>
+                        <id>it</id>
+                        <phase>integration-test</phase>
+                        <goals>
+                            <goal>integration-test</goal>
+                        </goals>
+                        <configuration>
+                            <test>MyIT</test>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>verify</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/surefire-1733-junit4/src/main/java/main/Service.java
similarity index 86%
copy from surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
copy to surefire-its/src/test/resources/surefire-1733-junit4/src/main/java/main/Service.java
index 7bbf986..68592db 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/surefire-1733-junit4/src/main/java/main/Service.java
@@ -1,3 +1,5 @@
+package main;
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -17,9 +19,9 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+/**
+ *
+ */
+public class Service
 {
-    requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
-    requires transitive org.hamcrest;
 }
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/surefire-1733-junit4/src/main/java/module-info.java
similarity index 86%
copy from surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
copy to surefire-its/src/test/resources/surefire-1733-junit4/src/main/java/module-info.java
index 7bbf986..216ec8a 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/surefire-1733-junit4/src/main/java/module-info.java
@@ -17,9 +17,10 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+/**
+ *
+ */
+module main
 {
-    requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
-    requires transitive org.hamcrest;
+    exports main;
 }
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/surefire-1733-junit4/src/test/java/module-info.java
similarity index 86%
copy from surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
copy to surefire-its/src/test/resources/surefire-1733-junit4/src/test/java/module-info.java
index 7bbf986..4334908 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/surefire-1733-junit4/src/test/java/module-info.java
@@ -17,9 +17,13 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+/**
+ *
+ */
+/*open*/ module test
 {
-    requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
-    requires transitive org.hamcrest;
+    requires main;
+    requires junit;
+    requires org.hamcrest;
+    exports test to junit;
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java b/surefire-its/src/test/resources/surefire-1733-junit4/src/test/java/test/MyIT.java
similarity index 55%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
copy to surefire-its/src/test/resources/surefire-1733-junit4/src/test/java/test/MyIT.java
index fea74fd..3e3eb52 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
+++ b/surefire-its/src/test/resources/surefire-1733-junit4/src/test/java/test/MyIT.java
@@ -1,4 +1,4 @@
-package org.apache.maven.plugin.surefire;
+package test;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,24 +19,27 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.MojoExecutionException;
+import main.Service;
+import org.junit.Test;
 
-import javax.annotation.Nonnull;
-import java.util.Set;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
 
 /**
- * @author Kristian Rosenvold
+ *
  */
-public interface ProviderInfo
+public class MyIT
 {
-    @Nonnull
-    String getProviderName();
-
-    boolean isApplicable();
-
-    @Nonnull
-    Set<Artifact> getProviderClasspath() throws MojoExecutionException;
+    @Test
+    public void test()
+    {
+        Service service = new Service();
+        String moduleName = service.getClass().getModule().getName();
+        System.out.println( service.getClass() + " in the module \"" + moduleName + "\"" );
+        assertThat( moduleName, is( "main" ) );
 
-    void addProviderProperties() throws MojoExecutionException;
+        moduleName = getClass().getModule().getName();
+        System.out.println( getClass() + " in the module \"" + moduleName + "\"" );
+        assertThat( moduleName, is( "test" ) );
+    }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java b/surefire-its/src/test/resources/surefire-1733-junit4/src/test/java/test/MyTest.java
similarity index 55%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
copy to surefire-its/src/test/resources/surefire-1733-junit4/src/test/java/test/MyTest.java
index fea74fd..c839350 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
+++ b/surefire-its/src/test/resources/surefire-1733-junit4/src/test/java/test/MyTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.plugin.surefire;
+package test;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,24 +19,27 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.MojoExecutionException;
+import main.Service;
+import org.junit.Test;
 
-import javax.annotation.Nonnull;
-import java.util.Set;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
 
 /**
- * @author Kristian Rosenvold
+ *
  */
-public interface ProviderInfo
+public class MyTest
 {
-    @Nonnull
-    String getProviderName();
-
-    boolean isApplicable();
-
-    @Nonnull
-    Set<Artifact> getProviderClasspath() throws MojoExecutionException;
+    @Test
+    public void test()
+    {
+        Service service = new Service();
+        String moduleName = service.getClass().getModule().getName();
+        System.out.println( service.getClass() + " in the module \"" + moduleName + "\"" );
+        assertThat( moduleName, is( "main" ) );
 
-    void addProviderProperties() throws MojoExecutionException;
+        moduleName = getClass().getModule().getName();
+        System.out.println( getClass() + " in the module \"" + moduleName + "\"" );
+        assertThat( moduleName, is( "test" ) );
+    }
 }
diff --git a/surefire-its/src/test/resources/surefire-1733-testng/pom.xml b/surefire-its/src/test/resources/surefire-1733-testng/pom.xml
new file mode 100644
index 0000000..630d09d
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1733-testng/pom.xml
@@ -0,0 +1,102 @@
+<?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.example</groupId>
+    <artifactId>surefire-1733-testng</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.release>${java.specification.version}</maven.compiler.release>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <version>7.1.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <version>2.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>3.16.1</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>3.8.1</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${surefire.version}</version>
+                <configuration>
+                    <test>MyTest</test>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <version>${surefire.version}</version>
+                <executions>
+                    <execution>
+                        <id>it</id>
+                        <phase>integration-test</phase>
+                        <goals>
+                            <goal>integration-test</goal>
+                        </goals>
+                        <configuration>
+                            <test>MyIT</test>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>verify</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/surefire-1733-testng/src/main/java/main/Service.java
similarity index 86%
copy from surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
copy to surefire-its/src/test/resources/surefire-1733-testng/src/main/java/main/Service.java
index 7bbf986..68592db 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/surefire-1733-testng/src/main/java/main/Service.java
@@ -1,3 +1,5 @@
+package main;
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -17,9 +19,9 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+/**
+ *
+ */
+public class Service
 {
-    requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
-    requires transitive org.hamcrest;
 }
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/surefire-1733-testng/src/main/java/module-info.java
similarity index 86%
copy from surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
copy to surefire-its/src/test/resources/surefire-1733-testng/src/main/java/module-info.java
index 7bbf986..216ec8a 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/surefire-1733-testng/src/main/java/module-info.java
@@ -17,9 +17,10 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+/**
+ *
+ */
+module main
 {
-    requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
-    requires transitive org.hamcrest;
+    exports main;
 }
diff --git a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java b/surefire-its/src/test/resources/surefire-1733-testng/src/test/java/module-info.java
similarity index 83%
copy from surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
copy to surefire-its/src/test/resources/surefire-1733-testng/src/test/java/module-info.java
index 7bbf986..8b1ec9c 100644
--- a/surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java
+++ b/surefire-its/src/test/resources/surefire-1733-testng/src/test/java/module-info.java
@@ -17,9 +17,14 @@
  * under the License.
  */
 
-module wtf.g4s8.oot.test
+/**
+ *
+ */
+/*open*/ module test
 {
-    requires wtf.g4s8.oot;
-    requires org.junit.jupiter.api;
-    requires transitive org.hamcrest;
+    requires main;
+    requires org.testng;
+    requires org.hamcrest;
+    requires org.assertj.core;
+    exports test to org.testng;
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java b/surefire-its/src/test/resources/surefire-1733-testng/src/test/java/test/MyIT.java
similarity index 55%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
copy to surefire-its/src/test/resources/surefire-1733-testng/src/test/java/test/MyIT.java
index fea74fd..534672d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
+++ b/surefire-its/src/test/resources/surefire-1733-testng/src/test/java/test/MyIT.java
@@ -1,4 +1,4 @@
-package org.apache.maven.plugin.surefire;
+package test;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,24 +19,27 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.MojoExecutionException;
+import main.Service;
+import org.testng.annotations.Test;
 
-import javax.annotation.Nonnull;
-import java.util.Set;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
 
 /**
- * @author Kristian Rosenvold
+ *
  */
-public interface ProviderInfo
+public class MyIT
 {
-    @Nonnull
-    String getProviderName();
-
-    boolean isApplicable();
-
-    @Nonnull
-    Set<Artifact> getProviderClasspath() throws MojoExecutionException;
+    @Test
+    public void test()
+    {
+        Service service = new Service();
+        String moduleName = service.getClass().getModule().getName();
+        System.out.println( service.getClass() + " in the module \"" + moduleName + "\"" );
+        assertThat( moduleName, is( "main" ) );
 
-    void addProviderProperties() throws MojoExecutionException;
+        moduleName = getClass().getModule().getName();
+        System.out.println( getClass() + " in the module \"" + moduleName + "\"" );
+        assertThat( moduleName, is( "test" ) );
+    }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java b/surefire-its/src/test/resources/surefire-1733-testng/src/test/java/test/MyTest.java
similarity index 54%
copy from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
copy to surefire-its/src/test/resources/surefire-1733-testng/src/test/java/test/MyTest.java
index fea74fd..c13d1f1 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
+++ b/surefire-its/src/test/resources/surefire-1733-testng/src/test/java/test/MyTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.plugin.surefire;
+package test;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,24 +19,28 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.MojoExecutionException;
+import main.Service;
+import org.testng.annotations.Test;
 
-import javax.annotation.Nonnull;
-import java.util.Set;
+import static org.assertj.core.api.Assertions.assertThat;
 
 /**
- * @author Kristian Rosenvold
+ *
  */
-public interface ProviderInfo
+public class MyTest
 {
-    @Nonnull
-    String getProviderName();
-
-    boolean isApplicable();
-
-    @Nonnull
-    Set<Artifact> getProviderClasspath() throws MojoExecutionException;
+    @Test
+    public void test()
+    {
+        Service service = new Service();
+        String moduleName = service.getClass().getModule().getName();
+        System.out.println( service.getClass() + " in the module \"" + moduleName + "\"" );
+        assertThat( moduleName )
+                .isEqualTo( "main" );
 
-    void addProviderProperties() throws MojoExecutionException;
+        moduleName = getClass().getModule().getName();
+        System.out.println( getClass() + " in the module \"" + moduleName + "\"" );
+        assertThat( moduleName )
+                .isEqualTo( "test" );
+    }
 }