[SUREFIRE-580] Allow "fail fast" or stop running on first failure


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

Branch: refs/heads/master
Commit: 2a944f0687ee849003403e401d69750af856fc47
Parents: 8c34806
Author: Tibor17 <tibo...@lycos.com>
Authored: Thu Sep 3 01:33:26 2015 +0200
Committer: Tibor17 <tibo...@lycos.com>
Committed: Sun Sep 6 22:57:59 2015 +0200

----------------------------------------------------------------------
 .../plugin/failsafe/IntegrationTestMojo.java    |  16 +
 .../plugin/surefire/AbstractSurefireMojo.java   |  48 +-
 .../surefire/SurefireExecutionParameters.java   |   2 +
 .../plugin/surefire/SurefireProperties.java     |  57 +--
 .../surefire/booterclient/BooterSerializer.java |   1 +
 .../surefire/booterclient/ForkStarter.java      | 219 +++++----
 .../surefire/booterclient/MockReporter.java     | 160 -------
 .../lazytestprovider/AbstractCommandStream.java | 122 +++++
 .../AbstractForkInputStream.java                |  61 +++
 .../lazytestprovider/NotifiableTestStream.java  |  14 +
 .../lazytestprovider/TestLessInputStream.java   | 453 +++++++++++++++++++
 .../TestProvidingInputStream.java               | 169 +++----
 .../booterclient/output/ForkClient.java         |   7 +
 .../surefire/report/TestSetRunListener.java     |   4 +
 ...erDeserializerProviderConfigurationTest.java |   2 +-
 ...terDeserializerStartupConfigurationTest.java |   2 +-
 .../surefire/booterclient/MockReporter.java     | 161 +++++++
 .../TestLessInputStreamBuilderTest.java         | 128 ++++++
 .../TestProvidingInputStreamTest.java           |  15 +-
 .../apache/maven/surefire/JUnit4SuiteTest.java  |   2 +
 .../maven/plugin/surefire/SurefirePlugin.java   |  17 +-
 .../site/apt/examples/skip-after-failure.apt.vm |  58 +++
 maven-surefire-plugin/src/site/site.xml         |   1 +
 .../surefire/booter/BaseProviderFactory.java    |  20 +-
 .../apache/maven/surefire/booter/Command.java   |  38 +-
 .../maven/surefire/booter/FailFastAware.java    |  31 ++
 .../surefire/booter/ForkingReporterFactory.java |   2 +-
 .../surefire/booter/ForkingRunListener.java     |  17 +-
 .../surefire/booter/MasterProcessCommand.java   |  12 +-
 .../surefire/booter/MasterProcessListener.java  |  28 ++
 .../surefire/booter/MasterProcessReader.java    | 421 +++++++++++++++++
 .../surefire/booter/SurefireReflector.java      |   5 +
 .../surefire/booter/TwoPropertiesWrapper.java   |  50 ++
 .../providerapi/ProviderParameters.java         |  11 +
 .../maven/surefire/report/RunListener.java      |   8 +-
 .../surefire/report/SimpleReportEntry.java      |   5 +
 .../booter/MasterProcessCommandTest.java        |   3 +-
 .../maven/surefire/report/MockReporter.java     |   9 +
 .../maven/surefire/booter/BooterConstants.java  |   1 +
 .../surefire/booter/BooterDeserializer.java     |   8 +-
 .../maven/surefire/booter/ForkedBooter.java     |   8 +-
 .../surefire/booter/MasterProcessListener.java  |  28 --
 .../surefire/booter/MasterProcessReader.java    | 257 -----------
 .../surefire/booter/ProviderConfiguration.java  |  15 +-
 .../maven/surefire/booter/ProviderFactory.java  |  13 +-
 .../booter/MasterProcessReaderTest.java         |  67 ++-
 .../surefire/common/junit4/JUnit4Reflector.java | 103 ++++-
 .../common/junit4/JUnit4RunListener.java        |  64 +--
 .../maven/surefire/common/junit4/Notifier.java  | 106 +++++
 .../maven/surefire/junit4/MockReporter.java     |  11 +-
 .../common/junit4/JUnit4Reflector40Test.java    |  28 ++
 .../maven/surefire/junit/JUnitTestSetTest.java  |   9 +
 .../surefire/junit4/JUnit4FailFastListener.java |  48 ++
 .../maven/surefire/junit4/JUnit4Provider.java   | 193 +++++---
 .../junitcore/ConcurrentRunListener.java        |  19 +-
 .../surefire/junitcore/FilteringRequest.java    |  54 +++
 .../junitcore/JUnit47FailFastListener.java      |  65 +++
 .../maven/surefire/junitcore/JUnitCore.java     |  90 ++++
 .../surefire/junitcore/JUnitCoreProvider.java   | 125 +++--
 .../surefire/junitcore/JUnitCoreWrapper.java    | 159 +++++--
 .../maven/surefire/junitcore/Stoppable.java     |  32 ++
 .../junitcore/JUnit4Reflector481Test.java       |  52 +++
 .../surefire/junitcore/Surefire746Test.java     |   4 +-
 .../surefire-testng-utils/pom.xml               |   2 +-
 .../testng/utils/FailFastEventsSingleton.java   |  53 +++
 .../surefire/testng/utils/FailFastListener.java |  77 ++++
 .../surefire/testng/utils/FailFastNotifier.java |  50 ++
 .../maven/surefire/testng/utils/Stoppable.java  |  32 ++
 .../testng/TestNGDirectoryTestSuite.java        |  13 +-
 .../maven/surefire/testng/TestNGExecutor.java   |  34 +-
 .../maven/surefire/testng/TestNGProvider.java   | 114 +++--
 .../surefire/testng/TestNGXmlTestSuite.java     |  12 +-
 72 files changed, 3337 insertions(+), 988 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
----------------------------------------------------------------------
diff --git 
a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
 
b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
index 2c2339a..eb315ce 100644
--- 
a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
+++ 
b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
@@ -301,6 +301,17 @@ public class IntegrationTestMojo
     @Parameter( property = "failsafe.excludesFile" )
     private File excludesFile;
 
+    /**
+     * Set to "true" to skip remaining tests after first test failure appeared.
+     * Due to race conditions in parallel/forked execution this may not be 
fully guaranteed.<br/>
+     * Enable with system property -Dfailsafe.skipAfterFailureCount=1 or any 
number greater than zero.
+     * Defaults to "0".
+     *
+     * @since 2.19
+     */
+    @Parameter( property = "failsafe.skipAfterFailureCount", defaultValue = 
"0" )
+    private int skipAfterFailureCount;
+
     protected int getRerunFailingTestsCount()
     {
         return rerunFailingTestsCount;
@@ -594,6 +605,11 @@ public class IntegrationTestMojo
         this.failIfNoSpecifiedTests = failIfNoSpecifiedTests;
     }
 
+    public int getSkipAfterFailureCount()
+    {
+        return skipAfterFailureCount;
+    }
+
     @Override
     public List<String> getIncludes()
     {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 4af79f2..f847604 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
@@ -683,6 +683,29 @@ public abstract class AbstractSurefireMojo
 
     protected abstract int getRerunFailingTestsCount();
 
+    public abstract List<String> getIncludes();
+
+    public abstract File getIncludesFile();
+
+    public abstract void setIncludes( List<String> includes );
+
+    public abstract File getExcludesFile();
+
+    public abstract File[] getSuiteXmlFiles();
+
+    public abstract void setSuiteXmlFiles( File[] suiteXmlFiles );
+
+    public abstract String getRunOrder();
+
+    public abstract void setRunOrder( String runOrder );
+
+    protected abstract void handleSummary( RunResult summary, Exception 
firstForkException )
+        throws MojoExecutionException, MojoFailureException;
+
+    protected abstract String[] getDefaultIncludes();
+
+    protected abstract boolean isSkipExecution();
+
     private SurefireDependencyResolver dependencyResolver;
 
     private TestListResolver specificTests;
@@ -807,8 +830,6 @@ public abstract class AbstractSurefireMojo
         return true;
     }
 
-    protected abstract boolean isSkipExecution();
-
     private void executeAfterPreconditionsChecked( DefaultScanResult 
scanResult )
         throws MojoExecutionException, MojoFailureException
     {
@@ -1031,9 +1052,6 @@ public abstract class AbstractSurefireMojo
         }
     }
 
-    protected abstract void handleSummary( RunResult summary, Exception 
firstForkException )
-        throws MojoExecutionException, MojoFailureException;
-
     protected void logReportsDirectory()
     {
         logDebugOrCliShowErrors(
@@ -1448,7 +1466,7 @@ public abstract class AbstractSurefireMojo
                                           reporterConfiguration,
                                           testNg, // Not really used in 
provider. Limited to de/serializer.
                                           testSuiteDefinition, 
providerProperties, null,
-                                          false, cli );
+                                          false, cli, 
getSkipAfterFailureCount() );
     }
 
     private static Map<String, String> toStringProperties( Properties 
properties )
@@ -2032,8 +2050,6 @@ public abstract class AbstractSurefireMojo
             : new ClassLoaderConfiguration( false, false );
     }
 
-    protected abstract String[] getDefaultIncludes();
-
     /**
      * Generate the test classpath.
      *
@@ -2591,19 +2607,11 @@ public abstract class AbstractSurefireMojo
         }
     }
 
-    public abstract List<String> getIncludes();
-
-    public abstract File getIncludesFile();
-
-    public abstract void setIncludes( List<String> includes );
-
     public List<String> getExcludes()
     {
         return excludes;
     }
 
-    public abstract File getExcludesFile();
-
     public void setExcludes( List<String> excludes )
     {
         this.excludes = excludes;
@@ -2805,10 +2813,6 @@ public abstract class AbstractSurefireMojo
         this.excludedGroups = excludedGroups;
     }
 
-    public abstract File[] getSuiteXmlFiles();
-
-    public abstract void setSuiteXmlFiles( File[] suiteXmlFiles );
-
     public String getJunitArtifactName()
     {
         return junitArtifactName;
@@ -3046,10 +3050,6 @@ public abstract class AbstractSurefireMojo
         return parallelMavenExecution != null && parallelMavenExecution;
     }
 
-    public abstract String getRunOrder();
-
-    public abstract void setRunOrder( String runOrder );
-
     public String[] getDependenciesToScan()
     {
         return dependenciesToScan;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
index 65c4062..6bd09ff 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
@@ -118,4 +118,6 @@ public interface SurefireExecutionParameters
     Boolean getFailIfNoSpecifiedTests();
 
     void setFailIfNoSpecifiedTests( boolean failIfNoSpecifiedTests );
+
+    int getSkipAfterFailureCount();
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java
index 472c51a..d41d4d7 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java
@@ -127,7 +127,6 @@ public class SurefireProperties
         return result;
     }
 
-
     public void copyToSystemProperties()
     {
 
@@ -197,34 +196,33 @@ public class SurefireProperties
 
     public void addList( List<?> items, String propertyPrefix )
     {
-        if ( items == null || items.isEmpty() )
-        {
-            return;
-        }
-        int i = 0;
-        for ( Object item : items )
+        if ( items != null && !items.isEmpty() )
         {
-            if ( item == null )
-            {
-                throw new NullPointerException( propertyPrefix + i + " has 
null value" );
-            }
-
-            String[] stringArray = StringUtils.split( item.toString(), "," );
-
-            for ( String aStringArray : stringArray )
+            int i = 0;
+            for ( Object item : items )
             {
-                setProperty( propertyPrefix + i, aStringArray );
-                i++;
+                if ( item == null )
+                {
+                    throw new NullPointerException( propertyPrefix + i + " has 
null value" );
+                }
+
+                String[] stringArray = StringUtils.split( item.toString(), "," 
);
+
+                for ( String aStringArray : stringArray )
+                {
+                    setProperty( propertyPrefix + i, aStringArray );
+                    i++;
+                }
             }
         }
     }
 
     public void setClasspath( String prefix, Classpath classpath )
     {
-        List classpathElements = classpath.getClassPath();
+        List<String> classpathElements = classpath.getClassPath();
         for ( int i = 0; i < classpathElements.size(); ++i )
         {
-            String element = (String) classpathElements.get( i );
+            String element = classpathElements.get( i );
             setProperty( prefix + i, element );
         }
     }
@@ -232,38 +230,26 @@ public class SurefireProperties
     private static SurefireProperties loadProperties( InputStream inStream )
         throws IOException
     {
-        Properties p = new Properties();
-
         try
         {
+            Properties p = new Properties();
             p.load( inStream );
+            return new SurefireProperties( p );
         }
         finally
         {
             close( inStream );
         }
-
-        return new SurefireProperties( p );
     }
 
     public static SurefireProperties loadProperties( File file )
         throws IOException
     {
-        if ( file != null )
-        {
-            return loadProperties( new FileInputStream( file ) );
-        }
-
-        return new SurefireProperties();
+        return file == null ? new SurefireProperties() : loadProperties( new 
FileInputStream( file ) );
     }
 
     private static void close( InputStream inputStream )
     {
-        if ( inputStream == null )
-        {
-            return;
-        }
-
         try
         {
             inputStream.close();
@@ -280,8 +266,5 @@ public class SurefireProperties
         {
             super.setProperty( key, value );
         }
-
     }
-
-
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 0164ba9..139e14b 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
@@ -133,6 +133,7 @@ class BooterSerializer
         properties.setProperty( USEMANIFESTONLYJAR, String.valueOf( 
classLoaderConfig.isUseManifestOnlyJar() ) );
         properties.setProperty( FAILIFNOTESTS, String.valueOf( 
booterConfiguration.isFailIfNoTests() ) );
         properties.setProperty( PROVIDER_CONFIGURATION, 
providerConfiguration.getProviderClassName() );
+        properties.setProperty( FAIL_FAST_COUNT, String.valueOf( 
booterConfiguration.getSkipAfterFailureCount() ) );
         List<CommandLineOption> mainCliOptions = 
booterConfiguration.getMainCliOptions();
         if ( mainCliOptions != null )
         {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 4b2e184..632834f 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
@@ -20,22 +20,21 @@ package org.apache.maven.plugin.surefire.booterclient;
  */
 
 import org.apache.maven.plugin.logging.Log;
-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.AbstractForkInputStream;
+import 
org.apache.maven.plugin.surefire.booterclient.lazytestprovider.NotifiableTestStream;
 import 
org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
+import 
org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream;
 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.ShutdownHookUtils;
 import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.ClasspathConfiguration;
-import org.apache.maven.surefire.booter.Command;
 import org.apache.maven.surefire.booter.KeyValueSource;
 import org.apache.maven.surefire.booter.PropertiesWrapper;
 import org.apache.maven.surefire.booter.ProviderConfiguration;
@@ -43,14 +42,11 @@ import org.apache.maven.surefire.booter.ProviderFactory;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 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.StackTraceWriter;
 import org.apache.maven.surefire.suite.RunResult;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.DefaultScanResult;
-import org.apache.maven.surefire.util.internal.DaemonThreadFactory;
-import org.apache.maven.surefire.util.internal.StringUtils;
 
 import java.io.File;
 import java.io.IOException;
@@ -59,6 +55,7 @@ import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Map;
+import java.util.Properties;
 import java.util.Queue;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.Callable;
@@ -70,10 +67,23 @@ import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
+import static 
org.apache.maven.shared.utils.cli.CommandLineUtils.executeCommandLine;
+import static 
org.apache.maven.shared.utils.cli.ShutdownHookUtils.addShutDownHook;
+import static 
org.apache.maven.shared.utils.cli.ShutdownHookUtils.removeShutdownHook;
+import static 
org.apache.maven.surefire.util.internal.StringUtils.FORK_STREAM_CHARSET_NAME;
+import static 
org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThread;
+import static 
org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThreadFactory;
+import static 
org.apache.maven.plugin.surefire.AbstractSurefireMojo.createCopyAndReplaceForkNumPlaceholder;
+import static org.apache.maven.plugin.surefire.booterclient.lazytestprovider.
+    TestLessInputStream.TestLessInputStreamBuilder;
 import static org.apache.maven.surefire.booter.Classpath.join;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
+import static 
org.apache.maven.surefire.booter.SystemPropertyManager.writePropertiesFile;
+import static org.apache.maven.surefire.suite.RunResult.timeout;
+import static org.apache.maven.surefire.suite.RunResult.failure;
+import static org.apache.maven.surefire.suite.RunResult.SUCCESS;
 import static java.lang.StrictMath.min;
 
 /**
@@ -92,7 +102,7 @@ import static java.lang.StrictMath.min;
  */
 public class ForkStarter
 {
-    private final ThreadFactory threadFactory = 
DaemonThreadFactory.newDaemonThreadFactory();
+    private final ThreadFactory threadFactory = newDaemonThreadFactory();
 
     /**
      * Closes an InputStream
@@ -180,9 +190,19 @@ public class ForkStarter
     {
         DefaultReporterFactory forkedReporterFactory = new 
DefaultReporterFactory( startupReportConfiguration );
         defaultReporterFactories.add( forkedReporterFactory );
-        final ForkClient forkClient =
-                new ForkClient( forkedReporterFactory, 
startupReportConfiguration.getTestVmSystemProperties() );
-        return fork( null, new PropertiesWrapper( providerProperties ), 
forkClient, effectiveSystemProperties, null );
+        PropertiesWrapper props = new PropertiesWrapper( providerProperties );
+        ForkClient forkClient =
+            new ForkClient( forkedReporterFactory, 
startupReportConfiguration.getTestVmSystemProperties() );
+        TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder();
+        TestLessInputStream stream = builder.build();
+        try
+        {
+            return fork( null, props, forkClient, effectiveSystemProperties, 
stream, false );
+        }
+        finally
+        {
+            builder.removeStream( stream );
+        }
     }
 
     private RunResult run( SurefireProperties effectiveSystemProperties )
@@ -209,41 +229,57 @@ public class ForkStarter
     private RunResult runSuitesForkOnceMultiple( final SurefireProperties 
effectiveSystemProperties, int forkCount )
         throws SurefireBooterForkException
     {
-        ArrayList<Future<RunResult>> results = new 
ArrayList<Future<RunResult>>( forkCount );
         ThreadPoolExecutor executorService = new ThreadPoolExecutor( 
forkCount, forkCount, 60, TimeUnit.SECONDS,
                                                                   new 
ArrayBlockingQueue<Runnable>( forkCount ) );
         executorService.setThreadFactory( threadFactory );
         try
         {
-            final Queue<Command> commands = new 
ConcurrentLinkedQueue<Command>();
+            final Queue<String> tests = new ConcurrentLinkedQueue<String>();
+
             for ( Class<?> clazz : getSuitesIterator() )
             {
-                commands.add( new Command( RUN_CLASS, clazz.getName() ) );
+                tests.add( clazz.getName() );
+            }
+
+            final Queue<TestProvidingInputStream> testStreams = new 
ConcurrentLinkedQueue<TestProvidingInputStream>();
+
+            for ( int forkNum = 0, total = min( forkCount, tests.size() ); 
forkNum < total; forkNum++ )
+            {
+                testStreams.add( new TestProvidingInputStream( tests ) );
             }
-            Collection<TestProvidingInputStream> testStreams = new 
ArrayList<TestProvidingInputStream>();
-            for ( int forkNum = 0, total = min( forkCount, commands.size() ); 
forkNum < total; forkNum++ )
+
+            final AtomicBoolean notifyStreamsToSkipTestsJustNow = new 
AtomicBoolean();
+            Collection<Future<RunResult>> results = new 
ArrayList<Future<RunResult>>( forkCount );
+            for ( final TestProvidingInputStream testProvidingInputStream : 
testStreams )
             {
-                final TestProvidingInputStream testProvidingInputStream = new 
TestProvidingInputStream( commands );
-                testStreams.add( testProvidingInputStream );
                 Callable<RunResult> pf = new Callable<RunResult>()
                 {
                     public RunResult call()
                         throws Exception
                     {
-                        DefaultReporterFactory forkedReporterFactory =
-                            new DefaultReporterFactory( 
startupReportConfiguration );
-                        defaultReporterFactories.add( forkedReporterFactory );
-                        ForkClient forkClient = new ForkClient( 
forkedReporterFactory,
-                                                                
startupReportConfiguration.getTestVmSystemProperties(),
-                                                                
testProvidingInputStream );
+                        DefaultReporterFactory reporter = new 
DefaultReporterFactory( startupReportConfiguration );
+                        defaultReporterFactories.add( reporter );
+
+                        Properties vmProps = 
startupReportConfiguration.getTestVmSystemProperties();
+
+                        ForkClient forkClient = new ForkClient( reporter, 
vmProps, testProvidingInputStream )
+                        {
+                            @Override
+                            protected void stopOnNextTest()
+                            {
+                                if ( 
notifyStreamsToSkipTestsJustNow.compareAndSet( false, true ) )
+                                {
+                                    notifyStreamsToSkipTests( testStreams );
+                                }
+                            }
+                        };
 
                         return fork( null, new PropertiesWrapper( 
providerConfiguration.getProviderProperties() ),
-                                     forkClient, effectiveSystemProperties, 
testProvidingInputStream );
+                                 forkClient, effectiveSystemProperties, 
testProvidingInputStream, true );
                     }
                 };
                 results.add( executorService.submit( pf ) );
             }
-            dispatchTestSetFinished( testStreams );
             return awaitResultsDone( results, executorService );
         }
         finally
@@ -252,6 +288,14 @@ public class ForkStarter
         }
     }
 
+    private static void notifyStreamsToSkipTests( Collection<? extends 
NotifiableTestStream> notifiableTestStreams )
+    {
+        for ( NotifiableTestStream notifiableTestStream : 
notifiableTestStreams )
+        {
+            notifiableTestStream.skipSinceNextTest();
+        }
+    }
+
     @SuppressWarnings( "checkstyle:magicnumber" )
     private RunResult runSuitesForkPerTestSet( final SurefireProperties 
effectiveSystemProperties, int forkCount )
         throws SurefireBooterForkException
@@ -262,6 +306,8 @@ public class ForkStarter
         executorService.setThreadFactory( threadFactory );
         try
         {
+            final AtomicBoolean notifyStreamsToSkipTestsJustNow = new 
AtomicBoolean();
+            final TestLessInputStreamBuilder builder = new 
TestLessInputStreamBuilder();
             for ( final Object testSet : getSuitesIterator() )
             {
                 Callable<RunResult> pf = new Callable<RunResult>()
@@ -272,10 +318,29 @@ public class ForkStarter
                         DefaultReporterFactory forkedReporterFactory =
                             new DefaultReporterFactory( 
startupReportConfiguration );
                         defaultReporterFactories.add( forkedReporterFactory );
-                        ForkClient forkClient = new ForkClient( 
forkedReporterFactory,
-                                                        
startupReportConfiguration.getTestVmSystemProperties() );
-                        return fork( testSet, new PropertiesWrapper( 
providerConfiguration.getProviderProperties() ),
-                                     forkClient, effectiveSystemProperties, 
null );
+                        Properties vmProps = 
startupReportConfiguration.getTestVmSystemProperties();
+                        ForkClient forkClient = new ForkClient( 
forkedReporterFactory, vmProps )
+                        {
+                            @Override
+                            protected void stopOnNextTest()
+                            {
+                                if ( 
notifyStreamsToSkipTestsJustNow.compareAndSet( false, true ) )
+                                {
+                                    
builder.getCachableCommands().skipSinceNextTest();
+                                }
+                            }
+                        };
+                        TestLessInputStream stream = builder.build();
+                        try
+                        {
+                            return fork( testSet,
+                                         new PropertiesWrapper( 
providerConfiguration.getProviderProperties() ),
+                                         forkClient, 
effectiveSystemProperties, stream, false );
+                        }
+                        finally
+                        {
+                            builder.removeStream( stream );
+                        }
                     }
                 };
                 results.add( executorService.submit( pf ) );
@@ -314,20 +379,14 @@ public class ForkStarter
             }
             catch ( ExecutionException e )
             {
-                throw new SurefireBooterForkException( "ExecutionException", e 
);
+                Throwable realException = e.getCause();
+                String error = realException == null ? "" : 
realException.getLocalizedMessage();
+                throw new SurefireBooterForkException( "ExecutionException " + 
error, realException );
             }
         }
         return globalResult;
     }
 
-    private static void dispatchTestSetFinished( 
Iterable<TestProvidingInputStream> testStreams )
-    {
-        for ( TestProvidingInputStream testStream : testStreams )
-        {
-            testStream.testSetFinished();
-        }
-    }
-
     @SuppressWarnings( "checkstyle:magicnumber" )
     private void closeExecutor( ExecutorService executorService )
         throws SurefireBooterForkException
@@ -347,14 +406,14 @@ public class ForkStarter
 
     private RunResult fork( Object testSet, KeyValueSource providerProperties, 
ForkClient forkClient,
                             SurefireProperties effectiveSystemProperties,
-                            TestProvidingInputStream testProvidingInputStream )
+                            AbstractForkInputStream testProvidingInputStream, 
boolean readTestsFromInStream )
         throws SurefireBooterForkException
     {
         int forkNumber = ForkNumberBucket.drawNumber();
         try
         {
             return fork( testSet, providerProperties, forkClient, 
effectiveSystemProperties, forkNumber,
-                         testProvidingInputStream );
+                         testProvidingInputStream, readTestsFromInStream );
         }
         finally
         {
@@ -364,28 +423,30 @@ public class ForkStarter
 
     private RunResult fork( Object testSet, KeyValueSource providerProperties, 
ForkClient forkClient,
                             SurefireProperties effectiveSystemProperties, int 
forkNumber,
-                            TestProvidingInputStream testProvidingInputStream )
+                            AbstractForkInputStream testProvidingInputStream, 
boolean readTestsFromInStream )
         throws SurefireBooterForkException
     {
-        File surefireProperties;
-        File systPropsFile = null;
+        final File surefireProperties;
+        final File systPropsFile;
         try
         {
             BooterSerializer booterSerializer = new BooterSerializer( 
forkConfiguration );
 
-            surefireProperties =
-                booterSerializer.serialize( providerProperties, 
providerConfiguration, startupConfiguration, testSet,
-                                            null != testProvidingInputStream );
+            surefireProperties = booterSerializer.serialize( 
providerProperties, providerConfiguration,
+                                                             
startupConfiguration, testSet, readTestsFromInStream );
 
             if ( effectiveSystemProperties != null )
             {
                 SurefireProperties filteredProperties =
-                    
AbstractSurefireMojo.createCopyAndReplaceForkNumPlaceholder( 
effectiveSystemProperties,
-                                                                               
  forkNumber );
-                systPropsFile =
-                    SystemPropertyManager.writePropertiesFile( 
filteredProperties, forkConfiguration.getTempDirectory(),
-                                                               "surefire_" + 
systemPropertiesFileCounter++,
-                                                               
forkConfiguration.isDebug() );
+                    createCopyAndReplaceForkNumPlaceholder( 
effectiveSystemProperties, forkNumber );
+
+                systPropsFile = writePropertiesFile( filteredProperties, 
forkConfiguration.getTempDirectory(),
+                                                     "surefire_" + 
systemPropertiesFileCounter++,
+                                                     
forkConfiguration.isDebug() );
+            }
+            else
+            {
+                systPropsFile = null;
             }
         }
         catch ( IOException e )
@@ -407,23 +468,14 @@ public class ForkStarter
             log.debug( bootClasspath.getLogMessage( "boot" ) );
             log.debug( bootClasspath.getCompactLogMessage( "boot(compact)" ) );
         }
+
         OutputStreamFlushableCommandline cli =
             forkConfiguration.createCommandLine( bootClasspath.getClassPath(), 
startupConfiguration, forkNumber );
 
-        final InputStreamCloser inputStreamCloser;
-        final Thread inputStreamCloserHook;
-        if ( testProvidingInputStream != null )
-        {
-            testProvidingInputStream.setFlushReceiverProvider( cli );
-            inputStreamCloser = new InputStreamCloser( 
testProvidingInputStream );
-            inputStreamCloserHook = DaemonThreadFactory.newDaemonThread( 
inputStreamCloser, "input-stream-closer" );
-            ShutdownHookUtils.addShutDownHook( inputStreamCloserHook );
-        }
-        else
-        {
-            inputStreamCloser = null;
-            inputStreamCloserHook = null;
-        }
+        InputStreamCloser inputStreamCloser = new InputStreamCloser( 
testProvidingInputStream );
+        Thread inputStreamCloserHook = newDaemonThread( inputStreamCloser, 
"input-stream-closer" );
+        testProvidingInputStream.setFlushReceiverProvider( cli );
+        addShutDownHook( inputStreamCloserHook );
 
         cli.createArg().setFile( surefireProperties );
 
@@ -444,11 +496,12 @@ public class ForkStarter
         try
         {
             final int timeout = forkedProcessTimeoutInSeconds > 0 ? 
forkedProcessTimeoutInSeconds : 0;
-            final int result =
-                CommandLineUtils.executeCommandLine( cli, 
testProvidingInputStream, threadedStreamConsumer,
-                                                     threadedStreamConsumer, 
timeout, inputStreamCloser,
-                                                     Charset.forName( 
StringUtils.FORK_STREAM_CHARSET_NAME ) );
-            if ( result != RunResult.SUCCESS )
+
+            final int result = executeCommandLine( cli, 
testProvidingInputStream, threadedStreamConsumer,
+                                                   threadedStreamConsumer, 
timeout, inputStreamCloser,
+                                                   Charset.forName( 
FORK_STREAM_CHARSET_NAME ) );
+
+            if ( result != SUCCESS )
             {
                 throw new SurefireBooterForkException( "Error occurred in 
starting fork, check output in log" );
             }
@@ -456,27 +509,24 @@ public class ForkStarter
         }
         catch ( CommandLineTimeOutException e )
         {
-            runResult = RunResult.timeout(
-                
forkClient.getDefaultReporterFactory().getGlobalRunStatistics().getRunResult() 
);
+            runResult = timeout( 
forkClient.getDefaultReporterFactory().getGlobalRunStatistics().getRunResult() 
);
         }
         catch ( CommandLineException e )
         {
-            runResult =
-                RunResult.failure( 
forkClient.getDefaultReporterFactory().getGlobalRunStatistics().getRunResult(), 
e );
+            runResult = failure( 
forkClient.getDefaultReporterFactory().getGlobalRunStatistics().getRunResult(), 
e );
             throw new SurefireBooterForkException( "Error while executing 
forked tests.", e.getCause() );
         }
         finally
         {
             threadedStreamConsumer.close();
-            if ( inputStreamCloser != null )
-            {
-                inputStreamCloser.run();
-                ShutdownHookUtils.removeShutdownHook( inputStreamCloserHook );
-            }
+            inputStreamCloser.run();
+            removeShutdownHook( inputStreamCloserHook );
+
             if ( runResult == null )
             {
                 runResult = 
forkClient.getDefaultReporterFactory().getGlobalRunStatistics().getRunResult();
             }
+
             if ( !runResult.isTimeout() )
             {
                 StackTraceWriter errorInFork = forkClient.getErrorInFork();
@@ -512,9 +562,8 @@ public class ForkStarter
             CommonReflector commonReflector = new CommonReflector( 
unifiedClassLoader );
             Object reporterFactory = 
commonReflector.createReportingReporterFactory( startupReportConfiguration );
 
-            final ProviderFactory providerFactory =
-                new ProviderFactory( startupConfiguration, 
providerConfiguration, unifiedClassLoader,
-                                     reporterFactory );
+            ProviderFactory providerFactory =
+                new ProviderFactory( startupConfiguration, 
providerConfiguration, unifiedClassLoader, reporterFactory );
             SurefireProvider surefireProvider = 
providerFactory.createProvider( false );
             return surefireProvider.getSuites();
         }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
deleted file mode 100644
index b80f80f..0000000
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package org.apache.maven.plugin.surefire.booterclient;
-
-/*
- * 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.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-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.RunListener;
-
-/**
- * Internal use only
- */
-public class MockReporter
-    implements RunListener, ConsoleLogger, ConsoleOutputReceiver
-{
-    private final List<String> events = new ArrayList<String>();
-
-    private final List<Object> data = new ArrayList<Object>();
-
-    public static final String SET_STARTING = "SET_STARTED";
-
-    public static final String SET_COMPLETED = "SET_COMPLETED";
-
-    public static final String TEST_STARTING = "TEST_STARTED";
-
-    public static final String TEST_SUCCEEDED = "TEST_COMPLETED";
-
-    public static final String TEST_FAILED = "TEST_FAILED";
-
-    public static final String TEST_ERROR = "TEST_ERROR";
-
-    public static final String TEST_SKIPPED = "TEST_SKIPPED";
-
-    public static final String TEST_ASSUMPTION_FAIL = 
"TEST_ASSUMPTION_SKIPPED";
-
-    public static final String CONSOLE_OUTPUT = "CONSOLE_OUTPUT";
-
-    public static final String STDOUT = "STDOUT";
-
-    public static final String STDERR = "STDERR";
-
-    private final AtomicInteger testSucceeded = new AtomicInteger();
-
-    private final AtomicInteger testIgnored = new AtomicInteger();
-
-    private final AtomicInteger testFailed = new AtomicInteger();
-
-    public MockReporter()
-    {
-    }
-
-    public void testSetStarting( ReportEntry report )
-    {
-        events.add( SET_STARTING );
-        data.add( report );
-    }
-
-    public void testSetCompleted( ReportEntry report )
-    {
-        events.add( SET_COMPLETED );
-        data.add( report );
-    }
-
-    public void testStarting( ReportEntry report )
-    {
-        events.add( TEST_STARTING );
-        data.add( report );
-    }
-
-    public void testSucceeded( ReportEntry report )
-    {
-        events.add( TEST_SUCCEEDED );
-        testSucceeded.incrementAndGet();
-        data.add( report );
-    }
-
-    public void testError( ReportEntry report )
-    {
-        events.add( TEST_ERROR );
-        data.add( report );
-        testFailed.incrementAndGet();
-    }
-
-    public void testFailed( ReportEntry report )
-    {
-        events.add( TEST_FAILED );
-        data.add( report );
-        testFailed.incrementAndGet();
-    }
-
-
-    public void testSkipped( ReportEntry report )
-    {
-        events.add( TEST_SKIPPED );
-        data.add( report );
-        testIgnored.incrementAndGet();
-    }
-
-
-    public List<String> getEvents()
-    {
-        return events;
-    }
-
-    public List getData()
-    {
-        return data;
-    }
-
-    public String getFirstEvent()
-    {
-        return events.get( 0 );
-    }
-
-    public ReportEntry getFirstData()
-    {
-        return (ReportEntry) data.get( 0 );
-    }
-
-
-    public void testAssumptionFailure( ReportEntry report )
-    {
-        events.add( TEST_ASSUMPTION_FAIL );
-        data.add( report );
-        testIgnored.incrementAndGet();
-
-    }
-
-    public void info( String message )
-    {
-        events.add( CONSOLE_OUTPUT );
-        data.add( message );
-    }
-
-    public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
-    {
-        events.add( stdout ? STDOUT : STDERR );
-        data.add( new String( buf, off, len ) );
-    }
-}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
new file mode 100644
index 0000000..4d6331c
--- /dev/null
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
@@ -0,0 +1,122 @@
+package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.booter.MasterProcessCommand;
+
+import java.io.IOException;
+
+/**
+ * Reader stream sends commands to forked jvm std-{@link java.io.InputStream 
input-stream}.
+ *
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19
+ * @see org.apache.maven.surefire.booter.Command
+ */
+public abstract class AbstractCommandStream
+    extends AbstractForkInputStream
+{
+    private byte[] currentBuffer;
+    private int currentPos;
+    private volatile MasterProcessCommand lastCommand;
+
+    protected abstract boolean isClosed();
+
+    /**
+     * Unnecessarily opposite to {@link #isClosed()} however may respect
+     * {@link #getLastCommand() last command} and {@link #isClosed()}.
+     */
+    protected abstract boolean canContinue();
+
+    /**
+     * Possibly waiting for next command (see {@link #nextCommand()}) unless 
the stream is atomically
+     * closed (see {@link #isClosed()} returns {@code true}) before this 
method has returned.
+     */
+    protected void beforeNextCommand()
+        throws IOException
+    {
+    }
+
+    protected abstract Command nextCommand();
+
+    /**
+     * Returns quietly and immediately.
+     */
+    protected final void invalidateInternalBuffer()
+    {
+        currentBuffer = null;
+        currentPos = 0;
+    }
+
+    protected final MasterProcessCommand getLastCommand()
+    {
+        return lastCommand;
+    }
+
+    /**
+     * Used by single thread in StreamFeeder class.
+     *
+     * @return {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @SuppressWarnings( "checkstyle:magicnumber" )
+    @Override
+    public int read()
+        throws IOException
+    {
+        if ( isClosed() )
+        {
+            return -1;
+        }
+
+        byte[] buffer = currentBuffer;
+        if ( buffer == null )
+        {
+            tryFlush();
+
+            if ( !canContinue() )
+            {
+                close();
+                return -1;
+            }
+
+            beforeNextCommand();
+
+            if ( isClosed() )
+            {
+                return -1;
+            }
+
+            Command cmd = nextCommand();
+            lastCommand = cmd.getCommandType();
+            buffer = lastCommand.hasDataType() ? lastCommand.encode( 
cmd.getData() ) : lastCommand.encode();
+        }
+
+        int b =  buffer[currentPos++] & 0xff;
+        if ( currentPos == buffer.length )
+        {
+            buffer = null;
+            currentPos = 0;
+        }
+        currentBuffer = buffer;
+        return b;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractForkInputStream.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractForkInputStream.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractForkInputStream.java
new file mode 100644
index 0000000..281c05d
--- /dev/null
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractForkInputStream.java
@@ -0,0 +1,61 @@
+package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
+
+/*
+ * 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.io.IOException;
+import java.io.InputStream;
+
+import static 
org.apache.maven.surefire.util.internal.StringUtils.requireNonNull;
+
+/**
+ * Reader stream sends bytes to forked jvm std-{@link InputStream 
input-stream}.
+ *
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public abstract class AbstractForkInputStream
+    extends InputStream
+    implements NotifiableTestStream
+{
+    private volatile FlushReceiverProvider flushReceiverProvider;
+
+    /**
+     * @param flushReceiverProvider the provider for a flush receiver.
+     */
+    public void setFlushReceiverProvider( FlushReceiverProvider 
flushReceiverProvider )
+    {
+        this.flushReceiverProvider = requireNonNull( flushReceiverProvider );
+    }
+
+    protected boolean tryFlush()
+        throws IOException
+    {
+        if ( flushReceiverProvider != null )
+        {
+            FlushReceiver flushReceiver = 
flushReceiverProvider.getFlushReceiver();
+            if ( flushReceiver != null )
+            {
+                flushReceiver.flush();
+                return true;
+            }
+        }
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
index 47aa642..d47645b 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
@@ -28,5 +28,19 @@ package 
org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
  */
 public interface NotifiableTestStream
 {
+    /**
+     * Notifies {@link TestProvidingInputStream} in order to dispatch a new 
test back to the forked
+     * jvm (particular fork which hits this call); or do nothing in {@link 
TestLessInputStream}.
+     */
     void provideNewTest();
+
+    /**
+     * Sends an event to a fork jvm in order to skip tests.
+     * Returns immediately without blocking.
+     */
+    void skipSinceNextTest();
+
+    void shutdown();
+
+    void noop();
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
new file mode 100644
index 0000000..b608620
--- /dev/null
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
@@ -0,0 +1,453 @@
+package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.booter.Command;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import static org.apache.maven.surefire.booter.Command.NOOP;
+import static org.apache.maven.surefire.booter.Command.SHUTDOWN;
+import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
+
+/**
+ * Dispatches commands without tests.
+ *
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public final class TestLessInputStream
+    extends AbstractCommandStream
+{
+    private final Semaphore barrier = new Semaphore( 0 );
+
+    private final AtomicBoolean closed = new AtomicBoolean();
+
+    private final Queue<Command> immediateCommands = new 
ConcurrentLinkedQueue<Command>();
+
+    private final TestLessInputStreamBuilder builder;
+
+    private Iterator<Command> cachableCommands;
+
+    private TestLessInputStream( TestLessInputStreamBuilder builder )
+    {
+        this.builder = builder;
+    }
+
+    public void provideNewTest()
+    {
+    }
+
+    public void skipSinceNextTest()
+    {
+        if ( canContinue() )
+        {
+            immediateCommands.add( SKIP_SINCE_NEXT_TEST );
+            barrier.release();
+        }
+    }
+
+    public void shutdown()
+    {
+        if ( canContinue() )
+        {
+            immediateCommands.add( SHUTDOWN );
+            barrier.release();
+        }
+    }
+
+    public void noop()
+    {
+        if ( canContinue() )
+        {
+            immediateCommands.add( NOOP );
+            barrier.release();
+        }
+    }
+
+    @Override
+    protected boolean isClosed()
+    {
+        return closed.get();
+    }
+
+    @Override
+    protected boolean canContinue()
+    {
+        return !isClosed();
+    }
+
+    @Override
+    protected Command nextCommand()
+    {
+        Command cmd = immediateCommands.poll();
+        if ( cmd == null )
+        {
+            if ( cachableCommands == null )
+            {
+                cachableCommands = builder.getIterableCachable().iterator();
+            }
+
+            cmd = cachableCommands.next();
+        }
+        return cmd;
+    }
+
+    @Override
+    protected void beforeNextCommand()
+        throws IOException
+    {
+        awaitNextCommand();
+    }
+
+    @Override
+    public void close()
+    {
+        if ( closed.compareAndSet( false, true ) )
+        {
+            invalidateInternalBuffer();
+            barrier.drainPermits();
+            barrier.release();
+        }
+    }
+
+    /**
+     * For testing purposes only.
+     *
+     * @return permits used internally by {@link #beforeNextCommand()}
+     */
+    int availablePermits()
+    {
+        return barrier.availablePermits();
+    }
+
+    private void awaitNextCommand()
+        throws IOException
+    {
+        try
+        {
+            barrier.acquire();
+        }
+        catch ( InterruptedException e )
+        {
+            // help GC to free this object because StreamFeeder Thread cannot 
read it anyway after IOE
+            invalidateInternalBuffer();
+            throw new IOException( e.getLocalizedMessage() );
+        }
+    }
+
+    /**
+     * todo
+     */
+    public static final class TestLessInputStreamBuilder
+    {
+        ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
+        private final Queue<TestLessInputStream> aliveStreams = new 
ConcurrentLinkedQueue<TestLessInputStream>();
+        private final ImmediateCommands immediateCommands = new 
ImmediateCommands();
+        private final CachableCommands cachableCommands = new 
CachableCommands();
+        private final Node head = new Node( null );
+        private final Iterable<Command> iterableCachable;
+
+        public TestLessInputStreamBuilder()
+        {
+            iterableCachable = new Iterable<Command>()
+            {
+                public Iterator<Command> iterator()
+                {
+                    return new CIt();
+                }
+            };
+        }
+
+        public TestLessInputStream build()
+        {
+            Lock lock = rwLock.writeLock();
+            lock.lock();
+            try
+            {
+                TestLessInputStream is = new TestLessInputStream( this );
+                aliveStreams.offer( is );
+                return is;
+            }
+            finally
+            {
+                lock.unlock();
+            }
+        }
+
+        public void removeStream( TestLessInputStream is )
+        {
+            Lock lock = rwLock.writeLock();
+            lock.lock();
+            try
+            {
+                aliveStreams.remove( is );
+            }
+            finally
+            {
+                lock.unlock();
+            }
+        }
+
+        public NotifiableTestStream getImmediateCommands()
+        {
+            return immediateCommands;
+        }
+
+        public NotifiableTestStream getCachableCommands()
+        {
+            return cachableCommands;
+        }
+
+        /**
+         * The iterator is not thread safe.
+         */
+        Iterable<Command> getIterableCachable()
+        {
+            return iterableCachable;
+        }
+
+        @SuppressWarnings( "checkstyle:innerassignment" )
+        private void addTailNode( Command command )
+        {
+            Node newTail = new Node( command );
+            Node currentTail = head;
+            do
+            {
+                for ( Node successor; ( successor = currentTail.next.get() ) 
!= null; )
+                {
+                    currentTail = successor;
+                }
+            } while ( !currentTail.next.compareAndSet( null, newTail ) );
+        }
+
+        @SuppressWarnings( "checkstyle:innerassignment" )
+        private boolean addTailNodeIfAbsent( Command command )
+        {
+            Node newTail = new Node( command );
+            Node currentTail = head;
+            do
+            {
+                for ( Node successor; ( successor = currentTail.next.get() ) 
!= null; )
+                {
+                    currentTail = successor;
+                    if ( command.equals( currentTail.command ) )
+                    {
+                        return false;
+                    }
+                }
+            } while ( !currentTail.next.compareAndSet( null, newTail ) );
+            return true;
+        }
+
+        private static Node nextCachedNode( Node current )
+        {
+            return current.next.get();
+        }
+
+        private final class CIt
+            implements Iterator<Command>
+        {
+            private Node node = TestLessInputStreamBuilder.this.head;
+
+            public boolean hasNext()
+            {
+                return examineNext( false ) != null;
+            }
+
+            public Command next()
+            {
+                Command command = examineNext( true );
+                if ( command == null )
+                {
+                    throw new NoSuchElementException();
+                }
+                return command;
+            }
+
+            public void remove()
+            {
+                throw new UnsupportedOperationException();
+            }
+
+            private Command examineNext( boolean store )
+            {
+                Node next = nextCachedNode( node );
+                if ( store && next != null )
+                {
+                    node = next;
+                }
+                return next == null ? null : next.command;
+            }
+        }
+
+        /**
+         * Event is called just now for all alive streams and command is not 
persisted.
+         */
+        private final class ImmediateCommands
+            implements NotifiableTestStream
+        {
+            public void provideNewTest()
+            {
+            }
+
+            public void skipSinceNextTest()
+            {
+                Lock lock = rwLock.readLock();
+                lock.lock();
+                try
+                {
+                    for ( TestLessInputStream aliveStream : 
TestLessInputStreamBuilder.this.aliveStreams )
+                    {
+                        aliveStream.skipSinceNextTest();
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
+
+            public void shutdown()
+            {
+                Lock lock = rwLock.readLock();
+                lock.lock();
+                try
+                {
+                    for ( TestLessInputStream aliveStream : 
TestLessInputStreamBuilder.this.aliveStreams )
+                    {
+                        aliveStream.shutdown();
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
+
+            public void noop()
+            {
+                Lock lock = rwLock.readLock();
+                lock.lock();
+                try
+                {
+                    for ( TestLessInputStream aliveStream : 
TestLessInputStreamBuilder.this.aliveStreams )
+                    {
+                        aliveStream.noop();
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
+        }
+
+        /**
+         * Event is persisted.
+         */
+        private final class CachableCommands
+            implements NotifiableTestStream
+        {
+            public void provideNewTest()
+            {
+            }
+
+            public void skipSinceNextTest()
+            {
+                Lock lock = rwLock.readLock();
+                lock.lock();
+                try
+                {
+                    if ( TestLessInputStreamBuilder.this.addTailNodeIfAbsent( 
SKIP_SINCE_NEXT_TEST ) )
+                    {
+                        release();
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
+
+            public void shutdown()
+            {
+                Lock lock = rwLock.readLock();
+                lock.lock();
+                try
+                {
+                    if ( TestLessInputStreamBuilder.this.addTailNodeIfAbsent( 
SHUTDOWN ) )
+                    {
+                        release();
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
+
+            public void noop()
+            {
+                Lock lock = rwLock.readLock();
+                lock.lock();
+                try
+                {
+                    if ( TestLessInputStreamBuilder.this.addTailNodeIfAbsent( 
NOOP ) )
+                    {
+                        release();
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
+
+            private void release()
+            {
+                for ( TestLessInputStream aliveStream : 
TestLessInputStreamBuilder.this.aliveStreams )
+                {
+                    aliveStream.barrier.release();
+                }
+            }
+        }
+
+        private static class Node
+        {
+            private final AtomicReference<Node> next = new 
AtomicReference<Node>();
+            private final Command command;
+
+            Node( Command command )
+            {
+                this.command = command;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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
index 97bf24d..dcc2997 100644
--- 
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
@@ -20,144 +20,136 @@ package 
org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
  */
 
 import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.booter.MasterProcessCommand;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
 import static 
org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
-import static 
org.apache.maven.surefire.util.internal.StringUtils.requireNonNull;
+import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.Command.SHUTDOWN;
+import static org.apache.maven.surefire.booter.Command.NOOP;
 
 /**
- * An {@link InputStream} that, when read, provides test class names out of a 
queue.
+ * An {@link java.io.InputStream} that, when read, provides test class names 
out of a queue.
  * <p/>
  * The Stream provides only one test at a time, but only after {@link 
#provideNewTest()} has been invoked.
  * <p/>
  * After providing each test class name, followed by a newline character, a 
flush is performed on the
  * {@link FlushReceiver} provided by the {@link FlushReceiverProvider} that 
can be set using
  * {@link #setFlushReceiverProvider(FlushReceiverProvider)}.
+ * <p/>
+ * The instance is used only in reusable forks in {@link 
org.apache.maven.plugin.surefire.booterclient.ForkStarter}
+ * by one Thread.
  *
  * @author Andreas Gudian
+ * @author Tibor Digana (tibor17)
  */
-public class TestProvidingInputStream
-    extends InputStream
-    implements NotifiableTestStream
+public final class TestProvidingInputStream
+    extends AbstractCommandStream
 {
-    private final Semaphore semaphore = new Semaphore( 0 );
+    private final Semaphore barrier = new Semaphore( 0 );
 
-    private final Queue<Command> commands;
+    private final Queue<Command> commands = new 
ConcurrentLinkedQueue<Command>();
 
     private final AtomicBoolean closed = new AtomicBoolean();
 
-    private byte[] currentBuffer;
-
-    private int currentPos;
-
-    private MasterProcessCommand lastCommand;
-
-    private volatile FlushReceiverProvider flushReceiverProvider;
+    private final Queue<String> testClassNames;
 
     /**
      * C'tor
      *
-     * @param commands source of the tests to be read from this stream
+     * @param testClassNames source of the tests to be read from this stream
      */
-    public TestProvidingInputStream( Queue<Command> commands )
+    public TestProvidingInputStream( Queue<String> testClassNames )
     {
-        this.commands = commands;
+        this.testClassNames = testClassNames;
     }
 
     /**
-     * @param flushReceiverProvider the provider for a flush receiver.
+     * For testing purposes.
      */
-    public void setFlushReceiverProvider( FlushReceiverProvider 
flushReceiverProvider )
+    void testSetFinished()
     {
-        this.flushReceiverProvider = requireNonNull( flushReceiverProvider );
+        if ( canContinue() )
+        {
+            commands.add( Command.TEST_SET_FINISHED );
+            barrier.release();
+        }
     }
 
-    public void testSetFinished()
+    public void skipSinceNextTest()
     {
-        commands.add( new Command( TEST_SET_FINISHED ) );
+        if ( canContinue() )
+        {
+            commands.add( SKIP_SINCE_NEXT_TEST );
+            barrier.release();
+        }
     }
 
-    /**
-     * Used by single thread in StreamFeeder.
-     *
-     * @return {@inheritDoc}
-     * @throws IOException {@inheritDoc}
-     */
-    @SuppressWarnings( "checkstyle:magicnumber" )
-    @Override
-    public int read()
-        throws IOException
+    public void shutdown()
     {
-        byte[] buffer = currentBuffer;
-        if ( buffer == null )
+        if ( canContinue() )
         {
-            if ( flushReceiverProvider != null )
-            {
-                FlushReceiver flushReceiver = 
flushReceiverProvider.getFlushReceiver();
-                if ( flushReceiver != null )
-                {
-                    flushReceiver.flush();
-                }
-            }
-
-            if ( lastCommand == TEST_SET_FINISHED || closed.get() )
-            {
-                close();
-                return -1;
-            }
-
-            awaitNextTest();
-
-            if ( closed.get() )
-            {
-                return -1;
-            }
-
-            Command command = commands.poll();
-            lastCommand = command.getCommandType();
-            String test = command.getData();
-            buffer = lastCommand == TEST_SET_FINISHED ? lastCommand.encode() : 
lastCommand.encode( test );
+            commands.add( SHUTDOWN );
+            barrier.release();
         }
+    }
 
-        int b =  buffer[currentPos++] & 0xff;
-        if ( currentPos == buffer.length )
+    public void noop()
+    {
+        if ( canContinue() )
         {
-            buffer = null;
-            currentPos = 0;
+            commands.add( NOOP );
+            barrier.release();
         }
-        currentBuffer = buffer;
-        return b;
     }
 
-    private void awaitNextTest()
-        throws IOException
+    @Override
+    protected Command nextCommand()
     {
-        try
+        Command cmd = commands.poll();
+        if ( cmd == null )
         {
-            semaphore.acquire();
+            String cmdData = testClassNames.poll();
+            return cmdData == null ? Command.TEST_SET_FINISHED : new Command( 
RUN_CLASS, cmdData );
         }
-        catch ( InterruptedException e )
+        else
         {
-            // help GC to free this object because StreamFeeder Thread cannot 
read it after IOE
-            currentBuffer = null;
-            throw new IOException( e.getLocalizedMessage() );
+            return cmd;
         }
     }
 
+    @Override
+    protected void beforeNextCommand()
+        throws IOException
+    {
+        awaitNextTest();
+    }
+
+    @Override
+    protected boolean isClosed()
+    {
+        return closed.get();
+    }
+
+    @Override
+    protected boolean canContinue()
+    {
+        return getLastCommand() != TEST_SET_FINISHED && !isClosed();
+    }
+
     /**
      * Signal that a new test is to be provided.
      */
     public void provideNewTest()
     {
-        if ( !closed.get() )
+        if ( canContinue() )
         {
-            semaphore.release();
+            barrier.release();
         }
     }
 
@@ -166,9 +158,24 @@ public class TestProvidingInputStream
     {
         if ( closed.compareAndSet( false, true ) )
         {
-            currentBuffer = null;
-            semaphore.drainPermits();
-            semaphore.release();
+            invalidateInternalBuffer();
+            barrier.drainPermits();
+            barrier.release();
+        }
+    }
+
+    private void awaitNextTest()
+        throws IOException
+    {
+        try
+        {
+            barrier.acquire();
+        }
+        catch ( InterruptedException e )
+        {
+            // help GC to free this object because StreamFeeder Thread cannot 
read it anyway after IOE
+            invalidateInternalBuffer();
+            throw new IOException( e.getLocalizedMessage() );
         }
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 1778051..6cbc9bc 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
@@ -75,6 +75,10 @@ public class ForkClient
         this.notifiableTestStream = notifiableTestStream;
     }
 
+    protected void stopOnNextTest()
+    {
+    }
+
     public DefaultReporterFactory getDefaultReporterFactory()
     {
         return defaultReporterFactory;
@@ -162,6 +166,9 @@ public class ForkClient
                 case ForkingRunListener.BOOTERCODE_BYE:
                     saidGoodBye = true;
                     break;
+                case ForkingRunListener.BOOTERCODE_STOP_ON_NEXT_TEST:
+                    stopOnNextTest();
+                    break;
                 default:
                     System.out.println( s );
             }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
index b7bcadb..497a316 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
@@ -217,6 +217,10 @@ public class TestSetRunListener
         clearCapture();
     }
 
+    public void testExecutionSkippedByUser()
+    {
+    }
+
     public void testAssumptionFailure( ReportEntry report )
     {
         testSkipped( report );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 113e2de..9c8cc07 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
@@ -237,7 +237,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 
HashMap<String, String>(), aTestTyped,
-                readTestsFromInStream, cli );
+                readTestsFromInStream, cli, 0 );
     }
 
     private StartupConfiguration getTestStartupConfiguration( 
ClassLoaderConfiguration classLoaderConfiguration )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 89c1f3e..6f232fa 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
@@ -144,7 +144,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 
HashMap<String, String>(),
-                BooterDeserializerProviderConfigurationTest.aTestTyped, true, 
cli );
+                BooterDeserializerProviderConfigurationTest.aTestTyped, true, 
cli, 0 );
     }
 
     private StartupConfiguration getTestStartupConfiguration( 
ClassLoaderConfiguration classLoaderConfiguration )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
new file mode 100644
index 0000000..3f2d221
--- /dev/null
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
@@ -0,0 +1,161 @@
+package org.apache.maven.plugin.surefire.booterclient;
+
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+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.RunListener;
+
+/**
+ * Internal tests use only.
+ */
+public class MockReporter
+    implements RunListener, ConsoleLogger, ConsoleOutputReceiver
+{
+    private final List<String> events = new ArrayList<String>();
+
+    private final List<Object> data = new ArrayList<Object>();
+
+    public static final String SET_STARTING = "SET_STARTED";
+
+    public static final String SET_COMPLETED = "SET_COMPLETED";
+
+    public static final String TEST_STARTING = "TEST_STARTED";
+
+    public static final String TEST_SUCCEEDED = "TEST_COMPLETED";
+
+    public static final String TEST_FAILED = "TEST_FAILED";
+
+    public static final String TEST_ERROR = "TEST_ERROR";
+
+    public static final String TEST_SKIPPED = "TEST_SKIPPED";
+
+    public static final String TEST_ASSUMPTION_FAIL = 
"TEST_ASSUMPTION_SKIPPED";
+
+    public static final String CONSOLE_OUTPUT = "CONSOLE_OUTPUT";
+
+    public static final String STDOUT = "STDOUT";
+
+    public static final String STDERR = "STDERR";
+
+    private final AtomicInteger testSucceeded = new AtomicInteger();
+
+    private final AtomicInteger testIgnored = new AtomicInteger();
+
+    private final AtomicInteger testFailed = new AtomicInteger();
+
+    public void testSetStarting( ReportEntry report )
+    {
+        events.add( SET_STARTING );
+        data.add( report );
+    }
+
+    public void testSetCompleted( ReportEntry report )
+    {
+        events.add( SET_COMPLETED );
+        data.add( report );
+    }
+
+    public void testStarting( ReportEntry report )
+    {
+        events.add( TEST_STARTING );
+        data.add( report );
+    }
+
+    public void testSucceeded( ReportEntry report )
+    {
+        events.add( TEST_SUCCEEDED );
+        testSucceeded.incrementAndGet();
+        data.add( report );
+    }
+
+    public void testError( ReportEntry report )
+    {
+        events.add( TEST_ERROR );
+        data.add( report );
+        testFailed.incrementAndGet();
+    }
+
+    public void testFailed( ReportEntry report )
+    {
+        events.add( TEST_FAILED );
+        data.add( report );
+        testFailed.incrementAndGet();
+    }
+
+    public void testSkipped( ReportEntry report )
+    {
+        events.add( TEST_SKIPPED );
+        data.add( report );
+        testIgnored.incrementAndGet();
+    }
+
+    public void testExecutionSkippedByUser()
+    {
+    }
+
+    public void testSkippedByUser( ReportEntry report )
+    {
+        testSkipped( report );
+    }
+
+    public List<String> getEvents()
+    {
+        return events;
+    }
+
+    public List getData()
+    {
+        return data;
+    }
+
+    public String getFirstEvent()
+    {
+        return events.get( 0 );
+    }
+
+    public ReportEntry getFirstData()
+    {
+        return (ReportEntry) data.get( 0 );
+    }
+
+    public void testAssumptionFailure( ReportEntry report )
+    {
+        events.add( TEST_ASSUMPTION_FAIL );
+        data.add( report );
+        testIgnored.incrementAndGet();
+    }
+
+    public void info( String message )
+    {
+        events.add( CONSOLE_OUTPUT );
+        data.add( message );
+    }
+
+    public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
+    {
+        events.add( stdout ? STDOUT : STDERR );
+        data.add( new String( buf, off, len ) );
+    }
+}

Reply via email to