You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by kr...@apache.org on 2013/01/24 08:29:46 UTC

[1/2] git commit: [SUREFIRE-949] add forkCount parameter, making the inconsitent forkMode parameter deprecated.

[SUREFIRE-949] add forkCount parameter, making the inconsitent forkMode parameter deprecated.

- All defaulting works as in the previous versions, with the exception of reuseForks (introduced in the last release). It's now true by default.
- forkCount supports "C" notation as in -T of maven-core


Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/01f5ddbc
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/01f5ddbc
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/01f5ddbc

Branch: refs/heads/master
Commit: 01f5ddbcfe5db33e5518c36199ababc60228e90a
Parents: 9a39962
Author: Andreas Gudian <an...@gmail.com>
Authored: Fri Jan 18 21:15:09 2013 +0100
Committer: Kristian Rosenvold <kr...@apache.org>
Committed: Mon Jan 21 20:08:45 2013 +0100

----------------------------------------------------------------------
 .../plugin/surefire/AbstractSurefireMojo.java      |  141 +++++++++--
 .../surefire/booterclient/ForkConfiguration.java   |    4 +-
 .../plugin/surefire/booterclient/ForkStarter.java  |   26 ++-
 .../maven/plugin/surefire/SurefirePluginTest.java  |  192 +++++++++------
 .../org/apache/maven/surefire/its/ForkModeIT.java  |   84 ++++++-
 .../surefire/its/fixture/SurefireLauncher.java     |   18 +-
 6 files changed, 337 insertions(+), 128 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/01f5ddbc/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
----------------------------------------------------------------------
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 fd00d5f..bc8eb0b 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
@@ -329,10 +329,12 @@ public abstract class AbstractSurefireMojo
     protected Boolean failIfNoTests;
 
     /**
+     * <strong>DEPRECATED</strong> since version 2.14. Use <code>forkCount</code> and <code>reuseForks</code> instead.<br/>
+     * <br/>
      * Option to specify the forking mode. Can be "never", "once", "always", "perthread". "none" and "pertest" are also accepted
-     * for backwards compatibility. "always" forks for each test-class. "perthread" will create "threadCount" parallel forks, each executing one test-class, see also parameter reuseForks.<br/>
+     * for backwards compatibility. "always" forks for each test-class. "perthread" will create <code>threadCount</code> parallel forks, each executing one test-class, see also parameter <code>reuseForks</code>.<br/>
      * The system properties and the "argLine" of the forked processes may contain the place holder string <code>${surefire.threadNumber}</code>,
-     * which is replaced with a fixed number for each thread, ranging from 1 to "threadCount".
+     * which is replaced with a fixed number for each thread, ranging from 1 to <code>threadCount</code>.
      *
      * @since 2.1
      */
@@ -433,8 +435,8 @@ public abstract class AbstractSurefireMojo
     protected String testNGArtifactName;
 
     /**
-     * (forkMode=perthread or TestNG/JUnit 4.7 provider) The attribute thread-count allows you to specify how many threads should be
-     * allocated for this execution. Only makes sense to use in conjunction with the <code>parallel</code> parameter or with forkMode=perthread.
+     * (TestNG/JUnit 4.7 provider) The attribute thread-count allows you to specify how many threads should be
+     * allocated for this execution. Only makes sense to use in conjunction with the <code>parallel</code> parameter.
      *
      * @since 2.2
      */
@@ -443,13 +445,28 @@ public abstract class AbstractSurefireMojo
 
 
     /**
-     * Indicates if forks can be reused. Currently only meaningful
-     * when forking N parallel forks
+     * Option to specify the number of VMs to fork in parallel in order to execute the tests.
+     * When terminated with "C", the number part is multiplied with the number of CPU cores. Floating point value are only accepted together with "C".
+     * If set to "0", no VM is forked and all tests are executed within the main process.<br/>
+     * <br/>
+     * Example values: "1.5C", "4"<br/>
+     * <br/>
+     * The system properties and the <code>argLine</code> of the forked processes may contain the place holder string <code>${surefire.forkNumber}</code>,
+     * which is replaced with a fixed number for each of the parallel forks, ranging from <code>1</code> to the effective value of <code>forkCount</code>.
+     * 
+     * @since 2.14
+     */
+    @Parameter( property = "forkCount", defaultValue="1")
+    private String forkCount;
+
+    /**
+     * Indicates if forked VMs can be reused. If set to "false", a new VM is forked for each test class to be executed. 
+     * If set to "true", up to <code>forkCount</code> VMs will be forked and then reused to execute all tests.
      *
      * @since 2.13
      */
 
-    @Parameter( property = "reuseForks", defaultValue = "false" )
+    @Parameter( property = "reuseForks", defaultValue = "true" )
     private boolean reuseForks;
 
     /**
@@ -587,12 +604,21 @@ public abstract class AbstractSurefireMojo
 
     private Toolchain toolchain;
 
+    private int effectiveForkCount = -1;
+    
     /**
      * The placeholder that is replaced by the executing thread's running number. The thread number
      * range starts with 1
+     * Deprecated.
      */
     public static final String THREAD_NUMBER_PLACEHOLDER = "${surefire.threadNumber}";
 
+    /**
+     * The placeholder that is replaced by the executing fork's running number. The fork number
+     * range starts with 1
+     */
+    public static final String FORK_NUMBER_PLACEHOLDER = "${surefire.forkNumber}";
+
     protected abstract String getPluginName();
 
     private SurefireDependencyResolver dependencyResolver;
@@ -664,6 +690,7 @@ public abstract class AbstractSurefireMojo
         }
         else
         {
+            convertDeprecatedForkMode();
             ensureWorkingDirectoryExists();
             ensureParallelRunningCompatibility();
             ensureThreadCountWithPerThread();
@@ -772,9 +799,9 @@ public abstract class AbstractSurefireMojo
             new RunOrderParameters( getRunOrder(), getStatisticsFileName( getConfigChecksum() ) );
 
         final RunResult result;
-        if ( isForkModeNever() )
+        if ( isNotForking() )
         {
-            createCopyAndReplaceThreadNumPlaceholder( effectiveProperties, 1 ).copyToSystemProperties();
+            createCopyAndReplaceForkNumPlaceholder( effectiveProperties, 1 ).copyToSystemProperties();
 
             InPluginVMSurefireStarter surefireStarter =
                 createInprocessStarter( provider, classLoaderConfiguration, runOrderParameters );
@@ -793,7 +820,7 @@ public abstract class AbstractSurefireMojo
             {
                 ForkStarter forkStarter =
                     createForkStarter( provider, forkConfiguration, classLoaderConfiguration, runOrderParameters );
-                result = forkStarter.run( effectiveProperties, scanResult, getEffectiveForkMode() );
+                result = forkStarter.run( effectiveProperties, scanResult );
             }
             finally
             {
@@ -805,7 +832,7 @@ public abstract class AbstractSurefireMojo
     }
 
 
-    public static SurefireProperties createCopyAndReplaceThreadNumPlaceholder(
+    public static SurefireProperties createCopyAndReplaceForkNumPlaceholder(
         SurefireProperties effectiveSystemProperties, int threadNumber )
     {
         SurefireProperties filteredProperties = new SurefireProperties( effectiveSystemProperties );
@@ -814,9 +841,11 @@ public abstract class AbstractSurefireMojo
         {
             if ( entry.getValue() instanceof String )
             {
-                filteredProperties.put( entry.getKey(),
-                                        ( (String) entry.getValue() ).replace( THREAD_NUMBER_PLACEHOLDER,
-                                                                               threadNumberString ) );
+                String value = (String) entry.getValue();
+                value = value.replace( THREAD_NUMBER_PLACEHOLDER, threadNumberString );
+                value = value.replace( FORK_NUMBER_PLACEHOLDER, threadNumberString );
+
+                filteredProperties.put( entry.getKey(), value );
             }
         }
         return filteredProperties;
@@ -941,11 +970,6 @@ public abstract class AbstractSurefireMojo
         return dependencyResolver.isWithinVersionSpec( artifact, "[4.0,)" );
     }
 
-    boolean isForkModeNever()
-    {
-        return isForkModeNever( getEffectiveForkMode() );
-    }
-
     static boolean isForkModeNever( String forkMode )
     {
         return ForkConfiguration.FORK_NEVER.equals( forkMode );
@@ -953,7 +977,7 @@ public abstract class AbstractSurefireMojo
 
     boolean isForking()
     {
-        return !isForkModeNever();
+        return 0 < getEffectiveForkCount();
     }
 
     String getEffectiveForkMode()
@@ -1392,10 +1416,61 @@ public abstract class AbstractSurefireMojo
                                       getEffectiveForkCount(), reuseForks );
     }
 
+    private void convertDeprecatedForkMode()
+    {
+        String effectiveForkMode = getEffectiveForkMode();
+        // FORK_ONCE (default) is represented by the default values of forkCount and reuseForks 
+        if ( ForkConfiguration.FORK_PERTHREAD.equals( effectiveForkMode ) )
+        {
+            forkCount = String.valueOf(threadCount);
+        }
+        else if ( ForkConfiguration.FORK_NEVER.equals( effectiveForkMode ) )
+        {
+            forkCount = "0";
+        } else if ( ForkConfiguration.FORK_ALWAYS.equals( effectiveForkMode )) {
+            forkCount = "1";
+            reuseForks = false;
+        }
+
+        if ( !ForkConfiguration.FORK_ONCE.equals( getForkMode() ) ) 
+        {
+            getLog().warn( "The parameter forkMode is deprecated since version 2.14. Use forkCount and reuseForks instead." );
+        }
+    }
 
-    private int getEffectiveForkCount()
+    protected int getEffectiveForkCount()
     {
-        return ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) ? getThreadCount() : 1;
+        if ( effectiveForkCount < 0 )
+        {
+            try
+            {
+                effectiveForkCount = convertWithCoreCount( forkCount );
+            }
+            catch ( NumberFormatException ignored )
+            {
+            }
+
+            if ( effectiveForkCount < 0 )
+            {
+                throw new IllegalArgumentException( "Fork count " + forkCount.trim() + " is not a legal value." );
+            }
+        }
+
+        return effectiveForkCount;
+    }
+
+    protected int convertWithCoreCount( String count )
+    {
+        String trimmed = count.trim();
+        if ( trimmed.endsWith( "C" ) )
+        {
+            double multiplier = Double.parseDouble( trimmed.substring( 0, trimmed.length() - 1 ) );
+            return (int) ( multiplier * ( (double) Runtime.getRuntime().availableProcessors() ) );
+        }
+        else
+        {
+            return Integer.parseInt( trimmed );
+        }
     }
 
     private String getEffectiveDebugForkedProcess()
@@ -1482,6 +1557,8 @@ public abstract class AbstractSurefireMojo
         checksum.add( isUseFile() );
         checksum.add( isRedirectTestOutputToFile() );
         checksum.add( getForkMode() );
+        checksum.add( getForkCount() );
+        checksum.add( isReuseForks() );
         checksum.add( getJvm() );
         checksum.add( getArgLine() );
         checksum.add( getDebugForkedProcess() );
@@ -1770,9 +1847,9 @@ public abstract class AbstractSurefireMojo
     void ensureParallelRunningCompatibility()
         throws MojoFailureException
     {
-        if ( isMavenParallel() && isForkModeNever() )
+        if ( isMavenParallel() && isNotForking() )
         {
-            throw new MojoFailureException( "parallel maven execution is not compatible with surefire forkmode NEVER" );
+            throw new MojoFailureException( "parallel maven execution is not compatible with surefire forkCount 0" );
         }
     }
 
@@ -1787,12 +1864,17 @@ public abstract class AbstractSurefireMojo
 
     void warnIfUselessUseSystemClassLoaderParameter()
     {
-        if ( isUseSystemClassLoader() && isForkModeNever() )
+        if ( isUseSystemClassLoader() && isNotForking() )
         {
             getLog().warn( "useSystemClassloader setting has no effect when not forking" );
         }
     }
 
+    private boolean isNotForking()
+    {
+        return !isForking();
+    }
+
     void warnIfDefunctGroupsCombinations()
         throws MojoFailureException, MojoExecutionException
     {
@@ -2478,4 +2560,13 @@ public abstract class AbstractSurefireMojo
         this.testSourceDirectory = testSourceDirectory;
     }
 
+    public String getForkCount()
+    {
+        return forkCount;
+    }
+
+    public boolean isReuseForks()
+    {
+        return reuseForks;
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/01f5ddbc/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
index 9f8780b..15d76a3 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
@@ -116,7 +116,6 @@ public class ForkConfiguration
         }
     }
 
-
     /**
      * @param classPath              cla the classpath arguments
      * @param classpathConfiguration the classpath configuration
@@ -196,7 +195,8 @@ public class ForkConfiguration
 
     private String replaceThreadNumberPlaceholder( String argLine, int threadNumber )
     {
-        return argLine.replace( AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER, String.valueOf( threadNumber ) );
+        return argLine.replace( AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER, String.valueOf( threadNumber ) )
+                        .replace( AbstractSurefireMojo.FORK_NUMBER_PLACEHOLDER, String.valueOf( threadNumber ) );
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/01f5ddbc/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
index 6a2adea..85535e9 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
@@ -22,7 +22,6 @@ package org.apache.maven.plugin.surefire.booterclient;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.security.AccessControlException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -150,8 +149,7 @@ public class ForkStarter
         defaultReporterFactory = new DefaultReporterFactory( startupReportConfiguration );
     }
 
-    public RunResult run( SurefireProperties effectiveSystemProperties, DefaultScanResult scanResult,
-                          String requestedForkMode )
+    public RunResult run( SurefireProperties effectiveSystemProperties, DefaultScanResult scanResult )
         throws SurefireBooterForkException, SurefireExecutionException
     {
         final RunResult result;
@@ -159,7 +157,7 @@ public class ForkStarter
         {
             Properties providerProperties = providerConfiguration.getProviderProperties();
             scanResult.writeTo( providerProperties );
-            if ( ForkConfiguration.FORK_ONCE.equals( requestedForkMode ) )
+            if ( isForkOnce() )
             {
                 final ForkClient forkClient =
                     new ForkClient( defaultReporterFactory, startupReportConfiguration.getTestVmSystemProperties() );
@@ -167,11 +165,11 @@ public class ForkStarter
                     fork( null, new PropertiesWrapper( providerProperties ), forkClient, effectiveSystemProperties, 1,
                           null );
             }
-            else if ( ForkConfiguration.FORK_ALWAYS.equals( requestedForkMode ) )
+            else if ( isForkAlways() )
             {
                 result = runSuitesForkPerTestSet( effectiveSystemProperties, 1 );
             }
-            else if ( ForkConfiguration.FORK_PERTHREAD.equals( requestedForkMode ) )
+            else
             {
                 if ( forkConfiguration.isReuseForks() )
                 {
@@ -182,10 +180,6 @@ public class ForkStarter
                     result = runSuitesForkPerTestSet( effectiveSystemProperties, forkConfiguration.getForkCount() );
                 }
             }
-            else
-            {
-                throw new SurefireExecutionException( "Unknown forkmode: " + requestedForkMode, null );
-            }
         }
         finally
         {
@@ -194,6 +188,16 @@ public class ForkStarter
         return result;
     }
 
+    private boolean isForkAlways()
+    {
+        return !forkConfiguration.isReuseForks() && 1 == forkConfiguration.getForkCount();
+    }
+
+    private boolean isForkOnce()
+    {
+        return forkConfiguration.isReuseForks() && 1 == forkConfiguration.getForkCount();
+    }
+
     private RunResult runSuitesForkOncePerThread( final SurefireProperties effectiveSystemProperties, int forkCount )
         throws SurefireBooterForkException
     {
@@ -383,7 +387,7 @@ public class ForkStarter
             if ( effectiveSystemProperties != null )
             {
                 SurefireProperties filteredProperties =
-                    AbstractSurefireMojo.createCopyAndReplaceThreadNumPlaceholder( effectiveSystemProperties,
+                    AbstractSurefireMojo.createCopyAndReplaceForkNumPlaceholder( effectiveSystemProperties,
                                                                                    threadNumber );
                 systPropsFile =
                     SystemPropertyManager.writePropertiesFile( filteredProperties,

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/01f5ddbc/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java b/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
index 5252a24..11abd51 100644
--- a/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
+++ b/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
@@ -1,79 +1,113 @@
-package org.apache.maven.plugin.surefire;
-/*
- * 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 java.lang.reflect.Field;
-import org.apache.maven.plugin.surefire.booterclient.ForkConfiguration;
-import org.apache.maven.toolchain.Toolchain;
-
-import junit.framework.TestCase;
-
-public class SurefirePluginTest
-    extends TestCase
-{
-
-    public void testForkMode()
-        throws NoSuchFieldException, IllegalAccessException
-    {
-        SurefirePlugin surefirePlugin = new SurefirePlugin();
-        setFieldValue( surefirePlugin, "toolchain", new MyToolChain() );
-        setFieldValue( surefirePlugin, "forkMode", "never" );
-        assertEquals( ForkConfiguration.FORK_ONCE, surefirePlugin.getEffectiveForkMode() );
-    }
-
-    private void setFieldValue( SurefirePlugin plugin, String fieldName, Object value )
-        throws NoSuchFieldException, IllegalAccessException
-    {
-        Field field = findField( plugin.getClass(), fieldName );
-        field.setAccessible( true );
-        field.set( plugin, value );
-
-    }
-
-    private Field findField( Class clazz, String fieldName )
-    {
-        while ( clazz != null )
-        {
-            try
-            {
-                return clazz.getDeclaredField( fieldName );
-            }
-            catch ( NoSuchFieldException e )
-            {
-                clazz = clazz.getSuperclass();
-            }
-        }
-        throw new IllegalArgumentException( "Field not found" );
-    }
-
-    private class MyToolChain
-        implements Toolchain
-    {
-        public String getType()
-        {
-            return null;
-        }
-
-        public String findTool( String s )
-        {
-            return null;
-        }
-    }
-}
+package org.apache.maven.plugin.surefire;
+/*
+ * 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 java.lang.reflect.Field;
+import org.apache.maven.plugin.surefire.booterclient.ForkConfiguration;
+import org.apache.maven.toolchain.Toolchain;
+
+import junit.framework.TestCase;
+
+public class SurefirePluginTest
+    extends TestCase
+{
+
+    public void testForkMode()
+        throws NoSuchFieldException, IllegalAccessException
+    {
+        SurefirePlugin surefirePlugin = new SurefirePlugin();
+        setFieldValue( surefirePlugin, "toolchain", new MyToolChain() );
+        setFieldValue( surefirePlugin, "forkMode", "never" );
+        assertEquals( ForkConfiguration.FORK_ONCE, surefirePlugin.getEffectiveForkMode() );
+    }
+
+    public void testForkCountComputation()
+    {
+        SurefirePlugin surefirePlugin = new SurefirePlugin();
+        assertConversionFails( surefirePlugin, "nothing" );
+
+        assertConversionFails( surefirePlugin, "5,0" );
+        assertConversionFails( surefirePlugin, "5.0" );
+        assertConversionFails( surefirePlugin, "5,0C" );
+        assertConversionFails( surefirePlugin, "5.0CC" );
+        
+        assertForkCount( surefirePlugin, 5, "5" );
+        
+        int availableProcessors = Runtime.getRuntime().availableProcessors();
+        assertForkCount( surefirePlugin, 3*availableProcessors, "3C" );
+        assertForkCount( surefirePlugin, (int) 2.5*availableProcessors, "2.5C" );
+        assertForkCount( surefirePlugin, availableProcessors, "1.0001 C" );
+    }
+
+    private void assertForkCount( SurefirePlugin surefirePlugin, int expected, String value )
+    {
+        assertEquals( expected, surefirePlugin.convertWithCoreCount( value ));
+    }
+    
+    private void assertConversionFails( SurefirePlugin surefirePlugin, String value )
+    {
+        try {
+            surefirePlugin.convertWithCoreCount( value );
+        } catch (NumberFormatException nfe)
+        {
+            return;
+        }
+        fail( "Expected NumberFormatException when converting " + value );
+    }
+
+    private void setFieldValue( SurefirePlugin plugin, String fieldName, Object value )
+        throws NoSuchFieldException, IllegalAccessException
+    {
+        Field field = findField( plugin.getClass(), fieldName );
+        field.setAccessible( true );
+        field.set( plugin, value );
+
+    }
+
+    private Field findField( Class clazz, String fieldName )
+    {
+        while ( clazz != null )
+        {
+            try
+            {
+                return clazz.getDeclaredField( fieldName );
+            }
+            catch ( NoSuchFieldException e )
+            {
+                clazz = clazz.getSuperclass();
+            }
+        }
+        throw new IllegalArgumentException( "Field not found" );
+    }
+
+    private class MyToolChain
+        implements Toolchain
+    {
+        public String getType()
+        {
+            return null;
+        }
+
+        public String findTool( String s )
+        {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/01f5ddbc/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ForkModeIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ForkModeIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ForkModeIT.java
index fde17ea..35c53d7 100644
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ForkModeIT.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ForkModeIT.java
@@ -19,6 +19,7 @@ package org.apache.maven.surefire.its;
  * under the License.
  */
 
+import java.lang.management.ManagementFactory;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
@@ -29,7 +30,7 @@ import org.apache.maven.surefire.its.fixture.TestFile;
 
 /**
  * Test forkMode
- *
+ * 
  * @author <a href="mailto:dfabulich@apache.org">Dan Fabulich</a>
  */
 public class ForkModeIT
@@ -39,36 +40,103 @@ public class ForkModeIT
     {
         String[] pids = doTest( unpack( getProject() ).debugLogging().forkAlways() );
         assertDifferentPids( pids );
+        assertEndWith( pids, "_1_1", 3);
+        assertFalse( "pid 1 is not the same as the main process' pid", pids[0].equals( getMyPID() ) );
     }
 
     public void testForkModePerTest()
     {
         String[] pids = doTest( unpack( getProject() ).debugLogging().forkPerTest() );
         assertDifferentPids( pids );
+        assertEndWith( pids, "_1_1", 3);
+        assertFalse( "pid 1 is not the same as the main process' pid", pids[0].equals( getMyPID() ) );
     }
 
     public void testForkModeNever()
     {
         String[] pids = doTest( unpack( getProject() ).debugLogging().forkNever() );
         assertSamePids( pids );
+        assertEndWith( pids, "_1_1", 3);
+        assertEquals( "my pid is equal to pid 1 of the test", getMyPID(), pids[0] );
     }
 
     public void testForkModeNone()
     {
         String[] pids = doTest( unpack( getProject() ).debugLogging().forkMode( "none" ) );
         assertSamePids( pids );
+        assertEndWith( pids, "_1_1", 3);
+        assertEquals( "my pid is equal to pid 1 of the test", getMyPID(), pids[0] );
     }
 
     public void testForkModeOncePerThreadSingleThread()
     {
         String[] pids = doTest( unpack( getProject() ).debugLogging().forkOncePerThread().threadCount( 1 ) );
         assertSamePids( pids );
+        assertEndWith( pids, "_1_1", 3);
+        assertFalse( "pid 1 is not the same as the main process' pid", pids[0].equals( getMyPID() ) );
     }
 
     public void testForkModeOncePerThreadTwoThreads()
     {
-        String[] pids = doTest( unpack( getProject() ).debugLogging().forkOncePerThread().threadCount( 2 ) );
+        String[] pids = doTest( unpack( getProject() ).debugLogging().forkOncePerThread().threadCount( 2 ).addGoal( "-DsleepLength=1200" ) );
+        assertDifferentPids( pids, 2 );
+        assertEndWith( pids, "_1_1", 1);
+        assertEndWith( pids, "_2_2", 2);
+        assertFalse( "pid 1 is not the same as the main process' pid", pids[0].equals( getMyPID() ) );
+    }
+
+    public void testForkCountZero()
+    {
+        String[] pids = doTest( unpack( getProject() ).debugLogging().forkCount( 0 ) );
+        assertSamePids( pids );
+        assertEndWith( pids, "_1_1", 3);
+        assertEquals( "my pid is equal to pid 1 of the test", getMyPID(), pids[0] );
+    }
+
+    public void testForkCountOneNoReuse()
+    {
+        String[] pids = doTest( unpack( getProject() ).debugLogging().forkCount( 1 ).reuseForks( false ) );
+        assertDifferentPids( pids );
+        assertEndWith( pids, "_1_1", 3);
+        assertFalse( "pid 1 is not the same as the main process' pid", pids[0].equals( getMyPID() ) );
+    }
+
+    public void testForkCountOneReuse()
+    {
+        String[] pids = doTest( unpack( getProject() ).debugLogging().forkCount( 1 ).reuseForks( true ) );
+        assertSamePids( pids );
+        assertEndWith( pids, "_1_1", 3);
+        assertFalse( "pid 1 is not the same as the main process' pid", pids[0].equals( getMyPID() ) );
+    }
+
+    public void testForkCountTwoNoReuse()
+    {
+        String[] pids = doTest( unpack( getProject() ).debugLogging().forkCount( 2 ).reuseForks( false ).addGoal( "-DsleepLength=1200" ) );
+        assertDifferentPids( pids );
+        assertEndWith( pids, "_1_1", 1);
+        assertEndWith( pids, "_2_2", 2);
+        assertFalse( "pid 1 is not the same as the main process' pid", pids[0].equals( getMyPID() ) );
+    }
+
+    public void testForkCountTwoReuse()
+    {
+        String[] pids = doTest( unpack( getProject() ).debugLogging().forkCount( 2 ).reuseForks( true ).addGoal( "-DsleepLength=1200" ) );
         assertDifferentPids( pids, 2 );
+        assertEndWith( pids, "_1_1", 1);
+        assertEndWith( pids, "_2_2", 2);
+        assertFalse( "pid 1 is not the same as the main process' pid", pids[0].equals( getMyPID() ) );
+    }
+
+    private void assertEndWith( String[] pids, String suffix, int expectedMatches )
+    {
+        int matches = 0;
+        for (String pid : pids) {
+            if ( pid.endsWith( suffix )) {
+                matches++;
+            }
+        }
+        
+        assertEquals( "suffix " + suffix + " matched the correct number of pids", expectedMatches, matches );
     }
 
     private void assertDifferentPids( String[] pids, int numOfDifferentPids )
@@ -80,11 +148,13 @@ public class ForkModeIT
     public void testForkModeOnce()
     {
         String[] pids = doTest( unpack( getProject() ).forkOnce() );
-        // DGF It would be nice to assert that "once" was different
-        // from "never" ... but there's no way to check the PID of
-        // Maven itself.  No matter, "once" is tested by setting
-        // argLine, which can't be done except by forking.
         assertSamePids( pids );
+        assertFalse( "pid 1 is not the same as the main process' pid", pids[0].equals( getMyPID() ) );
+    }
+
+    private String getMyPID()
+    {
+        return ManagementFactory.getRuntimeMXBean().getName() + " testValue_1_1";
     }
 
     private void assertSamePids( String[] pids )
@@ -113,7 +183,7 @@ public class ForkModeIT
 
     private String[] doTest( SurefireLauncher forkMode )
     {
-        forkMode.sysProp( "testProperty", "testValue_${surefire.threadNumber}" );
+        forkMode.sysProp( "testProperty", "testValue_${surefire.threadNumber}_${surefire.forkNumber}" );
         final OutputValidator outputValidator = forkMode.executeTest();
         outputValidator.verifyErrorFreeLog().assertTestSuiteResults( 3, 0, 0, 0 );
         String[] pids = new String[3];

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/01f5ddbc/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
index 135216e..201738f 100755
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
@@ -233,14 +233,12 @@ public class SurefireLauncher
 
     public SurefireLauncher forkPerThread()
     {
-        return forkMode( "perthread" );
+        return forkMode( "perthread" ).reuseForks( false );
     }
 
     public SurefireLauncher forkOncePerThread()
     {
-        forkPerThread();
-        mavenLauncher.sysProp( "reuseForks", true );
-        return this;
+        return forkPerThread().reuseForks( true );
     }
 
     public SurefireLauncher threadCount( int threadCount )
@@ -249,6 +247,18 @@ public class SurefireLauncher
         return this;
     }
 
+    public SurefireLauncher forkCount( int forkCount )
+    {
+        mavenLauncher.sysProp( "forkCount", forkCount );
+        return this;
+    }
+
+    public SurefireLauncher reuseForks( boolean reuseForks )
+    {
+        mavenLauncher.sysProp( "reuseForks", reuseForks );
+        return this;
+    }
+
     public SurefireLauncher forkMode( String forkMode )
     {
         mavenLauncher.sysProp( "forkMode", forkMode );