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 2012/11/17 11:25:00 UTC

[2/3] git commit: [SUREFIRE-751] forkMode=onceperthread

[SUREFIRE-751] forkMode=onceperthread


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

Branch: refs/heads/master
Commit: 9c6ce9be423219e88d6e9c777ba69eea5e831d0c
Parents: 0b27dc2
Author: agudian <an...@gmail.com>
Authored: Tue Nov 6 21:54:00 2012 +0100
Committer: Kristian Rosenvold <kr...@apache.org>
Committed: Sat Nov 17 11:15:26 2012 +0100

----------------------------------------------------------------------
 .../plugin/surefire/AbstractSurefireMojo.java      |   45 +++-
 .../surefire/booterclient/BooterSerializer.java    |    8 +-
 .../surefire/booterclient/ForkConfiguration.java   |   25 ++-
 .../plugin/surefire/booterclient/ForkStarter.java  |  172 ++++++++++++---
 .../lazytestprovider/FlushReceiver.java            |    7 +
 .../lazytestprovider/FlushReceiverProvider.java    |    5 +
 .../lazytestprovider/ProcessAwareCommandline.java  |   41 ++++
 .../lazytestprovider/TestProvidingInputStream.java |   52 +++++
 .../surefire/booterclient/output/ForkClient.java   |   17 ++
 ...ooterDeserializerProviderConfigurationTest.java |   59 ++++--
 ...BooterDeserializerStartupConfigurationTest.java |   10 +-
 .../booterclient/ForkConfigurationTest.java        |    4 +-
 pom.xml                                            |    2 +-
 .../maven/surefire/booter/ForkingRunListener.java  |    3 +
 .../apache/maven/surefire/util/LazyTestsToRun.java |  135 +++++++++++
 .../maven/surefire/booter/BooterConstants.java     |    1 +
 .../maven/surefire/booter/BooterDeserializer.java  |    5 +-
 .../apache/maven/surefire/booter/ForkedBooter.java |   15 ++-
 .../surefire/booter/ProviderConfiguration.java     |   12 +-
 .../org/apache/maven/surefire/its/ForkModeIT.java  |   24 ++-
 .../surefire/its/fixture/SurefireLauncher.java     |   10 +
 .../Surefire907PerThreadWithoutThreadCountIT.java  |    3 +-
 .../src/test/resources/fork-mode-testng/pom.xml    |    1 +
 .../src/test/java/forkMode/Test1.java              |    5 +
 .../src/test/resources/fork-mode/pom.xml           |    1 +
 .../fork-mode/src/test/java/forkMode/Test1.java    |    7 +-
 .../maven/surefire/junit/JUnit3Provider.java       |   12 +-
 .../maven/surefire/junit4/JUnit4Provider.java      |   18 ++-
 .../surefire/junitcore/JUnitCoreProvider.java      |   17 +-
 .../maven/surefire/junitcore/JUnitCoreWrapper.java |   23 ++-
 .../surefire/testng/TestNGDirectoryTestSuite.java  |  110 ++++++----
 .../maven/surefire/testng/TestNGProvider.java      |   12 +-
 32 files changed, 722 insertions(+), 139 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/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 7d14557..3214e2f 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
@@ -29,6 +29,7 @@ import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.Set;
 import org.apache.maven.artifact.Artifact;
@@ -327,8 +328,11 @@ public abstract class AbstractSurefireMojo
     protected Boolean failIfNoTests;
 
     /**
-     * Option to specify the forking mode. Can be "never", "once", "always" or "perthread". "none" and "pertest" are also accepted
-     * for backwards compatibility. "always" forks for each test-class. "perthread" will create "threadCount" parallel forks.
+     * Option to specify the forking mode. Can be "never", "once", "always", "perthread" or "onceperthread". "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.
+     * "onceperthread" will fork "threadCount" processes that each execute a 1/"threadCount" of all test-classes.<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".
      *
      * @since 2.1
      */
@@ -429,9 +433,9 @@ 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. (forkMode=perthread
-     * does not support/require the <code>parallel</code> parameter)
+     * (forkMode=perthread, forkmode=onceperthread 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
+     * or forkmode=onceperthread.
      *
      * @since 2.2
      */
@@ -572,6 +576,12 @@ public abstract class AbstractSurefireMojo
     private Artifact surefireBooterArtifact;
 
     private Toolchain toolchain;
+    
+    /**
+     * The placeholder that is replaced by the executing thread's running number. The thread number
+     * range starts with 1 
+     */
+    public static final String THREAD_NUMBER_PLACEHOLDER = "${surefire.threadNumber}";
 
     protected abstract String getPluginName();
 
@@ -741,7 +751,8 @@ public abstract class AbstractSurefireMojo
         final RunResult result;
         if ( isForkModeNever() )
         {
-            effectiveProperties.copyToSystemProperties();
+            createCopyAndReplaceThreadNumPlaceholder(effectiveProperties, 1).copyToSystemProperties();
+
             InPluginVMSurefireStarter surefireStarter =
                 createInprocessStarter( provider, classLoaderConfiguration, runOrderParameters );
             result = surefireStarter.runSuitesInProcess( scanResult );
@@ -771,6 +782,18 @@ public abstract class AbstractSurefireMojo
         return result;
     }
 
+
+    public static SurefireProperties createCopyAndReplaceThreadNumPlaceholder(SurefireProperties effectiveSystemProperties, int threadNumber) {
+        SurefireProperties filteredProperties = new SurefireProperties(effectiveSystemProperties);
+        String threadNumberString = String.valueOf(threadNumber);
+        for (Entry<Object,Object> entry : effectiveSystemProperties.entrySet()) {
+            if (entry.getValue() instanceof String) {
+                filteredProperties.put(entry.getKey(), ((String)entry.getValue()).replace(THREAD_NUMBER_PLACEHOLDER, threadNumberString));
+            }
+        }
+        return filteredProperties;
+    }
+
     protected void cleanupForkConfiguration( ForkConfiguration forkConfiguration )
     {
         if ( !getLog().isDebugEnabled() && forkConfiguration != null )
@@ -1002,7 +1025,7 @@ public abstract class AbstractSurefireMojo
 
         return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, failIfNoTests,
                                           reporterConfiguration, testNg, testSuiteDefinition, providerProperties,
-                                          null );
+                                          null, false );
     }
 
     public String getStatisticsFileName( String configurationHash )
@@ -1344,7 +1367,8 @@ public abstract class AbstractSurefireMojo
 
     private int getEffectiveForkCount()
     {
-        return ( ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) ) ? getThreadCount() : 1;
+        return ( ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) ||
+                 ForkConfiguration.FORK_ONCE_PERTHREAD.equals( getEffectiveForkMode() ) ) ? getThreadCount() : 1;
     }
 
     private String getEffectiveDebugForkedProcess()
@@ -1728,9 +1752,10 @@ public abstract class AbstractSurefireMojo
     void ensureThreadCountWithPerThread()
         throws MojoFailureException
     {
-        if ( ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) && getThreadCount() < 1 )
+        if ( ( ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) || 
+               ForkConfiguration.FORK_ONCE_PERTHREAD.equals( getEffectiveForkMode() )) && getThreadCount() < 1 )
         {
-            throw new MojoFailureException( "Fork mode perthread requires a thread count" );
+            throw new MojoFailureException( "Fork modes perthread and onceperthread require a thread count" );
         }
     }
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
index c4f2c16..5b1dcbb 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
@@ -21,7 +21,9 @@ package org.apache.maven.plugin.surefire.booterclient;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.List;
 import java.util.Properties;
+
 import org.apache.maven.surefire.booter.BooterConstants;
 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
 import org.apache.maven.surefire.booter.KeyValueSource;
@@ -66,7 +68,7 @@ class BooterSerializer
     DOes not modify sourceProperties
      */
     public File serialize(KeyValueSource sourceProperties, ProviderConfiguration booterConfiguration, StartupConfiguration providerConfiguration,
-                          Object testSet)
+                          Object testSet, boolean readTestsFromInStream)
         throws IOException
     {
 
@@ -82,7 +84,9 @@ class BooterSerializer
             properties.setProperty( BooterConstants.TESTARTIFACT_CLASSIFIER, testNg.getClassifier() );
         }
 
-        properties.setProperty( BooterConstants.FORKTESTSET, getTypeEncoded( testSet ) );
+       	properties.setProperty( BooterConstants.FORKTESTSET_PREFER_TESTS_FROM_IN_STREAM, Boolean.valueOf( readTestsFromInStream ) );        	
+       	properties.setProperty( BooterConstants.FORKTESTSET, getTypeEncoded( testSet ) );
+
         TestRequest testSuiteDefinition = booterConfiguration.getTestSuiteDefinition();
         if ( testSuiteDefinition != null )
         {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/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 451af02..fef053a 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
@@ -27,6 +27,9 @@ import java.util.Map;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 import java.util.jar.Manifest;
+
+import org.apache.maven.plugin.surefire.AbstractSurefireMojo;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.ProcessAwareCommandline;
 import org.apache.maven.plugin.surefire.util.Relocator;
 import org.apache.maven.shared.utils.StringUtils;
 import org.apache.maven.shared.utils.cli.Commandline;
@@ -52,6 +55,8 @@ public class ForkConfiguration
     public static final String FORK_NEVER = "never";
 
     public static final String FORK_PERTHREAD = "perthread";
+    
+    public static final String FORK_ONCE_PERTHREAD = "onceperthread";
 
     private final int forkCount;
 
@@ -102,7 +107,8 @@ public class ForkConfiguration
             return FORK_NEVER;
         }
         else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE ) ||
-            forkMode.equals( FORK_ALWAYS ) || forkMode.equals( FORK_PERTHREAD ) )
+            forkMode.equals( FORK_ALWAYS ) || forkMode.equals( FORK_PERTHREAD ) ||
+            forkMode.equals( FORK_ONCE_PERTHREAD ))
         {
             return forkMode;
         }
@@ -117,27 +123,28 @@ public class ForkConfiguration
      * @param classPath              cla the classpath arguments
      * @param classpathConfiguration the classpath configuration
      * @param shadefire              true if running shadefire
+     * @param threadNumber           the thread number, to be the replacement in the argLine
      * @return A commandline
      * @throws org.apache.maven.surefire.booter.SurefireBooterForkException
      *          when unable to perform the fork
      */
-    public Commandline createCommandLine( List<String> classPath, ClassLoaderConfiguration classpathConfiguration,
-                                          boolean shadefire )
+    public ProcessAwareCommandline createCommandLine( List<String> classPath, ClassLoaderConfiguration classpathConfiguration,
+                                          boolean shadefire, int threadNumber )
         throws SurefireBooterForkException
     {
-        return createCommandLine( classPath, classpathConfiguration.isManifestOnlyJarRequestedAndUsable(), shadefire );
+        return createCommandLine( classPath, classpathConfiguration.isManifestOnlyJarRequestedAndUsable(), shadefire, threadNumber );
     }
 
-    public Commandline createCommandLine( List<String> classPath, boolean useJar, boolean shadefire )
+    public ProcessAwareCommandline createCommandLine( List<String> classPath, boolean useJar, boolean shadefire, int threadNumber )
         throws SurefireBooterForkException
     {
-        Commandline cli = new Commandline();
+    	ProcessAwareCommandline cli = new ProcessAwareCommandline();
 
         cli.setExecutable( jvmExecutable );
 
         if ( argLine != null )
         {
-            cli.createArg().setLine( stripNewLines( argLine ) );
+            cli.createArg().setLine( replaceThreadNumberPlaceholder(stripNewLines( argLine ), threadNumber) );
         }
 
         if ( environmentVariables != null )
@@ -186,6 +193,10 @@ public class ForkConfiguration
         return cli;
     }
 
+    private String replaceThreadNumberPlaceholder(String argLine, int threadNumber) {
+        return argLine.replace(AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER, String.valueOf(threadNumber));
+    }
+
     /**
      * Create a jar with just a manifest containing a Main-Class entry for BooterConfiguration and a Class-Path entry
      * for all classpath elements.

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/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 7a7b4bc..6c135b3 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
@@ -23,24 +23,31 @@ import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Properties;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.maven.plugin.surefire.AbstractSurefireMojo;
 import org.apache.maven.plugin.surefire.CommonReflector;
 import org.apache.maven.plugin.surefire.StartupReportConfiguration;
 import org.apache.maven.plugin.surefire.SurefireProperties;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.ProcessAwareCommandline;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
 import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
 import org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.shared.utils.cli.CommandLineException;
 import org.apache.maven.shared.utils.cli.CommandLineTimeOutException;
 import org.apache.maven.shared.utils.cli.CommandLineUtils;
-import org.apache.maven.shared.utils.cli.Commandline;
 import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.ClasspathConfiguration;
 import org.apache.maven.surefire.booter.KeyValueSource;
@@ -52,9 +59,9 @@ import org.apache.maven.surefire.booter.SurefireBooterForkException;
 import org.apache.maven.surefire.booter.SurefireExecutionException;
 import org.apache.maven.surefire.booter.SystemPropertyManager;
 import org.apache.maven.surefire.providerapi.SurefireProvider;
-import org.apache.maven.surefire.report.RunStatistics;
 import org.apache.maven.surefire.suite.RunResult;
 import org.apache.maven.surefire.util.DefaultScanResult;
+import org.codehaus.plexus.util.CollectionUtils;
 
 
 /**
@@ -87,6 +94,17 @@ public class ForkStarter
 
     private static volatile int systemPropertiesFileCounter = 0;
 
+    private final ThreadLocal<Integer> threadNumber = new ThreadLocal<Integer>()
+    {
+        private final AtomicInteger nextThreadNumber = new AtomicInteger( 1 );
+
+        @Override
+        protected Integer initialValue()
+        {
+            return nextThreadNumber.getAndIncrement();
+        }
+    };
+
 
     public ForkStarter( ProviderConfiguration providerConfiguration, StartupConfiguration startupConfiguration,
                         ForkConfiguration forkConfiguration, int forkedProcessTimeoutInSeconds,
@@ -113,17 +131,21 @@ public class ForkStarter
             {
                 final ForkClient forkClient =
                     new ForkClient( fileReporterFactory, startupReportConfiguration.getTestVmSystemProperties() );
-                result = fork( null, new PropertiesWrapper( providerProperties ), forkClient,
-                               fileReporterFactory.getGlobalRunStatistics(), effectiveSystemProperties );
+                result =
+                    fork( null, new PropertiesWrapper( providerProperties ), forkClient, effectiveSystemProperties, 1,
+                          null );
             }
             else if ( ForkConfiguration.FORK_ALWAYS.equals( requestedForkMode ) )
             {
-                result = runSuitesForkPerTestSet( providerProperties, effectiveSystemProperties, 1 );
+                result = runSuitesForkPerTestSet( effectiveSystemProperties, 1 );
             }
             else if ( ForkConfiguration.FORK_PERTHREAD.equals( requestedForkMode ) )
             {
-                result = runSuitesForkPerTestSet( providerProperties, effectiveSystemProperties,
-                                                  forkConfiguration.getForkCount() );
+                result = runSuitesForkPerTestSet( effectiveSystemProperties, forkConfiguration.getForkCount() );
+            }
+            else if ( ForkConfiguration.FORK_ONCE_PERTHREAD.equals( requestedForkMode ) )
+            {
+                result = runSuitesForkOncePerThread( effectiveSystemProperties, forkConfiguration.getForkCount() );
             }
             else
             {
@@ -137,8 +159,85 @@ public class ForkStarter
         return result;
     }
 
-    private RunResult runSuitesForkPerTestSet( final Properties properties,
-                                               final SurefireProperties effectiveSystemProperties, int forkCount )
+    private RunResult runSuitesForkOncePerThread( final SurefireProperties effectiveSystemProperties, int forkCount )
+        throws SurefireBooterForkException
+    {
+
+        ArrayList<Future<RunResult>> results = new ArrayList<Future<RunResult>>( forkCount );
+        ExecutorService executorService = new ThreadPoolExecutor( forkCount, forkCount, 60, TimeUnit.SECONDS,
+                                                                  new ArrayBlockingQueue<Runnable>( forkCount ) );
+
+        try
+        {
+            // Ask to the executorService to run all tasks
+            RunResult globalResult = new RunResult( 0, 0, 0, 0 );
+
+            List<Class<?>> suites = CollectionUtils.iteratorToList( getSuitesIterator() );
+            final Queue<String> messageQueue = new ConcurrentLinkedQueue<String>();
+            for ( Class<?> clazz : suites )
+            {
+                messageQueue.add( clazz.getName() );
+            }
+
+            for ( int threadNum = 0; threadNum < forkCount && threadNum < suites.size(); threadNum++ )
+            {
+                final int finalThreadNumber = threadNum + 1;
+
+                Callable<RunResult> pf = new Callable<RunResult>()
+                {
+                    public RunResult call()
+                        throws Exception
+                    {
+                        TestProvidingInputStream testProvidingInputStream =
+                            new TestProvidingInputStream( messageQueue );
+
+                        ForkClient forkClient =
+                            new ForkClient( fileReporterFactory, startupReportConfiguration.getTestVmSystemProperties(),
+                                            testProvidingInputStream );
+
+                        return fork( null, new PropertiesWrapper( providerConfiguration.getProviderProperties() ),
+                                     forkClient, effectiveSystemProperties, finalThreadNumber,
+                                     testProvidingInputStream );
+                    }
+                };
+
+                results.add( executorService.submit( pf ) );
+            }
+
+            for ( Future<RunResult> result : results )
+            {
+                try
+                {
+                    RunResult cur = result.get();
+                    if ( cur != null )
+                    {
+                        globalResult = globalResult.aggregate( cur );
+                    }
+                    else
+                    {
+                        throw new SurefireBooterForkException( "No results for " + result.toString() );
+                    }
+                }
+                catch ( InterruptedException e )
+                {
+                    throw new SurefireBooterForkException( "Interrupted", e );
+                }
+                catch ( ExecutionException e )
+                {
+                    throw new SurefireBooterForkException( "ExecutionException", e );
+                }
+            }
+            return globalResult;
+
+        }
+        finally
+        {
+            closeExecutor( executorService );
+        }
+
+    }
+
+    private RunResult runSuitesForkPerTestSet( final SurefireProperties effectiveSystemProperties, final int forkCount )
         throws SurefireBooterForkException
     {
 
@@ -150,19 +249,27 @@ public class ForkStarter
         {
             // Ask to the executorService to run all tasks
             RunResult globalResult = new RunResult( 0, 0, 0, 0 );
-            final Iterator suites = getSuitesIterator();
+            final Iterator<Class<?>> suites = getSuitesIterator();
             while ( suites.hasNext() )
             {
                 final Object testSet = suites.next();
-                final ForkClient forkClient =
-                    new ForkClient( fileReporterFactory, startupReportConfiguration.getTestVmSystemProperties() );
                 Callable<RunResult> pf = new Callable<RunResult>()
                 {
                     public RunResult call()
                         throws Exception
                     {
-                        return fork( testSet, new PropertiesWrapper( properties ), forkClient,
-                                     fileReporterFactory.getGlobalRunStatistics(), effectiveSystemProperties );
+                        int thisThreadsThreadNumber = threadNumber.get();
+                        if ( thisThreadsThreadNumber > forkCount )
+                        {
+                            // this would be a bug in the ThreadPoolExecutor
+                            throw new IllegalStateException(
+                                "More threads than " + forkCount + " have been created by the ThreadPoolExecutor." );
+                        }
+
+                        ForkClient forkClient = new ForkClient( fileReporterFactory,
+                                                                startupReportConfiguration.getTestVmSystemProperties() );
+                        return fork( testSet, new PropertiesWrapper( providerConfiguration.getProviderProperties() ),
+                                     forkClient, effectiveSystemProperties, thisThreadsThreadNumber, null );
                     }
                 };
                 results.add( executorService.submit( pf ) );
@@ -217,9 +324,9 @@ public class ForkStarter
         }
     }
 
-
     private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkClient forkClient,
-                            RunStatistics globalRunStatistics, SurefireProperties effectiveSystemProperties )
+                            SurefireProperties effectiveSystemProperties, int threadNumber,
+                            TestProvidingInputStream testProvidingInputStream )
         throws SurefireBooterForkException
     {
         File surefireProperties;
@@ -229,14 +336,18 @@ public class ForkStarter
             BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration );
 
             surefireProperties =
-                booterSerializer.serialize( providerProperties, providerConfiguration, startupConfiguration, testSet );
+                booterSerializer.serialize( providerProperties, providerConfiguration, startupConfiguration, testSet,
+                                            null != testProvidingInputStream );
 
             if ( effectiveSystemProperties != null )
             {
-                systPropsFile = SystemPropertyManager.writePropertiesFile( effectiveSystemProperties,
-                                                                           forkConfiguration.getTempDirectory(),
-                                                                           "surefire_" + systemPropertiesFileCounter++,
-                                                                           forkConfiguration.isDebug() );
+                SurefireProperties filteredProperties =
+                    AbstractSurefireMojo.createCopyAndReplaceThreadNumPlaceholder( effectiveSystemProperties,
+                                                                                   threadNumber );
+                systPropsFile =
+                    SystemPropertyManager.writePropertiesFile( filteredProperties, forkConfiguration.getTempDirectory(),
+                                                               "surefire_" + systemPropertiesFileCounter++,
+                                                               forkConfiguration.isDebug() );
             }
         }
         catch ( IOException e )
@@ -254,10 +365,15 @@ public class ForkStarter
         // Surefire-booter if !useSystemClassLoader
         Classpath bootClasspath = Classpath.join( bootClasspathConfiguration, additionlClassPathUrls );
 
-        @SuppressWarnings( "unchecked" ) Commandline cli =
+        @SuppressWarnings( "unchecked" ) ProcessAwareCommandline cli =
             forkConfiguration.createCommandLine( bootClasspath.getClassPath(),
                                                  startupConfiguration.getClassLoaderConfiguration(),
-                                                 startupConfiguration.isShadefire() );
+                                                 startupConfiguration.isShadefire(), threadNumber );
+
+        if ( testProvidingInputStream != null )
+        {
+            testProvidingInputStream.setFlushReceiverProvider( cli );
+        }
 
         cli.createArg().setFile( surefireProperties );
 
@@ -279,7 +395,8 @@ public class ForkStarter
         {
             final int timeout = forkedProcessTimeoutInSeconds > 0 ? forkedProcessTimeoutInSeconds : 0;
             final int result =
-                CommandLineUtils.executeCommandLine( cli, threadedStreamConsumer, threadedStreamConsumer, timeout );
+                CommandLineUtils.executeCommandLine( cli, testProvidingInputStream, threadedStreamConsumer,
+                                                     threadedStreamConsumer, timeout );
             if ( result != RunResult.SUCCESS )
             {
                 throw new SurefireBooterForkException( "Error occurred in starting fork, check output in log" );
@@ -299,17 +416,18 @@ public class ForkStarter
         finally
         {
             threadedStreamConsumer.close();
-            forkClient.close(runResult == RunResult.Timeout);
+            forkClient.close( runResult == RunResult.Timeout );
             if ( runResult == null )
             {
-                runResult = globalRunStatistics.getRunResult();
+                runResult = fileReporterFactory.getGlobalRunStatistics().getRunResult();
             }
         }
 
         return runResult;
     }
 
-    private Iterator getSuitesIterator()
+    @SuppressWarnings( "unchecked" )
+    private Iterator<Class<?>> getSuitesIterator()
         throws SurefireBooterForkException
     {
         try

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java
new file mode 100644
index 0000000..5269d7a
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java
@@ -0,0 +1,7 @@
+package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
+
+import java.io.IOException;
+
+public interface FlushReceiver {
+	void flush() throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java
new file mode 100644
index 0000000..f285a66
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java
@@ -0,0 +1,5 @@
+package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
+
+public interface FlushReceiverProvider {
+	FlushReceiver getFlushReceiver();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java
new file mode 100644
index 0000000..5182107
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java
@@ -0,0 +1,41 @@
+package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.maven.shared.utils.cli.CommandLineException;
+import org.apache.maven.shared.utils.cli.Commandline;
+
+
+
+public class ProcessAwareCommandline extends Commandline implements FlushReceiverProvider {
+	private final class OutputStreamFlushReceiver implements FlushReceiver {
+		private final OutputStream outputStream;
+
+		private OutputStreamFlushReceiver(OutputStream outputStream) {
+			this.outputStream = outputStream;
+		}
+
+		public void flush() throws IOException {
+			outputStream.flush();
+		}
+	}
+
+	private FlushReceiver flushReceiver;
+	
+	@Override
+	public Process execute() throws CommandLineException {
+		Process process = super.execute();
+
+		if (process.getOutputStream() != null) {
+			flushReceiver = new OutputStreamFlushReceiver(process.getOutputStream());
+		}
+		
+		return process;
+	}
+
+	public FlushReceiver getFlushReceiver() {
+		return flushReceiver;
+	}
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
new file mode 100644
index 0000000..a4e899d
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
@@ -0,0 +1,52 @@
+package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Queue;
+import java.util.concurrent.Semaphore;
+
+public class TestProvidingInputStream extends InputStream {
+	private final Queue<String> testItemQueue;
+	private byte[] currentBuffer;
+	private int currentPos;
+	private Semaphore semaphore = new Semaphore(0);
+	private FlushReceiverProvider flushReceiverProvider;
+
+	public TestProvidingInputStream(Queue<String> testItemQueue) {
+		this.testItemQueue = testItemQueue;
+	}
+
+	public void setFlushReceiverProvider(FlushReceiverProvider flushReceiverProvider) {
+		this.flushReceiverProvider = flushReceiverProvider;
+	}
+
+	@Override
+	public synchronized int read() throws IOException {
+		if (null == currentBuffer) {
+			if (null != flushReceiverProvider && null != flushReceiverProvider.getFlushReceiver()) {
+				flushReceiverProvider.getFlushReceiver().flush();
+			}
+			
+			semaphore.acquireUninterruptibly();
+
+			String currentElement = testItemQueue.poll();
+			if (null != currentElement) {
+				currentBuffer = currentElement.getBytes();
+				currentPos = 0;
+			} else {
+				return -1;
+			}
+		}
+
+		if (currentPos < currentBuffer.length) {
+			return (currentBuffer[currentPos++] & 0xff);
+		} else {
+			currentBuffer = null;
+			return ('\n' & 0xff);
+		}
+	}
+
+	public void provideNewTest() {
+		semaphore.release();
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
index 7559954..fa7d141 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
@@ -28,6 +28,8 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 import java.util.StringTokenizer;
+
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.shared.utils.cli.StreamConsumer;
 import org.apache.maven.surefire.booter.ForkingRunListener;
@@ -36,6 +38,7 @@ import org.apache.maven.surefire.report.ConsoleLogger;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.ReporterException;
+import org.apache.maven.surefire.report.ReporterFactory;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.StackTraceWriter;
 import org.apache.maven.surefire.util.NestedRuntimeException;
@@ -49,7 +52,9 @@ import org.apache.maven.surefire.util.internal.StringUtils;
 public class ForkClient
     implements StreamConsumer
 {
+
     private final DefaultReporterFactory providerReporterFactory;
+    private final TestProvidingInputStream testProvidingInputStream;
 
     private final Map<Integer, RunListener> testSetReporters =
         Collections.synchronizedMap( new HashMap<Integer, RunListener>() );
@@ -60,8 +65,14 @@ public class ForkClient
 
     public ForkClient( DefaultReporterFactory providerReporterFactory, Properties testVmSystemProperties )
     {
+        this(providerReporterFactory, testVmSystemProperties, null);
+    }
+    
+    public ForkClient( DefaultReporterFactory providerReporterFactory, Properties testVmSystemProperties, TestProvidingInputStream testProvidingInputStream )
+    {
         this.providerReporterFactory = providerReporterFactory;
         this.testVmSystemProperties = testVmSystemProperties;
+        this.testProvidingInputStream = testProvidingInputStream;
     }
 
     public void consumeLine( String s )
@@ -134,6 +145,12 @@ public class ForkClient
                 case ForkingRunListener.BOOTERCODE_CONSOLE:
                     getOrCreateConsoleLogger( channelNumber ).info( createConsoleMessage( remaining ) );
                     break;
+                case ForkingRunListener.BOOTERCODE_NEXT_TEST:
+                	if (null != testProvidingInputStream)
+                	{
+                		testProvidingInputStream.provideNewTest();
+                	}
+                	break;
                 case ForkingRunListener.BOOTERCODE_BYE:
                     saidGoodBye = true;
                     break;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
----------------------------------------------------------------------
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 b7e1eba..509929d 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
@@ -27,13 +27,16 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
 import org.apache.maven.surefire.booter.BooterDeserializer;
 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
 import org.apache.maven.surefire.booter.ClasspathConfiguration;
 import org.apache.maven.surefire.booter.PropertiesWrapper;
 import org.apache.maven.surefire.booter.ProviderConfiguration;
 import org.apache.maven.surefire.booter.StartupConfiguration;
-import org.apache.maven.surefire.booter.SystemPropertyManager;
 import org.apache.maven.surefire.booter.TypeEncodedValue;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
@@ -42,9 +45,6 @@ import org.apache.maven.surefire.testset.TestArtifactInfo;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.RunOrder;
 
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
 /**
  * Performs roundtrip testing of serialization/deserialization of the ProviderConfiguration
  *
@@ -55,7 +55,7 @@ public class BooterDeserializerProviderConfigurationTest
 {
 
     public static final TypeEncodedValue aTestTyped = new TypeEncodedValue( String.class.getName(), "aTest" );
-
+    
     private final String aUserRequestedTest = "aUserRequestedTest";
 
     private static ClassLoaderConfiguration getForkConfiguration()
@@ -79,7 +79,7 @@ public class BooterDeserializerProviderConfigurationTest
         ClassLoaderConfiguration forkConfiguration = getForkConfiguration();
         final StartupConfiguration testStartupConfiguration = getTestStartupConfiguration( forkConfiguration );
         ProviderConfiguration providerConfiguration = getReloadedProviderConfiguration();
-        ProviderConfiguration read = saveAndReload( providerConfiguration, testStartupConfiguration );
+        ProviderConfiguration read = saveAndReload( providerConfiguration, testStartupConfiguration, false );
 
         Assert.assertEquals( aDir, read.getBaseDir() );
         Assert.assertEquals( includes.get( 0 ), read.getIncludes().get( 0 ) );
@@ -95,10 +95,10 @@ public class BooterDeserializerProviderConfigurationTest
         DirectoryScannerParameters directoryScannerParameters = getDirectoryScannerParametersWithoutSpecificTests();
         ClassLoaderConfiguration forkConfiguration = getForkConfiguration();
 
-        ProviderConfiguration providerConfiguration = getTestProviderConfiguration( directoryScannerParameters );
+        ProviderConfiguration providerConfiguration = getTestProviderConfiguration( directoryScannerParameters, false );
 
         final StartupConfiguration testProviderConfiguration = getTestStartupConfiguration( forkConfiguration );
-        ProviderConfiguration reloaded = saveAndReload( providerConfiguration, testProviderConfiguration );
+        ProviderConfiguration reloaded = saveAndReload( providerConfiguration, testProviderConfiguration, false );
 
         assertTrue( reloaded.getReporterConfiguration().isTrimStackTrace().booleanValue() );
         assertNotNull( reloaded.getReporterConfiguration().getReportsDirectory() );
@@ -134,6 +134,15 @@ public class BooterDeserializerProviderConfigurationTest
         Assert.assertEquals( aTestTyped, reloaded.getTestForFork() );
 
     }
+    
+    public void testTestForForkWithMultipleFiles()
+            throws IOException
+        {
+            final ProviderConfiguration reloaded = getReloadedProviderConfigurationForReadFromInStream();
+            Assert.assertNull( reloaded.getTestForFork() );
+            Assert.assertTrue( reloaded.isReadTestsFromInStream() );
+
+        }
 
     public void testFailIfNoTests()
         throws IOException
@@ -143,14 +152,26 @@ public class BooterDeserializerProviderConfigurationTest
 
     }
 
+    private ProviderConfiguration getReloadedProviderConfigurationForReadFromInStream()
+            throws IOException
+   {
+    	return getReloadedProviderConfiguration(true);
+   }
+    
     private ProviderConfiguration getReloadedProviderConfiguration()
+            throws IOException
+   {
+    	return getReloadedProviderConfiguration(false);
+   }
+    
+    private ProviderConfiguration getReloadedProviderConfiguration(boolean readTestsFromInStream)
         throws IOException
     {
         DirectoryScannerParameters directoryScannerParameters = getDirectoryScannerParametersWithoutSpecificTests();
         ClassLoaderConfiguration forkConfiguration = getForkConfiguration();
-        ProviderConfiguration booterConfiguration = getTestProviderConfiguration( directoryScannerParameters );
+        ProviderConfiguration booterConfiguration = getTestProviderConfiguration( directoryScannerParameters, readTestsFromInStream );
         final StartupConfiguration testProviderConfiguration = getTestStartupConfiguration( forkConfiguration );
-        return saveAndReload( booterConfiguration, testProviderConfiguration );
+        return saveAndReload( booterConfiguration, testProviderConfiguration, readTestsFromInStream );
     }
 
     private DirectoryScannerParameters getDirectoryScannerParametersWithoutSpecificTests()
@@ -168,19 +189,27 @@ public class BooterDeserializerProviderConfigurationTest
     }
 
     private ProviderConfiguration saveAndReload( ProviderConfiguration booterConfiguration,
-                                                 StartupConfiguration testProviderConfiguration )
+                                                 StartupConfiguration testProviderConfiguration, boolean readTestsFromInStream )
         throws IOException
     {
         final ForkConfiguration forkConfiguration = ForkConfigurationTest.getForkConfiguration( null, null );
         PropertiesWrapper props = new PropertiesWrapper( new Properties());
         BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration );
-        String aTest = "aTest";
-        final File propsTest = booterSerializer.serialize( props, booterConfiguration, testProviderConfiguration, aTest);
+        Object test;
+        if (readTestsFromInStream) 
+        {	
+        	test = null;
+        }
+        else
+        {
+        	test = "aTest";
+        }
+        final File propsTest = booterSerializer.serialize( props, booterConfiguration, testProviderConfiguration, test, readTestsFromInStream);
         BooterDeserializer booterDeserializer = new BooterDeserializer( new FileInputStream( propsTest ) );
         return booterDeserializer.deserialize();
     }
 
-    private ProviderConfiguration getTestProviderConfiguration( DirectoryScannerParameters directoryScannerParameters )
+    private ProviderConfiguration getTestProviderConfiguration( DirectoryScannerParameters directoryScannerParameters, boolean readTestsFromInStream )
     {
 
         File cwd = new File( "." );
@@ -192,7 +221,7 @@ public class BooterDeserializerProviderConfigurationTest
         RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, null );
         return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, true, reporterConfiguration,
                                           new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new Properties(),
-                                          aTestTyped );
+                                          aTestTyped, readTestsFromInStream );
     }
 
     private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
----------------------------------------------------------------------
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 bc9085a..d72621d 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
@@ -25,6 +25,9 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Properties;
+
+import junit.framework.TestCase;
+
 import org.apache.maven.surefire.booter.BooterDeserializer;
 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
 import org.apache.maven.surefire.booter.Classpath;
@@ -32,7 +35,6 @@ import org.apache.maven.surefire.booter.ClasspathConfiguration;
 import org.apache.maven.surefire.booter.PropertiesWrapper;
 import org.apache.maven.surefire.booter.ProviderConfiguration;
 import org.apache.maven.surefire.booter.StartupConfiguration;
-import org.apache.maven.surefire.booter.SystemPropertyManager;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
 import org.apache.maven.surefire.testset.RunOrderParameters;
@@ -40,8 +42,6 @@ import org.apache.maven.surefire.testset.TestArtifactInfo;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.RunOrder;
 
-import junit.framework.TestCase;
-
 /**
  * Performs roundtrip testing of serialization/deserialization of The StartupConfiguration
  *
@@ -130,7 +130,7 @@ public class BooterDeserializerStartupConfigurationTest
         PropertiesWrapper props = new PropertiesWrapper( new Properties());
         BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration );
         String aTest = "aTest";
-        final File propsTest  = booterSerializer.serialize( props, getProviderConfiguration(), startupConfiguration, aTest);
+        final File propsTest  = booterSerializer.serialize( props, getProviderConfiguration(), startupConfiguration, aTest, false );
         BooterDeserializer booterDeserializer = new BooterDeserializer( new FileInputStream( propsTest ) );
         return booterDeserializer.getProviderConfiguration();
     }
@@ -152,7 +152,7 @@ public class BooterDeserializerStartupConfigurationTest
         RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, null );
         return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, true, reporterConfiguration,
                                           new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new Properties(),
-                                          BooterDeserializerProviderConfigurationTest.aTestTyped );
+                                          BooterDeserializerProviderConfigurationTest.aTestTyped, true );
     }
 
     private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
----------------------------------------------------------------------
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 ce44f23..6ec931e 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
@@ -41,7 +41,7 @@ public class ForkConfigurationTest
         File cpElement = getTempClasspathFile();
 
         Commandline cli =
-            config.createCommandLine( Collections.singletonList( cpElement.getAbsolutePath() ), true, false );
+            config.createCommandLine( Collections.singletonList( cpElement.getAbsolutePath() ), true, false, 1 );
 
         String line = StringUtils.join( cli.getCommandline(), " " );
         assertTrue( line.contains( "-jar" ) );
@@ -56,7 +56,7 @@ public class ForkConfigurationTest
 
         final Commandline commandLine =
             forkConfiguration.createCommandLine( Collections.singletonList( cpElement.getAbsolutePath() ), false,
-                                                 false );
+                                                 false, 1 );
         assertTrue( commandLine.toString().contains( "abc def" ) );
     }
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 11776ba..2eb2c3c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -222,7 +222,7 @@
       <dependency>
         <groupId>org.apache.maven.shared</groupId>
         <artifactId>maven-shared-utils</artifactId>
-        <version>0.1</version>
+        <version>0.2-SNAPSHOT</version>
       </dependency>
       <dependency>
         <groupId>jmock</groupId>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
index b3283e3..e5c8ac3 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
@@ -22,6 +22,7 @@ package org.apache.maven.surefire.booter;
 import java.io.PrintStream;
 import java.util.Enumeration;
 import java.util.Properties;
+
 import org.apache.maven.surefire.report.ConsoleLogger;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReportEntry;
@@ -75,6 +76,8 @@ public class ForkingRunListener
 
     public static final byte BOOTERCODE_SYSPROPS = (byte) 'I';
 
+    public static final byte BOOTERCODE_NEXT_TEST = (byte) 'N';
+
     public static final byte BOOTERCODE_BYE = (byte) 'Z';
 
     private final PrintStream target;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java
new file mode 100644
index 0000000..dabe1ae
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java
@@ -0,0 +1,135 @@
+/**
+ * 
+ */
+package org.apache.maven.surefire.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.surefire.booter.ForkingRunListener;
+
+/**
+ * A variant of TestsToRun that is provided with test class names asynchronously
+ * from an {@link InputStream} (e.g. {@code System.in}). The method
+ * {@link #iterator()} returns an Iterator that blocks on calls to
+ * {@link Iterator#hasNext()} until new classes are available, or no more
+ * classes will be available.
+ * <p/>
+ * The methods {@link #getLocatedClasses()} and {@link #size()} will throw an
+ * {@link UnsupportedOperationException}.
+ * 
+ * @author Andreas Gudian
+ * 
+ */
+public class LazyTestsToRun extends TestsToRun {
+	private List workQueue = new ArrayList();
+	private BufferedReader inputReader;
+	private boolean streamClosed = false;
+	private ClassLoader testClassLoader;
+	private PrintStream originalOutStream;
+
+	public LazyTestsToRun(InputStream testSource, ClassLoader testClassLoader, PrintStream originalOutStream) {
+		super(Collections.emptyList());
+
+		this.testClassLoader = testClassLoader;
+		this.originalOutStream = originalOutStream;
+
+		inputReader = new BufferedReader(new InputStreamReader(testSource));
+	}
+
+	protected void addWorkItem(String className) {
+		synchronized (workQueue) {
+			workQueue.add(ReflectionUtils.loadClass(testClassLoader, className));
+		}
+	}
+
+	protected void requestNextTest() {
+		StringBuffer sb = new StringBuffer();
+		sb.append((char) ForkingRunListener.BOOTERCODE_NEXT_TEST).append(",0,want more!\n");
+		originalOutStream.print(sb.toString());
+	}
+
+	private class BlockingIterator implements Iterator {
+		private int lastPos = -1;
+
+		public boolean hasNext() {
+			int nextPos = lastPos + 1;
+			synchronized (workQueue) {
+				if (workQueue.size() > nextPos) {
+					return true;
+				} else {
+					if (needsToWaitForInput(nextPos)) {
+						requestNextTest();
+
+						String nextClassName;
+						try {
+							nextClassName = inputReader.readLine();
+						} catch (IOException e) {
+							streamClosed = true;
+							return false;
+						}
+
+						if (null == nextClassName) {
+							streamClosed = true;
+						} else {
+							addWorkItem(nextClassName);
+						}
+					}
+
+					return (workQueue.size() > nextPos);
+				}
+			}
+		}
+
+		private boolean needsToWaitForInput(int nextPos) {
+			return workQueue.size() == nextPos && !streamClosed;
+		}
+
+		public Object next() {
+			synchronized (workQueue) {
+				return workQueue.get(++lastPos);
+			}
+		}
+
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+	}
+
+	public Iterator iterator() {
+		return new BlockingIterator();
+	}
+
+	/**
+	 * Unsupported. Use {@link #iterator()} instead.
+	 */
+	public int size() {
+		throw new UnsupportedOperationException("use method iterator()");
+	}
+
+	/**
+	 * Unsupported. Use {@link #iterator()} instead.
+	 */
+	public Class[] getLocatedClasses() {
+		throw new UnsupportedOperationException("use method iterator()");
+	}
+
+	public String toString() {
+		StringBuffer sb = new StringBuffer("LazyTestsToRun ");
+		synchronized (workQueue) {
+			sb.append("(more items expected: ").append(!streamClosed).append("): ");
+			sb.append(workQueue);
+		}
+
+		return sb.toString();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
index 375bf68..430ea9c 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
@@ -46,4 +46,5 @@ public interface BooterConstants
     String TEST_SUITE_XML_FILES = "testSuiteXmlFiles";
     String PROVIDER_CONFIGURATION = "providerConfiguration";
     String FORKTESTSET = "forkTestSet";
+	String FORKTESTSET_PREFER_TESTS_FROM_IN_STREAM = "preferTestsFromInStream";
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
index af03c1c..85e4c93 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
@@ -61,7 +61,10 @@ public class BooterDeserializer
         final File reportsDirectory = new File( properties.getProperty( REPORTSDIRECTORY ) );
         final String testNgVersion = properties.getProperty( TESTARTIFACT_VERSION );
         final String testArtifactClassifier = properties.getProperty( TESTARTIFACT_CLASSIFIER );
+        
         final TypeEncodedValue typeEncodedTestForFork = properties.getTypeEncodedValue( FORKTESTSET );
+        final boolean preferTestsFromInStream = properties.getBooleanProperty( FORKTESTSET_PREFER_TESTS_FROM_IN_STREAM );
+        
         final String requestedTest = properties.getProperty( REQUESTEDTEST );
         final String requestedTestMethod = properties.getProperty( REQUESTEDTESTMETHOD );
         final File sourceDirectory = properties.getFileProperty( SOURCE_DIRECTORY );
@@ -90,7 +93,7 @@ public class BooterDeserializer
 
         return new ProviderConfiguration( dirScannerParams, runOrderParameters,
                                           properties.getBooleanProperty( FAILIFNOTESTS ), reporterConfiguration, testNg,
-                                          testSuiteDefinition, properties.getProperties(), typeEncodedTestForFork );
+                                          testSuiteDefinition, properties.getProperties(), typeEncodedTestForFork, preferTestsFromInStream );
     }
 
     public StartupConfiguration getProviderConfiguration()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
index 938755c..2e8f812 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
@@ -23,7 +23,9 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
 import java.io.PrintStream;
+
 import org.apache.maven.surefire.suite.RunResult;
+import org.apache.maven.surefire.util.LazyTestsToRun;
 
 /**
  * The part of the booter that is unique to a forked vm.
@@ -64,6 +66,7 @@ public class ForkedBooter
             final StartupConfiguration startupConfiguration = booterDeserializer.getProviderConfiguration();
 
             TypeEncodedValue forkedTestSet = providerConfiguration.getTestForFork();
+            boolean readTestsFromInputStream = providerConfiguration.isReadTestsFromInStream();
 
             final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
             final ClassLoader testClassLoader = classpathConfiguration.createForkingTestClassLoader(
@@ -71,7 +74,17 @@ public class ForkedBooter
 
             startupConfiguration.writeSurefireTestClasspathProperty();
 
-            Object testSet = forkedTestSet != null ? forkedTestSet.getDecodedValue( testClassLoader ) : null;
+            Object testSet;
+            if (forkedTestSet != null) {
+                 testSet = forkedTestSet.getDecodedValue( testClassLoader );
+            } 
+            else if (readTestsFromInputStream) {
+                testSet = new LazyTestsToRun(System.in, testClassLoader, originalOut);
+            }
+            else {
+                testSet = null;
+            }
+
             runSuitesInProcess( testSet, testClassLoader, startupConfiguration, providerConfiguration, originalOut );
             // Say bye.
             originalOut.println( "Z,0,BYE!" );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
index bc1dc7f..23535ad 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
@@ -64,12 +64,14 @@ public class ProviderConfiguration
     private final boolean failIfNoTests;
 
     private final TypeEncodedValue forkTestSet;
+    
+    private final boolean readTestsFromInStream;
 
     public ProviderConfiguration( DirectoryScannerParameters directoryScannerParameters,
                                   RunOrderParameters runOrderParameters, boolean failIfNoTests,
                                   ReporterConfiguration reporterConfiguration, TestArtifactInfo testArtifact,
                                   TestRequest testSuiteDefinition, Properties providerProperties,
-                                  TypeEncodedValue typeEncodedTestSet )
+                                  TypeEncodedValue typeEncodedTestSet, boolean readTestsFromInStream)
     {
         this.runOrderParameters = runOrderParameters;
         this.providerProperties = providerProperties;
@@ -79,6 +81,7 @@ public class ProviderConfiguration
         this.dirScannerParams = directoryScannerParameters;
         this.failIfNoTests = failIfNoTests;
         this.forkTestSet = typeEncodedTestSet;
+        this.readTestsFromInStream = readTestsFromInStream;
     }
 
 
@@ -133,9 +136,14 @@ public class ProviderConfiguration
     {
         return forkTestSet;
     }
-
+    
     public RunOrderParameters getRunOrderParameters()
     {
         return runOrderParameters;
     }
+
+
+	public boolean isReadTestsFromInStream() {
+		return readTestsFromInStream;
+	}
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/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 158ad85..8653106 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,10 @@ package org.apache.maven.surefire.its;
  * under the License.
  */
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
 import org.apache.maven.surefire.its.fixture.OutputValidator;
 import org.apache.maven.surefire.its.fixture.SurefireIntegrationTestCase;
 import org.apache.maven.surefire.its.fixture.SurefireLauncher;
@@ -55,8 +59,25 @@ public class ForkModeIT
         String[] pids = doTest( unpack( getProject() ).forkMode( "none" ) );
         assertSamePids( pids );
     }
+    
+    public void testForkModeOncePerThreadSingleThread()
+    {
+        String[] pids = doTest( unpack( getProject() ).forkOncePerThread().threadCount(1) );
+        assertSamePids( pids );
+    }
+
+    public void testForkModeOncePerThreadTwoThreads()
+    {
+        String[] pids = doTest( unpack( getProject() ).forkOncePerThread().threadCount(2) );
+        assertDifferentPids( pids, 2 );
+    }
+    
+    private void assertDifferentPids(String[] pids, int numOfDifferentPids) {
+		Set<String> pidSet = new HashSet<String>(Arrays.asList(pids));
+		assertEquals( "number of different pids is not as expected", numOfDifferentPids, pidSet.size() );
+	}
 
-    public void testForkModeOnce()
+	public void testForkModeOnce()
     {
         String[] pids = doTest( unpack( getProject() ).forkOnce() );
         // DGF It would be nice to assert that "once" was different
@@ -92,6 +113,7 @@ public class ForkModeIT
 
     private String[] doTest( SurefireLauncher forkMode )
     {
+    	forkMode.addD( "testProperty", "testValue_${surefire.threadNumber}" );
         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/9c6ce9be/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 d6fc2e8..552e8b4 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
@@ -384,6 +384,16 @@ public class SurefireLauncher
         return forkMode( "perthread" );
     }
 
+    public SurefireLauncher forkOncePerThread()
+    {
+        return forkMode( "onceperthread" );
+    }
+    
+    public SurefireLauncher threadCount(int threadCount)
+    {
+        return addGoal( "-DthreadCount=" + threadCount );
+    }
+    
     public SurefireLauncher forkMode( String forkMode )
     {
         return addGoal( "-DforkMode=" + forkMode );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire907PerThreadWithoutThreadCountIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire907PerThreadWithoutThreadCountIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire907PerThreadWithoutThreadCountIT.java
index 43ecef4..577b8a5 100755
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire907PerThreadWithoutThreadCountIT.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire907PerThreadWithoutThreadCountIT.java
@@ -19,7 +19,6 @@ package org.apache.maven.surefire.its.jiras;
  */
 import org.apache.maven.surefire.its.fixture.OutputValidator;
 import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
-import org.apache.maven.surefire.its.fixture.SurefireLauncher;
 
 import org.junit.Test;
 
@@ -30,7 +29,7 @@ public class Surefire907PerThreadWithoutThreadCountIT
     public void categoryAB()
     {
         OutputValidator validator = unpack("fork-mode").forkPerThread().executeTestWithFailure();
-        validator.verifyTextInLog( "Fork mode perthread requires a thread count" );
+        validator.verifyTextInLog( "Fork modes perthread and onceperthread require a thread count" );
     }
 
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-integration-tests/src/test/resources/fork-mode-testng/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/fork-mode-testng/pom.xml b/surefire-integration-tests/src/test/resources/fork-mode-testng/pom.xml
index 62593c9..25b66f0 100644
--- a/surefire-integration-tests/src/test/resources/fork-mode-testng/pom.xml
+++ b/surefire-integration-tests/src/test/resources/fork-mode-testng/pom.xml
@@ -26,6 +26,7 @@
         <version>${surefire.version}</version>
         <configuration>
           <forkMode>${forkMode}</forkMode>
+          <threadCount>${threadCount}</threadCount>
         </configuration>
       </plugin>
     </plugins>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-integration-tests/src/test/resources/fork-mode-testng/src/test/java/forkMode/Test1.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/fork-mode-testng/src/test/java/forkMode/Test1.java b/surefire-integration-tests/src/test/resources/fork-mode-testng/src/test/java/forkMode/Test1.java
index ef70d3e..408550b 100644
--- a/surefire-integration-tests/src/test/resources/fork-mode-testng/src/test/java/forkMode/Test1.java
+++ b/surefire-integration-tests/src/test/resources/fork-mode-testng/src/test/java/forkMode/Test1.java
@@ -4,12 +4,15 @@ import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
+import java.util.Random;
 
 import org.testng.annotations.Test;
 
 public class Test1
 {
 
+	private static final Random RANDOM = new Random();
+	
     @Test
     public void test1()
         throws IOException
@@ -32,6 +35,8 @@ public class Test1
         // In fact, it usually contains the pid and the local host name!
         String pid = ManagementFactory.getRuntimeMXBean().getName();
         fw.write( pid );
+        fw.write( " " );
+        fw.write( System.getProperty( "testProperty", String.valueOf( RANDOM.nextLong() ) ) );
         fw.flush();
         fw.close();
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-integration-tests/src/test/resources/fork-mode/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/fork-mode/pom.xml b/surefire-integration-tests/src/test/resources/fork-mode/pom.xml
index 416fda9..aa37e2c 100644
--- a/surefire-integration-tests/src/test/resources/fork-mode/pom.xml
+++ b/surefire-integration-tests/src/test/resources/fork-mode/pom.xml
@@ -42,6 +42,7 @@
         <version>${surefire.version}</version>
         <configuration>
           <forkMode>${forkMode}</forkMode>
+          <threadCount>${threadCount}</threadCount>
         </configuration>
       </plugin>
     </plugins>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-integration-tests/src/test/resources/fork-mode/src/test/java/forkMode/Test1.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/fork-mode/src/test/java/forkMode/Test1.java b/surefire-integration-tests/src/test/resources/fork-mode/src/test/java/forkMode/Test1.java
index 05bac14..35f746e 100644
--- a/surefire-integration-tests/src/test/resources/fork-mode/src/test/java/forkMode/Test1.java
+++ b/surefire-integration-tests/src/test/resources/fork-mode/src/test/java/forkMode/Test1.java
@@ -4,6 +4,7 @@ import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
+import java.util.Random;
 
 import junit.framework.TestCase;
 
@@ -11,7 +12,9 @@ public class Test1
     extends TestCase
 {
 
-    public void test1()
+	private static final Random RANDOM = new Random();
+
+	public void test1()
         throws IOException
     {
         dumpPidFile( this );
@@ -32,6 +35,8 @@ public class Test1
         // In fact, it usually contains the pid and the local host name!
         String pid = ManagementFactory.getRuntimeMXBean().getName();
         fw.write( pid );
+        fw.write( " " );
+        fw.write( System.getProperty( "testProperty", String.valueOf( RANDOM.nextLong() ) ) );
         fw.flush();
         fw.close();
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java
index ff39099..bbff4da 100644
--- a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java
+++ b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java
@@ -76,7 +76,17 @@ public class JUnit3Provider
     {
         if ( testsToRun == null )
         {
-            testsToRun = forkTestSet == null ? scanClassPath() : TestsToRun.fromClass( (Class<?>) forkTestSet );
+            if (forkTestSet instanceof TestsToRun)
+            {
+                testsToRun = (TestsToRun) forkTestSet;
+            }
+            else if (forkTestSet instanceof Class)
+            {
+                testsToRun = TestsToRun.fromClass( (Class<?>) forkTestSet );
+            } else
+            {
+                testsToRun = scanClassPath();
+            }
         }
 
         ReporterFactory reporterFactory = providerParameters.getReporterFactory();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
index 28f127f..5354685 100644
--- a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
+++ b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
@@ -89,9 +89,19 @@ public class JUnit4Provider
     {
         if ( testsToRun == null )
         {
-            testsToRun = forkTestSet == null ? scanClassPath() : TestsToRun.fromClass( (Class<?>) forkTestSet );
+            if (forkTestSet instanceof TestsToRun)
+            {
+                testsToRun = (TestsToRun) forkTestSet;
+            }
+            else if (forkTestSet instanceof Class)
+            {
+                testsToRun = TestsToRun.fromClass( (Class) forkTestSet );
+            } else
+            {
+                testsToRun = scanClassPath();
+            }
         }
-
+        
         upgradeCheck();
 
         final ReporterFactory reporterFactory = providerParameters.getReporterFactory();
@@ -107,9 +117,9 @@ public class JUnit4Provider
 
         runNotifer.fireTestRunStarted( null );
 
-        for ( Class<?> clazz : testsToRun.getLocatedClasses() )
+        for ( Iterator<Class<?>> iter = testsToRun.iterator(); iter.hasNext(); )
         {
-            executeTestSet( clazz, reporter, runNotifer );
+            executeTestSet( iter.next(), reporter, runNotifer );
         }
 
         runNotifer.fireTestRunFinished( result );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
index 01b7c5d..afe6888 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
@@ -116,12 +116,17 @@ public class JUnitCoreProvider
 
         if ( testsToRun == null )
         {
-            testsToRun = forkTestSet == null ? getSuitesAsList( filter ) : TestsToRun.fromClass( (Class) forkTestSet );
-        }
-
-        if ( testsToRun.size() == 0 )
-        {
-            filter = null;
+            if (forkTestSet instanceof TestsToRun)
+            {
+                testsToRun = (TestsToRun) forkTestSet;
+            }
+            else if (forkTestSet instanceof Class)
+            {
+                testsToRun = TestsToRun.fromClass( (Class) forkTestSet );
+            } else
+            {
+                testsToRun = getSuitesAsList( filter );
+            }
         }
 
         final Map<String, TestSet> testSetMap = new ConcurrentHashMap<String, TestSet>();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9c6ce9be/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java
index 57350b9..bfd4dff 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java
@@ -19,12 +19,13 @@ package org.apache.maven.surefire.junitcore;
  * under the License.
  */
 
+import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
+
 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.apache.maven.surefire.util.TestsToRun;
-
 import org.junit.runner.Computer;
 import org.junit.runner.JUnitCore;
 import org.junit.runner.Request;
@@ -51,16 +52,20 @@ class JUnitCoreWrapper
             junitCore.addListener( runListener );
         }
 
-        Request req = Request.classes( computer, testsToRun.getLocatedClasses() );
-        if ( filter != null )
-        {
-            req = req.filterWith( filter );
-        }
-
         try
         {
-            final Result run = junitCore.run( req );
-            JUnit4RunListener.rethrowAnyTestMechanismFailures( run );
+	        Iterator classIter = testsToRun.iterator();
+	        while (classIter.hasNext()) 
+	        {
+		        Request req = Request.classes( computer, new Class[]{ (Class) classIter.next() });
+		        if ( filter != null )
+		        {
+		            req = req.filterWith( filter );
+		        }
+	
+	            final Result run = junitCore.run( req );
+	            JUnit4RunListener.rethrowAnyTestMechanismFailures( run );
+        	}
         }
         finally
         {