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/24 15:11:45 UTC

[maven-surefire] branch SUREFIRE-1733 updated: JPMS arguments should go to the arg file

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

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


The following commit(s) were added to refs/heads/SUREFIRE-1733 by this push:
     new 8dea82b  JPMS arguments should go to the arg file
8dea82b is described below

commit 8dea82ba40d0b56d121f05bbbc557e6ede90c8b4
Author: tibordigana <ti...@apache.org>
AuthorDate: Sun May 24 17:09:01 2020 +0200

    JPMS arguments should go to the arg file
---
 .../plugin/surefire/AbstractSurefireMojo.java      |  51 +++---
 .../plugin/surefire/ProviderForkRequirements.java  |   2 +-
 .../apache/maven/plugin/surefire/ProviderInfo.java |   3 +-
 .../booterclient/DefaultForkConfiguration.java     |   6 -
 .../ModularClasspathForkConfiguration.java         |  16 +-
 .../AbstractSurefireMojoJava7PlusTest.java         | 149 +++++++++++++++++
 .../plugin/surefire/AbstractSurefireMojoTest.java  |   1 +
 ...ooterDeserializerProviderConfigurationTest.java |   2 +-
 ...BooterDeserializerStartupConfigurationTest.java |   5 +-
 .../booterclient/DefaultForkConfigurationTest.java |   7 +-
 .../booterclient/ForkConfigurationTest.java        | 184 ++++++++++++++++++++-
 .../ModularClasspathForkConfigurationTest.java     |   6 +-
 .../maven/surefire/booter/ModularClasspath.java    |   7 +-
 .../surefire/booter/StartupConfiguration.java      |  14 +-
 14 files changed, 396 insertions(+), 57 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 39ca9b7..139d214 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
@@ -125,6 +125,7 @@ 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;
@@ -1928,7 +1929,7 @@ public abstract class AbstractSurefireMojo
                 inProcClasspath, effectiveIsEnableAssertions(), isChildDelegation() );
         ProviderForkRequirements forkRequirements = new ProviderForkRequirements( false, false, false );
         return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration,
-            ProcessCheckerType.toEnum( getEnableProcessChecker() ), providerInfo.getJvmArgs( forkRequirements ) );
+            ProcessCheckerType.toEnum( getEnableProcessChecker() ), providerInfo.getJpmsArguments( forkRequirements ) );
     }
 
     private static Set<Artifact> retainInProcArtifactsUnique( Set<Artifact> providerArtifacts,
@@ -2033,7 +2034,8 @@ public abstract class AbstractSurefireMojo
         getConsoleLogger().debug( "main module descriptor name: " + javaModuleDescriptor.name() );
 
         ModularClasspath modularClasspath = new ModularClasspath( javaModuleDescriptor.name(),
-                testModulepath.getClassPath(), packages, getTestClassesDirectory(), isMainDescriptor );
+            testModulepath.getClassPath(), packages, isMainDescriptor ? getTestClassesDirectory() : null,
+            isMainDescriptor );
 
         Artifact[] additionalInProcArtifacts = { getCommonArtifact(), getBooterArtifact(), getExtensionsArtifact(),
             getApiArtifact(), getSpiArtifact(), getLoggerApiArtifact(), getSurefireSharedUtilsArtifact() };
@@ -2053,7 +2055,7 @@ public abstract class AbstractSurefireMojo
         getConsoleLogger().debug( inProcClasspath.getCompactLogMessage( "in-process(compact) classpath:" ) );
 
         return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration,
-            ProcessCheckerType.toEnum( getEnableProcessChecker() ), providerInfo.getJvmArgs( forkRequirements ) );
+            ProcessCheckerType.toEnum( getEnableProcessChecker() ), providerInfo.getJpmsArguments( forkRequirements ) );
     }
 
     private Artifact getCommonArtifact()
@@ -3061,9 +3063,9 @@ public abstract class AbstractSurefireMojo
 
         @Nonnull
         @Override
-        public String[] getJvmArgs( @Nonnull ProviderForkRequirements forkRequirements )
+        public List<String[]> getJpmsArguments( @Nonnull ProviderForkRequirements forkRequirements )
         {
-            return new String[0];
+            return emptyList();
         }
 
         @Override
@@ -3098,9 +3100,9 @@ public abstract class AbstractSurefireMojo
 
         @Nonnull
         @Override
-        public String[] getJvmArgs( @Nonnull ProviderForkRequirements forkRequirements )
+        public List<String[]> getJpmsArguments( @Nonnull ProviderForkRequirements forkRequirements )
         {
-            return new String[0];
+            return emptyList();
         }
 
         @Override
@@ -3146,9 +3148,9 @@ public abstract class AbstractSurefireMojo
 
         @Nonnull
         @Override
-        public String[] getJvmArgs( @Nonnull ProviderForkRequirements forkRequirements )
+        public List<String[]> getJpmsArguments( @Nonnull ProviderForkRequirements forkRequirements )
         {
-            return new String[0];
+            return emptyList();
         }
 
         @Override
@@ -3196,10 +3198,10 @@ public abstract class AbstractSurefireMojo
 
         @Nonnull
         @Override
-        public String[] getJvmArgs( @Nonnull ProviderForkRequirements forkRequirements )
+        public List<String[]> getJpmsArguments( @Nonnull ProviderForkRequirements forkRequirements )
         {
             boolean hasTestDescriptor = forkRequirements.isModularPath() && forkRequirements.hasTestModuleDescriptor();
-            return hasTestDescriptor ? getJpmsArgs() : new String[0];
+            return hasTestDescriptor ? getJpmsArgs() : Collections.<String[]>emptyList();
         }
 
         @Override
@@ -3258,18 +3260,27 @@ public abstract class AbstractSurefireMojo
             return new LinkedHashSet<>( providerArtifacts.values() );
         }
 
-        private String[] getJpmsArgs()
+        private List<String[]> getJpmsArgs()
         {
-            return new String[] {
+            List<String[]> args = new ArrayList<>();
+
+
+            args.add( new String[] {
                 "--add-modules",
-                "ALL-MODULE-PATH",
+                "ALL-MODULE-PATH"
+            } );
 
+            args.add( new String[] {
                 "--add-opens",
-                "org.junit.platform.commons/org.junit.platform.commons.util=ALL-UNNAMED",
+                "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,
@@ -3371,9 +3382,9 @@ public abstract class AbstractSurefireMojo
 
         @Nonnull
         @Override
-        public String[] getJvmArgs( @Nonnull ProviderForkRequirements forkRequirements )
+        public List<String[]> getJpmsArguments( @Nonnull ProviderForkRequirements forkRequirements )
         {
-            return new String[0];
+            return emptyList();
         }
 
         @Override
@@ -3427,9 +3438,9 @@ public abstract class AbstractSurefireMojo
 
         @Nonnull
         @Override
-        public String[] getJvmArgs( @Nonnull ProviderForkRequirements forkRequirements )
+        public List<String[]> getJpmsArguments( @Nonnull ProviderForkRequirements forkRequirements )
         {
-            return new String[0];
+            return emptyList();
         }
 
         @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderForkRequirements.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderForkRequirements.java
index a29da2d..352fe2f 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderForkRequirements.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderForkRequirements.java
@@ -22,7 +22,7 @@ package org.apache.maven.plugin.surefire;
 /**
  * Used to get additional provider-specific JVM arguments.
  *
- * @see ProviderInfo#getJvmArgs(ProviderForkRequirements)
+ * @see ProviderInfo#getJpmsArguments(ProviderForkRequirements)
  */
 final class ProviderForkRequirements
 {
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 4c76dbc..e0d1004 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;
 
 /**
@@ -41,5 +42,5 @@ public interface ProviderInfo
     void addProviderProperties() throws MojoExecutionException;
 
     @Nonnull
-    String[] getJvmArgs( @Nonnull ProviderForkRequirements forkRequirements );
+    List<String[]> getJpmsArguments( @Nonnull ProviderForkRequirements forkRequirements );
 }
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 3f94084..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
@@ -153,12 +153,6 @@ public abstract class DefaultForkConfiguration
                     .setLine( jvmArgLine );
         }
 
-        for ( String arg : config.getProviderForkArgs() )
-        {
-            cli.createArg()
-                .setValue( arg );
-        }
-
         if ( getDebugLine() != null && !getDebugLine().isEmpty() )
         {
             cli.createArg()
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 e9ba37c..4cae9f9 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
@@ -97,8 +97,8 @@ public class ModularClasspathForkConfiguration
             File patchFile = modularClasspath.getPatchFile();
             List<String> classpath = toCompleteClasspath( config );
 
-            File argsFile =
-                createArgsFile( moduleName, modulePath, classpath, packages, patchFile, startClass, isMainDescriptor );
+            File argsFile = createArgsFile( moduleName, modulePath, classpath, packages, patchFile, startClass,
+                isMainDescriptor, config.getJpmsArguments() );
 
             cli.createArg().setValue( "@" + escapeToPlatformPath( argsFile.getAbsolutePath() ) );
         }
@@ -114,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, boolean isMainDescriptor )
+                         File patchFile, @Nonnull String startClassName, boolean isMainDescriptor,
+                         @Nonnull List<String[]> providerJpmsArguments )
             throws IOException
     {
         File surefireArgs = createTempFile( "surefireargs", "", getTempDirectory() );
@@ -205,6 +206,15 @@ public class ModularClasspathForkConfiguration
                         .append( NL );
             }
 
+            for ( String[] entries : providerJpmsArguments )
+            {
+                for ( String entry : entries )
+                {
+                    args.append( entry )
+                        .append( NL );
+                }
+            }
+
             args.append( startClassName );
 
             String argsFileContent = args.toString();
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 0254440..d8e1136 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
@@ -60,6 +60,7 @@ import static org.apache.maven.artifact.versioning.VersionRange.createFromVersio
 import static org.fest.assertions.Assertions.assertThat;
 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;
@@ -266,6 +267,154 @@ 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" );
+    }
+
     private static File mockFile( String absolutePath )
     {
         File f = mock( File.class );
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 1fd97ff..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
@@ -683,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] );
 
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 e4f3acc..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
@@ -287,7 +287,7 @@ public class BooterDeserializerProviderConfigurationTest
         ClasspathConfiguration classpathConfiguration = new ClasspathConfiguration( true, true );
 
         return new StartupConfiguration( "com.provider", classpathConfiguration, classLoaderConfiguration, ALL,
-            new String[0] );
+            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 58d4614..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, new String[0] );
+                getManifestOnlyJarForkConfiguration(), null, Collections.<String[]>emptyList() );
         assertNull( saveAndReload( startupConfiguration ).getProcessChecker() );
     }
 
@@ -205,7 +206,7 @@ public class BooterDeserializerStartupConfigurationTest
     private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration )
     {
         return new StartupConfiguration( "com.provider", classpathConfiguration, classLoaderConfiguration, ALL,
-            new String[0] );
+            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 8ffd892..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, new String[0] );
+                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, new String[0] );
+        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 f2bfe48..b3bbf6b 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,43 @@ 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.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 +69,7 @@ public class ForkConfigurationTest
 {
     private static final StartupConfiguration STARTUP_CONFIG = new StartupConfiguration( "",
             new ClasspathConfiguration( true, true ),
-            new ClassLoaderConfiguration( true, true ), ALL, new String[0] );
+            new ClassLoaderConfiguration( true, true ), ALL, Collections.<String[]>emptyList() );
 
     private static int idx = 0;
 
@@ -79,6 +91,159 @@ 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( "", 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" );
+
+        int beginOfFileArg = cliAsString.indexOf( '@' );
+        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" );
+    }
+
+    @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 +255,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, new String[0] );
+        StartupConfiguration startup =
+            new StartupConfiguration( "", cpConfig, clc, ALL, Collections.<String[]>emptyList() );
 
         Commandline cli = config.createCommandLine( startup, 1, temporaryFolder() );
 
@@ -110,7 +276,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, new String[0] );
+        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 +292,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, new String[0] );
+        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 519f700..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, true );
+        File jigsawArgsFile = config.createArgsFile( "abc", modulePath, classPath, packages, patchFile,
+            startClassName, true, Collections.<String[]>emptyList() );
 
         assertThat( jigsawArgsFile )
                 .isNotNull();
@@ -144,7 +144,7 @@ public class ModularClasspathForkConfigurationTest
                         emptyClasspath(), true, true );
         ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
         StartupConfiguration startupConfiguration = new StartupConfiguration( "JUnitCoreProvider",
-            modularClasspathConfiguration, clc, null, new String[0] );
+            modularClasspathConfiguration, clc, null, Collections.<String[]>emptyList() );
         OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
         config.resolveClasspath( cli, ForkedBooter.class.getName(), startupConfiguration,
                 createTempFile( "surefire", "surefire-reports" ) );
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 7dcf0db..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.
@@ -44,13 +45,14 @@ public final class ModularClasspath
     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;
     }
 
@@ -72,7 +74,6 @@ public final class ModularClasspath
         return unmodifiableCollection( packages );
     }
 
-    @Nonnull
     public File getPatchFile()
     {
         return patchFile;
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 846cb0b..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,19 +36,19 @@ public class StartupConfiguration
     private final AbstractPathConfiguration classpathConfiguration;
     private final ClassLoaderConfiguration classLoaderConfiguration;
     private final ProcessCheckerType processChecker;
-    private final String[] providerForkArgs;
+    private final List<String[]> jpmsArguments;
 
     public StartupConfiguration( @Nonnull String providerClassName,
                                  @Nonnull AbstractPathConfiguration classpathConfiguration,
                                  @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
                                  ProcessCheckerType processChecker,
-                                 @Nonnull String[] providerForkArgs )
+                                 @Nonnull List<String[]> jpmsArguments )
     {
         this.classpathConfiguration = classpathConfiguration;
         this.classLoaderConfiguration = classLoaderConfiguration;
         this.providerClassName = providerClassName;
         this.processChecker = processChecker;
-        this.providerForkArgs = providerForkArgs;
+        this.jpmsArguments = jpmsArguments;
     }
 
     public boolean isProviderMainClass()
@@ -60,7 +62,7 @@ public class StartupConfiguration
                                                    ProcessCheckerType processChecker )
     {
         return new StartupConfiguration( providerClassName, classpathConfig, classLoaderConfig,
-            processChecker, new String[0] );
+            processChecker, Collections.<String[]>emptyList() );
     }
 
     public AbstractPathConfiguration getClasspathConfiguration()
@@ -138,8 +140,8 @@ public class StartupConfiguration
         return processChecker;
     }
 
-    public String[] getProviderForkArgs()
+    public List<String[]> getJpmsArguments()
     {
-        return providerForkArgs;
+        return jpmsArguments;
     }
 }