You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2015/09/06 22:58:32 UTC

[01/17] maven-surefire git commit: [SUREFIRE] added missing interface MainCliOptionsAware

Repository: maven-surefire
Updated Branches:
  refs/heads/master e89a0ae2d -> 86b253ba4


[SUREFIRE] added missing interface MainCliOptionsAware


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

Branch: refs/heads/master
Commit: 668b1b927de52dab37e36762cb8c718474eda3f1
Parents: e89a0ae
Author: Tibor17 <ti...@lycos.com>
Authored: Fri Jul 24 23:22:35 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:57:53 2015 +0200

----------------------------------------------------------------------
 .../surefire/booter/BaseProviderFactory.java    |  2 +-
 .../surefire/booter/MainCliOptionsAware.java    | 35 ++++++++++++++++++++
 .../surefire/booter/SurefireReflector.java      | 13 ++++++++
 .../maven/surefire/booter/ProviderFactory.java  |  1 +
 4 files changed, 50 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/668b1b92/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
index a3ba5db..980f6d9 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
@@ -45,7 +45,7 @@ import java.util.Map;
  */
 public class BaseProviderFactory
     implements DirectoryScannerParametersAware, ReporterConfigurationAware, SurefireClassLoadersAware, TestRequestAware,
-    ProviderPropertiesAware, ProviderParameters, TestArtifactInfoAware, RunOrderParametersAware
+    ProviderPropertiesAware, ProviderParameters, TestArtifactInfoAware, RunOrderParametersAware, MainCliOptionsAware
 {
     private static final int ROOT_CHANNEL = 0;
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/668b1b92/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
new file mode 100644
index 0000000..b3b3c9d
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
@@ -0,0 +1,35 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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.cli.CommandLineOption;
+
+import java.util.List;
+
+/**
+ * CLI options in plugin (main) JVM process.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public interface MainCliOptionsAware
+{
+    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/668b1b92/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
index 912babe..0e53a8f 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
@@ -26,6 +26,8 @@ import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.util.List;
 import java.util.Map;
+
+import org.apache.maven.surefire.cli.CommandLineOption;
 import org.apache.maven.surefire.providerapi.ProviderParameters;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.report.ReporterFactory;
@@ -80,6 +82,8 @@ public class SurefireReflector
 
     private final Class<?> testListResolver;
 
+    private final Class<?> mainCliOptions;
+
 
     public SurefireReflector( ClassLoader surefireClassLoader )
     {
@@ -102,6 +106,7 @@ public class SurefireReflector
             runResult = surefireClassLoader.loadClass( RunResult.class.getName() );
             booterParameters = surefireClassLoader.loadClass( ProviderParameters.class.getName() );
             testListResolver = surefireClassLoader.loadClass( TestListResolver.class.getName() );
+            mainCliOptions = surefireClassLoader.loadClass( MainCliOptionsAware.class.getName() );
         }
         catch ( ClassNotFoundException e )
         {
@@ -263,6 +268,14 @@ public class SurefireReflector
         }
     }
 
+    public void setMainCliOptions( Object o, List<CommandLineOption> options )
+    {
+        if ( mainCliOptions.isAssignableFrom( o.getClass() ) )
+        {
+            ReflectionUtils.invokeSetter( o, "setMainCliOptions", List.class, options );
+        }
+    }
+
     public void setDirectoryScannerParameters( Object o, DirectoryScannerParameters dirScannerParams )
     {
         final Object param = createDirectoryScannerParameters( dirScannerParams );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/668b1b92/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
index d529980..c5d93ab 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
@@ -100,6 +100,7 @@ public class ProviderFactory
         surefireReflector.setTestArtifactInfoAware( o, providerConfiguration.getTestArtifact() );
         surefireReflector.setRunOrderParameters( o, providerConfiguration.getRunOrderParameters() );
         surefireReflector.setIfDirScannerAware( o, providerConfiguration.getDirScannerParams() );
+        surefireReflector.setMainCliOptions( o, providerConfiguration.getMainCliOptions() );
 
         Object provider = surefireReflector.instantiateProvider( startupConfiguration.getActualClassName(), o );
         currentThread.setContextClassLoader( systemClassLoader );


[07/17] maven-surefire git commit: [SUREFIRE-580] Allow "fail fast" or stop running on first failure

Posted by ti...@apache.org.
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
index 77f41be..d481bb2 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
@@ -23,6 +23,8 @@ import org.apache.maven.surefire.booter.ProviderParameterNames;
 import org.apache.maven.surefire.cli.CommandLineOption;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.testng.conf.Configurator;
+import org.apache.maven.surefire.testng.utils.FailFastListener;
+import org.apache.maven.surefire.testng.utils.Stoppable;
 import org.apache.maven.surefire.testset.TestListResolver;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.apache.maven.surefire.util.ReflectionUtils;
@@ -44,6 +46,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static org.apache.maven.surefire.util.ReflectionUtils.instantiate;
+
 /**
  * Contains utility methods for executing TestNG.
  *
@@ -69,7 +73,7 @@ public class TestNGExecutor
     public static void run( Class<?>[] testClasses, String testSourceDirectory,
                             Map<String, String> options, // string,string because TestNGMapConfigurator#configure()
                             RunListener reportManager, TestNgTestSuite suite, File reportsDirectory,
-                            TestListResolver methodFilter, List<CommandLineOption> mainCliOptions )
+                            TestListResolver methodFilter, List<CommandLineOption> mainCliOptions, boolean isFailFast )
         throws TestSetFailedException
     {
         TestNG testng = new TestNG( true );
@@ -119,7 +123,7 @@ public class TestNGExecutor
 
         testng.setXmlSuites( xmlSuites );
         configurator.configure( testng, options );
-        postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory );
+        postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory, isFailFast );
         testng.run();
     }
 
@@ -260,13 +264,14 @@ public class TestNGExecutor
 
     public static void run( List<String> suiteFiles, String testSourceDirectory,
                             Map<String, String> options, // string,string because TestNGMapConfigurator#configure()
-                            RunListener reportManager, TestNgTestSuite suite, File reportsDirectory )
+                            RunListener reportManager, TestNgTestSuite suite, File reportsDirectory,
+                            boolean isFailFast )
         throws TestSetFailedException
     {
         TestNG testng = new TestNG( true );
         Configurator configurator = getConfigurator( options.get( "testng.configurator" ) );
         configurator.configure( testng, options );
-        postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory );
+        postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory, isFailFast );
         testng.setTestSuites( suiteFiles );
         testng.run();
     }
@@ -291,8 +296,8 @@ public class TestNGExecutor
         }
     }
 
-    private static void postConfigure( TestNG testNG, String sourcePath, RunListener reportManager,
-                                       TestNgTestSuite suite, File reportsDirectory )
+    private static void postConfigure( TestNG testNG, String sourcePath, final RunListener reportManager,
+                                       TestNgTestSuite suite, File reportsDirectory, boolean skipAfterFailure )
         throws TestSetFailedException
     {
         // turn off all TestNG output
@@ -301,7 +306,22 @@ public class TestNGExecutor
         TestNGReporter reporter = createTestNGReporter( reportManager, suite );
         testNG.addListener( (Object) reporter );
 
-        // FIXME: use classifier to decide if we need to pass along the source dir (onyl for JDK14)
+        if ( skipAfterFailure )
+        {
+            ClassLoader cl = Thread.currentThread().getContextClassLoader();
+            testNG.addListener( instantiate( cl, "org.apache.maven.surefire.testng.utils.FailFastNotifier",
+                                             Object.class ) );
+            Stoppable stoppable = new Stoppable()
+            {
+                public void pleaseStop()
+                {
+                    reportManager.testExecutionSkippedByUser();
+                }
+            };
+            testNG.addListener( new FailFastListener( stoppable ) );
+        }
+
+        // FIXME: use classifier to decide if we need to pass along the source dir (only for JDK14)
         if ( sourcePath != null )
         {
             testNG.setSourcePath( sourcePath );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
index d28d6ac..8777ceb 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
@@ -24,12 +24,16 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.booter.MasterProcessListener;
+import org.apache.maven.surefire.booter.MasterProcessReader;
 import org.apache.maven.surefire.cli.CommandLineOption;
 import org.apache.maven.surefire.providerapi.AbstractProvider;
 import org.apache.maven.surefire.providerapi.ProviderParameters;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.report.ReporterFactory;
 import org.apache.maven.surefire.suite.RunResult;
+import org.apache.maven.surefire.testng.utils.FailFastEventsSingleton;
 import org.apache.maven.surefire.testset.TestListResolver;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.testset.TestSetFailedException;
@@ -37,6 +41,8 @@ import org.apache.maven.surefire.util.RunOrderCalculator;
 import org.apache.maven.surefire.util.ScanResult;
 import org.apache.maven.surefire.util.TestsToRun;
 
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
+
 /**
  * @author Kristian Rosenvold
  * @noinspection UnusedDeclaration
@@ -58,12 +64,16 @@ public class TestNGProvider
 
     private final RunOrderCalculator runOrderCalculator;
 
-    private List<CommandLineOption> mainCliOptions;
+    private final List<CommandLineOption> mainCliOptions;
+
+    private final MasterProcessReader commandsReader;
 
     private TestsToRun testsToRun;
 
     public TestNGProvider( ProviderParameters booterParameters )
     {
+        // don't start a thread in MasterProcessReader while we are in in-plugin process
+        commandsReader = booterParameters.isInsideFork() ? MasterProcessReader.getReader() : null;
         providerParameters = booterParameters;
         testClassLoader = booterParameters.getTestClassLoader();
         runOrderCalculator = booterParameters.getRunOrderCalculator();
@@ -77,44 +87,55 @@ public class TestNGProvider
     public RunResult invoke( Object forkTestSet )
         throws TestSetFailedException
     {
-
-        final ReporterFactory reporterFactory = providerParameters.getReporterFactory();
-
-        if ( isTestNGXmlTestSuite( testRequest ) )
+        try
         {
-            TestNGXmlTestSuite testNGXmlTestSuite = getXmlSuite();
-            testNGXmlTestSuite.locateTestSets( testClassLoader );
-            if ( forkTestSet != null && testRequest == null )
+            if ( isFailFast() && commandsReader != null )
             {
-                testNGXmlTestSuite.execute( (String) forkTestSet, reporterFactory );
+                registerPleaseStopListener();
             }
-            else
-            {
-                testNGXmlTestSuite.execute( reporterFactory );
-            }
-        }
-        else
-        {
-            if ( testsToRun == null )
+
+            final ReporterFactory reporterFactory = providerParameters.getReporterFactory();
+
+            if ( isTestNGXmlTestSuite( testRequest ) )
             {
-                if ( forkTestSet instanceof TestsToRun )
+                TestNGXmlTestSuite testNGXmlTestSuite = newXmlSuite();
+                testNGXmlTestSuite.locateTestSets( testClassLoader );
+                if ( forkTestSet != null && testRequest == null )
                 {
-                    testsToRun = (TestsToRun) forkTestSet;
+                    testNGXmlTestSuite.execute( (String) forkTestSet, reporterFactory );
                 }
-                else if ( forkTestSet instanceof Class )
+                else
                 {
-                    testsToRun = TestsToRun.fromClass( (Class) forkTestSet );
+                    testNGXmlTestSuite.execute( reporterFactory );
                 }
-                else
+            }
+            else
+            {
+                if ( testsToRun == null )
                 {
-                    testsToRun = scanClassPath();
+                    if ( forkTestSet instanceof TestsToRun )
+                    {
+                        testsToRun = (TestsToRun) forkTestSet;
+                    }
+                    else if ( forkTestSet instanceof Class )
+                    {
+                        testsToRun = TestsToRun.fromClass( (Class) forkTestSet );
+                    }
+                    else
+                    {
+                        testsToRun = scanClassPath();
+                    }
                 }
+                TestNGDirectoryTestSuite suite = newDirectorySuite();
+                suite.execute( testsToRun, reporterFactory );
             }
-            TestNGDirectoryTestSuite suite = getDirectorySuite();
-            suite.execute( testsToRun, reporterFactory );
-        }
 
-        return reporterFactory.close();
+            return reporterFactory.close();
+        }
+        finally
+        {
+            closeCommandsReader();
+        }
     }
 
     boolean isTestNGXmlTestSuite( TestRequest testSuiteDefinition )
@@ -123,18 +144,45 @@ public class TestNGProvider
         return suiteXmlFiles != null && !suiteXmlFiles.isEmpty() && !hasSpecificTests();
     }
 
-    private TestNGDirectoryTestSuite getDirectorySuite()
+    private boolean isFailFast()
+    {
+        return providerParameters.getSkipAfterFailureCount() > 0;
+    }
+
+    private void closeCommandsReader()
+    {
+        if ( commandsReader != null )
+        {
+            commandsReader.stop();
+        }
+    }
+
+    private MasterProcessListener registerPleaseStopListener()
+    {
+        MasterProcessListener listener = new MasterProcessListener()
+        {
+            public void update( Command command )
+            {
+                FailFastEventsSingleton.getInstance().setSkipOnNextTest();
+            }
+        };
+        commandsReader.addListener( SKIP_SINCE_NEXT_TEST, listener );
+        return listener;
+    }
+
+    private TestNGDirectoryTestSuite newDirectorySuite()
     {
         return new TestNGDirectoryTestSuite( testRequest.getTestSourceDirectory().toString(), providerProperties,
                                              reporterConfiguration.getReportsDirectory(), createMethodFilter(),
-                                             runOrderCalculator, scanResult, mainCliOptions );
+                                             runOrderCalculator, scanResult, mainCliOptions, isFailFast() );
     }
 
-    private TestNGXmlTestSuite getXmlSuite()
+    private TestNGXmlTestSuite newXmlSuite()
     {
-        return new TestNGXmlTestSuite( testRequest.getSuiteXmlFiles(), testRequest.getTestSourceDirectory().toString(),
+        return new TestNGXmlTestSuite( testRequest.getSuiteXmlFiles(),
+                                       testRequest.getTestSourceDirectory().toString(),
                                        providerProperties,
-                                       reporterConfiguration.getReportsDirectory() );
+                                       reporterConfiguration.getReportsDirectory(), isFailFast() );
     }
 
     @SuppressWarnings( "unchecked" )
@@ -144,7 +192,7 @@ public class TestNGProvider
         {
             try
             {
-                return getXmlSuite().locateTestSets( testClassLoader ).keySet();
+                return newXmlSuite().locateTestSets( testClassLoader ).keySet();
             }
             catch ( TestSetFailedException e )
             {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
index 552e482..bf105fe 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
@@ -49,6 +49,8 @@ public class TestNGXmlTestSuite
 
     private final File reportsDirectory;
 
+    private final boolean isFailFast;
+
     // Not really used
     private Map<File, String> testSets;
 
@@ -57,15 +59,13 @@ public class TestNGXmlTestSuite
      * xml file(s). The XML files are suite definitions files according to TestNG DTD.
      */
     public TestNGXmlTestSuite( List<File> suiteFiles, String testSourceDirectory, Map<String, String> confOptions,
-                               File reportsDirectory )
+                               File reportsDirectory, boolean isFailFast )
     {
         this.suiteFiles = suiteFiles;
-
         this.options = confOptions;
-
         this.testSourceDirectory = testSourceDirectory;
-
         this.reportsDirectory = reportsDirectory;
+        this.isFailFast = isFailFast;
     }
 
     public void execute( ReporterFactory reporterManagerFactory )
@@ -80,8 +80,8 @@ public class TestNGXmlTestSuite
         ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) reporter );
 
         TestNGDirectoryTestSuite.startTestSuite( reporter, this );
-        TestNGExecutor.run( this.suiteFilePaths, this.testSourceDirectory, this.options, reporter, this,
-                            reportsDirectory );
+        TestNGExecutor.run( suiteFilePaths, testSourceDirectory, options, reporter, this, reportsDirectory,
+                            isFailFast );
         TestNGDirectoryTestSuite.finishTestSuite( reporter, this );
     }
 


[08/17] maven-surefire git commit: [SUREFIRE-580] Allow "fail fast" or stop running on first failure

Posted by ti...@apache.org.
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4RunListener.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4RunListener.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4RunListener.java
index 276f3d4..b54f56c 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4RunListener.java
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4RunListener.java
@@ -31,6 +31,10 @@ import org.junit.runner.notification.Failure;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.getAnnotatedIgnoreValue;
+import static org.apache.maven.surefire.report.SimpleReportEntry.ignored;
+import static org.apache.maven.surefire.report.SimpleReportEntry.withException;
+
 /**
  * RunListener for JUnit4, delegates to our own RunListener
  *
@@ -72,10 +76,8 @@ public class JUnit4RunListener
     public void testIgnored( Description description )
         throws Exception
     {
-        final String reason = JUnit4Reflector.getAnnotatedIgnoreValue( description );
-        final SimpleReportEntry report =
-            SimpleReportEntry.ignored( getClassName( description ), description.getDisplayName(), reason );
-        reporter.testSkipped( report );
+        String reason = getAnnotatedIgnoreValue( description );
+        reporter.testSkipped( ignored( getClassName( description ), description.getDisplayName(), reason ) );
     }
 
     /**
@@ -104,33 +106,29 @@ public class JUnit4RunListener
         {
             testHeader = "Failure when constructing test";
         }
-        ReportEntry report = SimpleReportEntry.withException( getClassName( failure.getDescription() ), testHeader,
-                                                              createStackTraceWriter( failure ) );
+
+        ReportEntry report =
+            withException( getClassName( failure.getDescription() ), testHeader, createStackTraceWriter( failure ) );
 
         if ( failure.getException() instanceof AssertionError )
         {
-            this.reporter.testFailed( report );
+            reporter.testFailed( report );
         }
         else
         {
-            this.reporter.testError( report );
+            reporter.testError( report );
         }
-        failureFlag.set( true );
-    }
 
-    protected StackTraceWriter createStackTraceWriter( Failure failure )
-    {
-        return new JUnit4StackTraceWriter( failure );
+        failureFlag.set( true );
     }
 
     @SuppressWarnings( { "UnusedDeclaration" } )
     public void testAssumptionFailure( Failure failure )
     {
-        this.reporter.testAssumptionFailure( createReportEntry( failure.getDescription() ) );
+        reporter.testAssumptionFailure( createReportEntry( failure.getDescription() ) );
         failureFlag.set( true );
     }
 
-
     /**
      * Called after a specific test has finished.
      *
@@ -146,12 +144,15 @@ public class JUnit4RunListener
         }
     }
 
-    protected SimpleReportEntry createReportEntry( Description description )
+    /**
+     * Delegates to {@link RunListener#testExecutionSkippedByUser()}.
+     */
+    public void testExecutionSkippedByUser()
     {
-        return new SimpleReportEntry( getClassName( description ), description.getDisplayName() );
+        reporter.testExecutionSkippedByUser();
     }
 
-    public String getClassName( Description description )
+    private static String getClassName( Description description )
     {
         String name = extractClassName( description );
         if ( name == null || isInsaneJunitNullString( name ) )
@@ -170,34 +171,30 @@ public class JUnit4RunListener
         return name;
     }
 
-    private boolean isInsaneJunitNullString( String value )
+    protected StackTraceWriter createStackTraceWriter( Failure failure )
     {
-        return "null".equals( value );
+        return new JUnit4StackTraceWriter( failure );
+    }
+
+    protected SimpleReportEntry createReportEntry( Description description )
+    {
+        return new SimpleReportEntry( getClassName( description ), description.getDisplayName() );
     }
 
     public static String extractClassName( Description description )
     {
         String displayName = description.getDisplayName();
         Matcher m = PARENS.matcher( displayName );
-        if ( !m.find() )
-        {
-            return displayName;
-        }
-        return m.group( 1 );
+        return m.find() ? m.group( 1 ) : displayName;
     }
 
     public static String extractMethodName( Description description )
     {
         String displayName = description.getDisplayName();
         int i = displayName.indexOf( "(" );
-        if ( i >= 0 )
-        {
-            return displayName.substring( 0, i );
-        }
-        return displayName;
+        return i >= 0 ? displayName.substring( 0, i ) : displayName;
     }
 
-
     public static void rethrowAnyTestMechanismFailures( Result run )
         throws TestSetFailedException
     {
@@ -214,4 +211,9 @@ public class JUnit4RunListener
             }
         }
     }
+
+    private static boolean isInsaneJunitNullString( String value )
+    {
+        return "null".equals( value );
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java
new file mode 100644
index 0000000..e9136e1
--- /dev/null
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java
@@ -0,0 +1,106 @@
+package org.apache.maven.surefire.common.junit4;
+
+/*
+ * 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.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * Extends {@link RunNotifier JUnit notifier},
+ * encapsulates several different types of {@link RunListener JUnit listeners}, and
+ * fires events to listeners.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public class Notifier
+    extends RunNotifier
+{
+    private final Collection<RunListener> listeners = new ArrayList<RunListener>();
+
+    private JUnit4RunListener reporter;
+
+    @Override
+    public void pleaseStop()
+    {
+        super.pleaseStop();
+        reporter.testExecutionSkippedByUser();
+    }
+
+    /**
+     * Adds reporter listener to the bottom of queue and retrieves old reporter if any exists.
+     *
+     * @param reporter registered listener
+     * @return old listener; or null if did not exist
+     */
+    public JUnit4RunListener setReporter( JUnit4RunListener reporter )
+    {
+        JUnit4RunListener old = this.reporter;
+        this.reporter = reporter;
+        addListener( reporter );
+        return old;
+    }
+
+    @Override
+    public void addListener( RunListener listener )
+    {
+        listeners.add( listener );
+        super.addListener( listener );
+    }
+
+    public Notifier addListeners( Collection<RunListener> given )
+    {
+        for ( RunListener listener : given )
+        {
+            addListener( listener );
+        }
+        return this;
+    }
+
+    public Notifier addListeners( RunListener... given )
+    {
+        for ( RunListener listener : given )
+        {
+            addListener( listener );
+        }
+        return this;
+    }
+
+    @Override
+    public void removeListener( RunListener listener )
+    {
+        listeners.remove( listener );
+        super.removeListener( listener );
+    }
+
+    public void removeListeners()
+    {
+        for ( Iterator<RunListener> it = listeners.iterator(); it.hasNext(); )
+        {
+            RunListener listener = it.next();
+            it.remove();
+            super.removeListener( listener );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/junit4/MockReporter.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/junit4/MockReporter.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/junit4/MockReporter.java
index 04484e0..99d7aae 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/junit4/MockReporter.java
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/junit4/MockReporter.java
@@ -26,7 +26,7 @@ import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
 
 /**
- * Internal use only
+ * Internal tests use only.
  */
 public class MockReporter
     implements RunListener
@@ -83,6 +83,15 @@ public class MockReporter
         testIgnored.incrementAndGet();
     }
 
+    public void testExecutionSkippedByUser()
+    {
+    }
+
+    public void testSkippedByUser( ReportEntry report )
+    {
+        testSkipped( report );
+    }
+
     public int getTestSucceeded()
     {
         return testSucceeded.get();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector40Test.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector40Test.java b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector40Test.java
index 28b5fed..df4819c 100644
--- a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector40Test.java
+++ b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector40Test.java
@@ -47,6 +47,34 @@ public class JUnit4Reflector40Test
         {
         }
     }
+
+    public void testCreateIgnored()
+    {
+        Ignore ignore = JUnit4Reflector.createIgnored( "error" );
+        assertNotNull( ignore );
+        assertNotNull( ignore.value() );
+        assertEquals( "error", ignore.value() );
+    }
+
+    public void testCreateDescription()
+    {
+        Ignore ignore = JUnit4Reflector.createIgnored( "error" );
+        Description description = JUnit4Reflector.createDescription( "exception", ignore );
+        assertEquals( "exception", description.getDisplayName() );
+        assertEquals( "exception", description.toString() );
+        assertEquals( 0, description.getChildren().size() );
+        // JUnit 4 description does not get annotations
+        Ignore annotatedIgnore = JUnit4Reflector.getAnnotatedIgnore( description );
+        assertNull( annotatedIgnore );
+    }
+
+    public void testCreatePureDescription()
+    {
+        Description description = JUnit4Reflector.createDescription( "exception" );
+        assertEquals( "exception", description.getDisplayName() );
+        assertEquals( "exception", description.toString() );
+        assertEquals( 0, description.getChildren().size() );
+    }
 }
 
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java b/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java
index aac3655..9ca397e 100644
--- a/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java
+++ b/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java
@@ -100,6 +100,15 @@ public class JUnitTestSetTest
             throw new IllegalStateException();
         }
 
+        public void testExecutionSkippedByUser()
+        {
+        }
+
+        public void testSkippedByUser( ReportEntry report )
+        {
+            testSkipped( report );
+        }
+
         public List getSucceededTests()
         {
             return succeededTests;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4FailFastListener.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4FailFastListener.java b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4FailFastListener.java
new file mode 100644
index 0000000..d45b678
--- /dev/null
+++ b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4FailFastListener.java
@@ -0,0 +1,48 @@
+package org.apache.maven.surefire.junit4;
+
+/*
+ * 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.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
+
+/**
+ * Calling {@link RunNotifier#pleaseStop()} if failure appeared.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+final class JUnit4FailFastListener
+    extends RunListener
+{
+    private final RunNotifier notifier;
+
+    JUnit4FailFastListener( RunNotifier notifier )
+    {
+        this.notifier = notifier;
+    }
+
+    @Override
+    public void testFailure( Failure failure )
+        throws Exception
+    {
+        notifier.pleaseStop();
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 3f0e57f..e8f041d 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
@@ -19,22 +19,20 @@ package org.apache.maven.surefire.junit4;
  * under the License.
  */
 
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
 import java.util.Set;
 
+import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.booter.MasterProcessListener;
+import org.apache.maven.surefire.booter.MasterProcessReader;
 import org.apache.maven.surefire.common.junit4.ClassMethod;
-import org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil;
-import org.apache.maven.surefire.common.junit4.JUnit4Reflector;
 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
-import org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory;
 import org.apache.maven.surefire.common.junit4.JUnit4TestChecker;
 import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
+import org.apache.maven.surefire.common.junit4.Notifier;
 import org.apache.maven.surefire.providerapi.AbstractProvider;
 import org.apache.maven.surefire.providerapi.ProviderParameters;
-import org.apache.maven.surefire.report.ConsoleOutputCapture;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.PojoStackTraceWriter;
 import org.apache.maven.surefire.report.ReportEntry;
@@ -49,13 +47,28 @@ import org.apache.maven.surefire.util.RunOrderCalculator;
 import org.apache.maven.surefire.util.ScanResult;
 import org.apache.maven.surefire.util.TestsToRun;
 import org.junit.runner.Description;
-import org.junit.runner.Request;
 import org.junit.runner.Result;
 import org.junit.runner.Runner;
 import org.junit.runner.manipulation.Filter;
-import org.junit.runner.notification.RunNotifier;
-
+import org.junit.runner.notification.StoppedByUserException;
+
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.createSuiteDescription;
+import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.cutTestClassAndMethod;
+import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.generateFailingTests;
+import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createDescription;
+import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createIgnored;
+import static org.apache.maven.surefire.common.junit4.JUnit4RunListener.rethrowAnyTestMechanismFailures;
+import static org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory.createCustomListeners;
+import static org.apache.maven.surefire.report.ConsoleOutputCapture.startCapture;
+import static org.apache.maven.surefire.report.SimpleReportEntry.withException;
 import static org.apache.maven.surefire.testset.TestListResolver.toClassFileName;
+import static org.apache.maven.surefire.util.TestsToRun.fromClass;
+import static org.junit.runner.Request.aClass;
+import static org.junit.runner.Request.method;
+import static java.lang.reflect.Modifier.isAbstract;
+import static java.lang.reflect.Modifier.isInterface;
+import static java.util.Collections.unmodifiableCollection;
 
 /**
  * @author Kristian Rosenvold
@@ -67,7 +80,7 @@ public class JUnit4Provider
 
     private final ClassLoader testClassLoader;
 
-    private final List<org.junit.runner.notification.RunListener> customRunListeners;
+    private final Collection<org.junit.runner.notification.RunListener> customRunListeners;
 
     private final JUnit4TestChecker jUnit4TestChecker;
 
@@ -81,16 +94,20 @@ public class JUnit4Provider
 
     private final int rerunFailingTestsCount;
 
+    private final MasterProcessReader commandsReader;
+
     private TestsToRun testsToRun;
 
     public JUnit4Provider( ProviderParameters booterParameters )
     {
+        // don't start a thread in MasterProcessReader while we are in in-plugin process
+        commandsReader = booterParameters.isInsideFork() ? MasterProcessReader.getReader() : null;
         providerParameters = booterParameters;
         testClassLoader = booterParameters.getTestClassLoader();
         scanResult = booterParameters.getScanResult();
         runOrderCalculator = booterParameters.getRunOrderCalculator();
         String listeners = booterParameters.getProviderProperties().get( "listener" );
-        customRunListeners = JUnit4RunListenerFactory.createCustomListeners( listeners );
+        customRunListeners = unmodifiableCollection( createCustomListeners( listeners ) );
         jUnit4TestChecker = new JUnit4TestChecker( testClassLoader );
         TestRequest testRequest = booterParameters.getTestRequest();
         testResolver = testRequest.getTestListResolver();
@@ -100,6 +117,11 @@ public class JUnit4Provider
     public RunResult invoke( Object forkTestSet )
         throws TestSetFailedException
     {
+        if ( isRerunFailingTests() && isFailFast() )
+        {
+            throw new TestSetFailedException( "don't enable parameters rerunFailingTestsCount, skipAfterFailureCount" );
+        }
+
         if ( testsToRun == null )
         {
             if ( forkTestSet instanceof TestsToRun )
@@ -108,7 +130,7 @@ public class JUnit4Provider
             }
             else if ( forkTestSet instanceof Class )
             {
-                testsToRun = TestsToRun.fromClass( (Class) forkTestSet );
+                testsToRun = fromClass( (Class) forkTestSet );
             }
             else
             {
@@ -118,48 +140,102 @@ public class JUnit4Provider
 
         upgradeCheck();
 
-        final ReporterFactory reporterFactory = providerParameters.getReporterFactory();
+        ReporterFactory reporterFactory = providerParameters.getReporterFactory();
 
         RunListener reporter = reporterFactory.createReporter();
 
-        ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) reporter );
-
-        JUnit4RunListener jUnit4TestSetReporter = new JUnit4RunListener( reporter );
+        startCapture( (ConsoleOutputReceiver) reporter );
 
+        Notifier notifier = new Notifier();
+        notifier.setReporter( new JUnit4RunListener( reporter ) );
+        if ( isFailFast() )
+        {
+            notifier.addListener( new JUnit4FailFastListener( notifier ) );
+        }
         Result result = new Result();
-        RunNotifier runNotifier = getRunNotifier( jUnit4TestSetReporter, result, customRunListeners );
-
-        runNotifier.fireTestRunStarted( testsToRun.allowEagerReading()
-                                            ? createTestsDescription()
-                                            : JUnit4Reflector.createDescription( UNDETERMINED_TESTS_DESCRIPTION ) );
+        notifier.addListeners( customRunListeners )
+            .addListener( result.createListener() );
 
-        for ( Class aTestsToRun : testsToRun )
+        if ( isFailFast() && commandsReader != null )
         {
-            executeTestSet( aTestsToRun, reporter, runNotifier );
+            registerPleaseStopJunitListener( notifier );
         }
 
-        runNotifier.fireTestRunFinished( result );
+        try
+        {
+            notifier.fireTestRunStarted( testsToRun.allowEagerReading()
+                                                ? createTestsDescription()
+                                                : createDescription( UNDETERMINED_TESTS_DESCRIPTION ) );
 
-        JUnit4RunListener.rethrowAnyTestMechanismFailures( result );
+            for ( Class aTestsToRun : testsToRun )
+            {
+                executeTestSet( aTestsToRun, reporter, notifier );
+            }
+        }
+        finally
+        {
+            notifier.fireTestRunFinished( result );
+            notifier.removeListeners();
+            closeCommandsReader();
+        }
 
-        closeRunNotifier( jUnit4TestSetReporter, customRunListeners );
+        rethrowAnyTestMechanismFailures( result );
         return reporterFactory.close();
     }
 
-    private void executeTestSet( Class<?> clazz, RunListener reporter, RunNotifier listeners )
+    private boolean isRerunFailingTests()
+    {
+        return rerunFailingTestsCount > 0;
+    }
+
+    private boolean isFailFast()
+    {
+        return providerParameters.getSkipAfterFailureCount() > 0;
+    }
+
+    private void closeCommandsReader()
+    {
+        if ( commandsReader != null )
+        {
+            commandsReader.stop();
+        }
+    }
+
+    private MasterProcessListener registerPleaseStopJunitListener( final Notifier notifier )
+    {
+        MasterProcessListener listener = new MasterProcessListener()
+        {
+            public void update( Command command )
+            {
+                notifier.pleaseStop();
+            }
+        };
+        commandsReader.addListener( SKIP_SINCE_NEXT_TEST, listener );
+        return listener;
+    }
+
+    private void executeTestSet( Class<?> clazz, RunListener reporter, Notifier notifier )
     {
         final ReportEntry report = new SimpleReportEntry( getClass().getName(), clazz.getName() );
         reporter.testSetStarting( report );
         try
         {
-            executeWithRerun( clazz, listeners );
+            executeWithRerun( clazz, notifier );
         }
         catch ( Throwable e )
         {
-            String reportName = report.getName();
-            String reportSourceName = report.getSourceName();
-            PojoStackTraceWriter stackWriter = new PojoStackTraceWriter( reportSourceName, reportName, e );
-            reporter.testError( SimpleReportEntry.withException( reportSourceName, reportName, stackWriter ) );
+            if ( isFailFast() && e instanceof StoppedByUserException )
+            {
+                String reason = e.getClass().getName();
+                notifier.fireTestIgnored( createDescription( clazz.getName(), createIgnored( reason ) ) );
+            }
+            else
+            {
+                String reportName = report.getName();
+                String reportSourceName = report.getSourceName();
+                PojoStackTraceWriter stackWriter = new PojoStackTraceWriter( reportSourceName, reportName, e );
+                reporter.testError( withException( reportSourceName, reportName, stackWriter ) );
+            }
         }
         finally
         {
@@ -167,55 +243,28 @@ public class JUnit4Provider
         }
     }
 
-    private void executeWithRerun( Class<?> clazz, RunNotifier listeners ) throws TestSetFailedException
+    private void executeWithRerun( Class<?> clazz, Notifier notifier ) throws TestSetFailedException
     {
         JUnitTestFailureListener failureListener = new JUnitTestFailureListener();
-        listeners.addListener( failureListener );
+        notifier.addListener( failureListener );
         boolean hasMethodFilter = testResolver != null && testResolver.hasMethodPatterns();
-        execute( clazz, listeners, hasMethodFilter ? new TestResolverFilter() : new NullFilter() );
+        execute( clazz, notifier, hasMethodFilter ? new TestResolverFilter() : new NullFilter() );
 
         // Rerun failing tests if rerunFailingTestsCount is larger than 0
-        if ( rerunFailingTestsCount > 0 )
+        if ( isRerunFailingTests() )
         {
             for ( int i = 0; i < rerunFailingTestsCount && !failureListener.getAllFailures().isEmpty(); i++ )
             {
-                Set<ClassMethod> failedTests =
-                    JUnit4ProviderUtil.generateFailingTests( failureListener.getAllFailures() );
+                Set<ClassMethod> failedTests = generateFailingTests( failureListener.getAllFailures() );
                 failureListener.reset();
                 if ( !failedTests.isEmpty() )
                 {
-                    executeFailedMethod( listeners, failedTests );
+                    executeFailedMethod( notifier, failedTests );
                 }
             }
         }
     }
 
-    private static RunNotifier getRunNotifier( org.junit.runner.notification.RunListener main, Result result,
-                                        List<org.junit.runner.notification.RunListener> others )
-    {
-        RunNotifier notifier = new RunNotifier();
-        notifier.addListener( main );
-        notifier.addListener( result.createListener() );
-        for ( org.junit.runner.notification.RunListener listener : others )
-        {
-            notifier.addListener( listener );
-        }
-        return notifier;
-    }
-
-    // I am not entirely sure as to why we do this explicit freeing, it's one of those
-    // pieces of code that just seem to linger on in here ;)
-    private static void closeRunNotifier( org.junit.runner.notification.RunListener main,
-                                   Iterable<org.junit.runner.notification.RunListener> others )
-    {
-        RunNotifier notifier = new RunNotifier();
-        notifier.removeListener( main );
-        for ( org.junit.runner.notification.RunListener listener : others )
-        {
-            notifier.removeListener( listener );
-        }
-    }
-
     public Iterable<Class<?>> getSuites()
     {
         testsToRun = scanClassPath();
@@ -258,7 +307,7 @@ public class JUnit4Provider
         {
             classes.add( clazz );
         }
-        return JUnit4ProviderUtil.createSuiteDescription( classes );
+        return createSuiteDescription( classes );
     }
 
     private static boolean isJUnit4UpgradeCheck()
@@ -266,12 +315,12 @@ public class JUnit4Provider
         return System.getProperty( "surefire.junit4.upgradecheck" ) != null;
     }
 
-    private static void execute( Class<?> testClass, RunNotifier notifier, Filter filter )
+    private static void execute( Class<?> testClass, Notifier notifier, Filter filter )
     {
         final int classModifiers = testClass.getModifiers();
-        if ( !Modifier.isAbstract( classModifiers ) && !Modifier.isInterface( classModifiers ) )
+        if ( !isAbstract( classModifiers ) && !isInterface( classModifiers ) )
         {
-            Runner runner = Request.aClass( testClass ).filterWith( filter ).getRunner();
+            Runner runner = aClass( testClass ).filterWith( filter ).getRunner();
             if ( countTestsInRunner( runner.getDescription() ) != 0 )
             {
                 runner.run( notifier );
@@ -279,7 +328,7 @@ public class JUnit4Provider
         }
     }
 
-    private void executeFailedMethod( RunNotifier notifier, Set<ClassMethod> failedMethods )
+    private void executeFailedMethod( Notifier notifier, Set<ClassMethod> failedMethods )
         throws TestSetFailedException
     {
         for ( ClassMethod failedMethod : failedMethods )
@@ -288,7 +337,7 @@ public class JUnit4Provider
             {
                 Class<?> methodClass = Class.forName( failedMethod.getClazz(), true, testClassLoader );
                 String methodName = failedMethod.getMethod();
-                Request.method( methodClass, methodName ).getRunner().run( notifier );
+                method( methodClass, methodName ).getRunner().run( notifier );
             }
             catch ( ClassNotFoundException e )
             {
@@ -353,7 +402,7 @@ public class JUnit4Provider
         public boolean shouldRun( Description description )
         {
             // class: Java class name; method: 1. "testMethod" or 2. "testMethod[5+whatever]" in @Parameterized
-            final ClassMethod cm = JUnit4ProviderUtil.cutTestClassAndMethod( description );
+            final ClassMethod cm = cutTestClassAndMethod( description );
             final boolean isSuite = description.isSuite();
             final boolean isValidTest = description.isTest() && cm.isValid();
             final String clazz = cm.getClazz();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConcurrentRunListener.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConcurrentRunListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConcurrentRunListener.java
index 7254364..5886317 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConcurrentRunListener.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConcurrentRunListener.java
@@ -113,6 +113,12 @@ public abstract class ConcurrentRunListener
         testMethod.detachFromCurrentThread();
     }
 
+    public void testExecutionSkippedByUser()
+    {
+        // cannot guarantee proper call to all listeners
+        reporterManagerThreadLocal.get().testExecutionSkippedByUser();
+    }
+
     public void testAssumptionFailure( ReportEntry failure )
     {
         final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
@@ -180,19 +186,15 @@ public abstract class ConcurrentRunListener
         return reporterManagerThreadLocal.get();
     }
 
-
     public static ConcurrentRunListener createInstance( Map<String, TestSet> classMethodCounts,
-                                                            ReporterFactory reporterManagerFactory,
+                                                            ReporterFactory reporterFactory,
                                                             boolean parallelClasses, boolean parallelBoth,
                                                             ConsoleLogger consoleLogger )
         throws TestSetFailedException
     {
-        if ( parallelClasses )
-        {
-            return new ClassesParallelRunListener( classMethodCounts, reporterManagerFactory, consoleLogger );
-        }
-        return new MethodsParallelRunListener( classMethodCounts, reporterManagerFactory, !parallelBoth,
-                                               consoleLogger );
+        return parallelClasses
+            ? new ClassesParallelRunListener( classMethodCounts, reporterFactory, consoleLogger )
+            : new MethodsParallelRunListener( classMethodCounts, reporterFactory, !parallelBoth, consoleLogger );
     }
 
 
@@ -210,5 +212,4 @@ public abstract class ConcurrentRunListener
             consoleLogger.info( new String( buf, off, len ) );
         }
     }
-
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/FilteringRequest.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/FilteringRequest.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/FilteringRequest.java
new file mode 100644
index 0000000..5e1c192
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/FilteringRequest.java
@@ -0,0 +1,54 @@
+package org.apache.maven.surefire.junitcore;
+
+/*
+ * 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.junit.runner.Request;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.NoTestsRemainException;
+
+/**
+ * Moved nested class from {@link JUnitCoreWrapper}.
+ */
+final class FilteringRequest
+    extends Request
+{
+    private Runner filteredRunner;
+
+    public FilteringRequest( Request req, Filter filter )
+    {
+        try
+        {
+            Runner runner = req.getRunner();
+            filter.apply( runner );
+            filteredRunner = runner;
+        }
+        catch ( NoTestsRemainException e )
+        {
+            filteredRunner = null;
+        }
+    }
+
+    @Override
+    public Runner getRunner()
+    {
+        return filteredRunner;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnit47FailFastListener.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnit47FailFastListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnit47FailFastListener.java
new file mode 100644
index 0000000..dda6b68
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnit47FailFastListener.java
@@ -0,0 +1,65 @@
+package org.apache.maven.surefire.junitcore;
+
+/*
+ * 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.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * Calling {@link Stoppable#pleaseStop()} if failure appeared.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+final class JUnit47FailFastListener
+    extends RunListener
+{
+    private final Stoppable stoppable;
+
+    private final ConcurrentLinkedQueue<String> testClassNames = new ConcurrentLinkedQueue<String>();
+
+    JUnit47FailFastListener( Stoppable stoppable )
+    {
+        this.stoppable = stoppable;
+    }
+
+    Queue<String> getRemainingTestClasses()
+    {
+        return testClassNames;
+    }
+
+    @Override
+    public void testStarted( Description description )
+        throws Exception
+    {
+        testClassNames.remove( description.getClassName() );
+    }
+
+    @Override
+    public void testFailure( Failure failure )
+        throws Exception
+    {
+        stoppable.pleaseStop();
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCore.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCore.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCore.java
new file mode 100644
index 0000000..7f1a03a
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCore.java
@@ -0,0 +1,90 @@
+package org.apache.maven.surefire.junitcore;
+
+/*
+ * 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.common.junit4.Notifier;
+import org.apache.maven.surefire.testset.TestSetFailedException;
+import org.junit.runner.Result;
+import org.junit.runner.Runner;
+import org.junit.runner.notification.RunListener;
+
+/**
+ * JUnitCore solves bugs in original junit class {@link org.junit.runner.JUnitCore}.<p>
+ * The notifier method {@link org.junit.runner.notification.RunNotifier#fireTestRunFinished}
+ * is called anyway in finally block.
+ * This class provides method {@link #pleaseStop()} without any need to retrieve
+ * {@link org.junit.runner.notification.RunNotifier} outside.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ * @see https://github.com/junit-team/junit/issues/1186
+ */
+class JUnitCore
+    implements Stoppable
+{
+    private final Notifier notifier;
+
+    JUnitCore( Notifier notifier )
+    {
+        this.notifier = notifier;
+    }
+
+    Result run( Runner runner )
+        throws TestSetFailedException
+    {
+        Result result = new Result();
+        RunListener listener = result.createListener();
+        notifier.addFirstListener( listener );
+        try
+        {
+            notifier.fireTestRunStarted( runner.getDescription() );
+            runner.run( notifier );
+        }
+        catch ( Throwable e )
+        {
+            afterException( e );
+        }
+        finally
+        {
+            notifier.fireTestRunFinished( result );
+            notifier.removeListener( listener );
+            afterFinished();
+        }
+        return result;
+    }
+
+    protected void afterException( Throwable e )
+        throws TestSetFailedException
+    {
+        throw new TestSetFailedException( e );
+    }
+
+    protected void afterFinished()
+    {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void pleaseStop()
+    {
+        notifier.pleaseStop();
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 43b8a1e..243cca0 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
@@ -19,14 +19,18 @@ package org.apache.maven.surefire.junitcore;
  * under the License.
  */
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil;
-import org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory;
+import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.booter.MasterProcessListener;
+import org.apache.maven.surefire.booter.MasterProcessReader;
+import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
 import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
+import org.apache.maven.surefire.common.junit4.Notifier;
 import org.apache.maven.surefire.common.junit48.FilterFactory;
 import org.apache.maven.surefire.common.junit48.JUnit48Reflector;
 import org.apache.maven.surefire.common.junit48.JUnit48TestChecker;
@@ -34,9 +38,7 @@ import org.apache.maven.surefire.providerapi.AbstractProvider;
 import org.apache.maven.surefire.providerapi.ProviderParameters;
 import org.apache.maven.surefire.report.ConsoleLogger;
 import org.apache.maven.surefire.report.ConsoleOutputCapture;
-import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReporterFactory;
-import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.suite.RunResult;
 import org.apache.maven.surefire.testset.TestListResolver;
 import org.apache.maven.surefire.testset.TestSetFailedException;
@@ -45,6 +47,14 @@ import org.apache.maven.surefire.util.ScanResult;
 import org.apache.maven.surefire.util.ScannerFilter;
 import org.apache.maven.surefire.util.TestsToRun;
 import org.junit.runner.manipulation.Filter;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.junitcore.ConcurrentRunListener.createInstance;
+import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.generateFailingTests;
+import static org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory.createCustomListeners;
+import static java.util.Collections.unmodifiableCollection;
 
 /**
  * @author Kristian Rosenvold
@@ -59,7 +69,7 @@ public class JUnitCoreProvider
 
     private final ScannerFilter scannerFilter;
 
-    private final List<org.junit.runner.notification.RunListener> customRunListeners;
+    private final Collection<RunListener> customRunListeners;
 
     private final ProviderParameters providerParameters;
 
@@ -67,16 +77,19 @@ public class JUnitCoreProvider
 
     private final int rerunFailingTestsCount;
 
-    private TestsToRun testsToRun;
-
     private final JUnit48Reflector jUnit48Reflector;
 
     private final RunOrderCalculator runOrderCalculator;
 
     private final TestListResolver testResolver;
 
+    private final MasterProcessReader commandsReader;
+
+    private TestsToRun testsToRun;
+
     public JUnitCoreProvider( ProviderParameters providerParameters )
     {
+        commandsReader = providerParameters.isInsideFork() ? MasterProcessReader.getReader() : null;
         this.providerParameters = providerParameters;
         testClassLoader = providerParameters.getTestClassLoader();
         scanResult = providerParameters.getScanResult();
@@ -85,8 +98,8 @@ public class JUnitCoreProvider
         scannerFilter = new JUnit48TestChecker( testClassLoader );
         testResolver = providerParameters.getTestRequest().getTestListResolver();
         rerunFailingTestsCount = providerParameters.getTestRequest().getRerunFailingTestsCount();
-        customRunListeners = JUnit4RunListenerFactory.createCustomListeners(
-            providerParameters.getProviderProperties().get( "listener" ) );
+        String listeners = providerParameters.getProviderProperties().get( "listener" );
+        customRunListeners = unmodifiableCollection( createCustomListeners( listeners ) );
         jUnit48Reflector = new JUnit48Reflector( testClassLoader );
     }
 
@@ -104,6 +117,11 @@ public class JUnitCoreProvider
     public RunResult invoke( Object forkTestSet )
         throws TestSetFailedException
     {
+        if ( isRerunFailingTests() && isFailFast() )
+        {
+            throw new TestSetFailedException( "don't enable parameters rerunFailingTestsCount, skipAfterFailureCount" );
+        }
+
         final ReporterFactory reporterFactory = providerParameters.getReporterFactory();
 
         final ConsoleLogger consoleLogger = providerParameters.getConsoleLogger();
@@ -127,55 +145,96 @@ public class JUnitCoreProvider
             }
         }
 
-        customRunListeners.add( 0, getRunListener( reporterFactory, consoleLogger ) );
+        Notifier notifier = new Notifier();
+        notifier.setReporter( createRunListener( reporterFactory, consoleLogger ) );
 
         // Add test failure listener
         JUnitTestFailureListener testFailureListener = new JUnitTestFailureListener();
-        customRunListeners.add( 0, testFailureListener );
+        notifier.addListener( testFailureListener );
 
-        JUnitCoreWrapper.execute( consoleLogger, testsToRun, jUnitCoreParameters, customRunListeners, filter );
+        if ( isFailFast() && commandsReader != null )
+        {
+            registerPleaseStopJunitListener( notifier );
+        }
 
-        // Rerun failing tests if rerunFailingTestsCount is larger than 0
-        if ( rerunFailingTestsCount > 0 )
+        try
         {
-            for ( int i = 0; i < rerunFailingTestsCount && !testFailureListener.getAllFailures().isEmpty(); i++ )
+            JUnitCoreWrapper core = new JUnitCoreWrapper( notifier, jUnitCoreParameters, consoleLogger, isFailFast() );
+
+            core.execute( testsToRun, customRunListeners, filter );
+
+            // Rerun failing tests if rerunFailingTestsCount is larger than 0
+            if ( isRerunFailingTests() )
             {
-                Map<Class<?>, Set<String>> failingTests =
-                    JUnit4ProviderUtil.generateFailingTests( testFailureListener.getAllFailures(), testClassLoader );
-                testFailureListener.reset();
-                final FilterFactory filterFactory = new FilterFactory( testClassLoader );
-                Filter failingMethodsFilter = filterFactory.createFailingMethodFilter( failingTests );
-                JUnitCoreWrapper.execute( consoleLogger, testsToRun, jUnitCoreParameters, customRunListeners,
-                                          failingMethodsFilter );
+                for ( int i = 0; i < rerunFailingTestsCount && !testFailureListener.getAllFailures().isEmpty(); i++ )
+                {
+                    List<Failure> failures = testFailureListener.getAllFailures();
+                    Map<Class<?>, Set<String>> failingTests = generateFailingTests( failures, testClassLoader );
+                    testFailureListener.reset();
+                    final FilterFactory filterFactory = new FilterFactory( testClassLoader );
+                    Filter failingMethodsFilter = filterFactory.createFailingMethodFilter( failingTests );
+                    core.execute( testsToRun, failingMethodsFilter );
+                }
             }
         }
+        finally
+        {
+            notifier.removeListeners();
+            closeCommandsReader();
+        }
         return reporterFactory.close();
     }
 
-    private org.junit.runner.notification.RunListener getRunListener( ReporterFactory reporterFactory,
-                                                                      ConsoleLogger consoleLogger )
+    private boolean isRerunFailingTests()
+    {
+        return rerunFailingTestsCount > 0;
+    }
+
+    private boolean isFailFast()
+    {
+        return providerParameters.getSkipAfterFailureCount() > 0;
+    }
+
+    private void closeCommandsReader()
+    {
+        if ( commandsReader != null )
+        {
+            commandsReader.stop();
+        }
+    }
+
+    private MasterProcessListener registerPleaseStopJunitListener( final Notifier stoppable )
+    {
+        MasterProcessListener listener = new MasterProcessListener()
+        {
+            public void update( Command command )
+            {
+                stoppable.pleaseStop();
+            }
+        };
+        commandsReader.addListener( SKIP_SINCE_NEXT_TEST, listener );
+        return listener;
+    }
+
+    private JUnit4RunListener createRunListener( ReporterFactory reporterFactory, ConsoleLogger consoleLogger )
         throws TestSetFailedException
     {
-        org.junit.runner.notification.RunListener jUnit4RunListener;
         if ( isSingleThreaded() )
         {
             NonConcurrentRunListener rm = new NonConcurrentRunListener( reporterFactory.createReporter() );
             ConsoleOutputCapture.startCapture( rm );
-            jUnit4RunListener = rm;
+            return rm;
         }
         else
         {
             final Map<String, TestSet> testSetMap = new ConcurrentHashMap<String, TestSet>();
 
-            RunListener listener =
-                ConcurrentRunListener.createInstance( testSetMap, reporterFactory,
-                                                      isParallelTypes(),
-                                                      isParallelMethodsAndTypes(), consoleLogger );
-            ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) listener );
+            ConcurrentRunListener listener = createInstance( testSetMap, reporterFactory, isParallelTypes(),
+                                                             isParallelMethodsAndTypes(), consoleLogger );
+            ConsoleOutputCapture.startCapture( listener );
 
-            jUnit4RunListener = new JUnitCoreRunListener( listener, testSetMap );
+            return new JUnitCoreRunListener( listener, testSetMap );
         }
-        return jUnit4RunListener;
     }
 
     private boolean isParallelMethodsAndTypes()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 bf2f71a..e0c9f9e 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,79 +19,103 @@ package org.apache.maven.surefire.junitcore;
  * under the License.
  */
 
-import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
+import org.apache.maven.surefire.common.junit4.Notifier;
 import org.apache.maven.surefire.junitcore.pc.ParallelComputer;
 import org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder;
 import org.apache.maven.surefire.report.ConsoleLogger;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.apache.maven.surefire.util.TestsToRun;
+import org.junit.Ignore;
 import org.junit.runner.Computer;
-import org.junit.runner.JUnitCore;
 import org.junit.runner.Request;
 import org.junit.runner.Result;
-import org.junit.runner.Runner;
 import org.junit.runner.manipulation.Filter;
-import org.junit.runner.manipulation.NoTestsRemainException;
 import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.StoppedByUserException;
 
-import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Queue;
+import java.util.Random;
+
+import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createDescription;
+import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createIgnored;
+import static org.apache.maven.surefire.common.junit4.JUnit4RunListener.rethrowAnyTestMechanismFailures;
 
 /**
  * Encapsulates access to JUnitCore
  *
  * @author Kristian Rosenvold
  */
-
-class JUnitCoreWrapper
+final class JUnitCoreWrapper
 {
-    public static void execute( ConsoleLogger logger, TestsToRun testsToRun, JUnitCoreParameters jUnitCoreParameters,
-                                List<RunListener> listeners, Filter filter )
+    private final Notifier notifier;
+    private final JUnitCoreParameters jUnitCoreParameters;
+    private final ConsoleLogger logger;
+    private final boolean failFast;
+
+    private Object o = new Random().nextInt();
+
+    JUnitCoreWrapper( Notifier notifier, JUnitCoreParameters jUnitCoreParameters, ConsoleLogger logger,
+                      boolean failFast )
+    {
+        this.notifier = notifier;
+        this.jUnitCoreParameters = jUnitCoreParameters;
+        this.logger = logger;
+        this.failFast = failFast;
+    }
+
+    void execute( TestsToRun testsToRun, Filter filter )
+        throws TestSetFailedException
+    {
+        execute( testsToRun, Collections.<RunListener>emptyList(), filter );
+    }
+
+    void execute( TestsToRun testsToRun, Collection<RunListener> listeners, Filter filter )
         throws TestSetFailedException
     {
-        JUnitCore junitCore = createJUnitCore( listeners );
         if ( testsToRun.allowEagerReading() )
         {
-            executeEager( logger, testsToRun, filter, jUnitCoreParameters, junitCore );
+            executeEager( testsToRun, filter, listeners );
         }
         else
         {
-            executeLazy( logger, testsToRun, filter, jUnitCoreParameters, junitCore );
+            executeLazy( testsToRun, filter, listeners );
         }
     }
 
-    private static JUnitCore createJUnitCore( List<RunListener> listeners )
+    private JUnitCore createJUnitCore( final Notifier notifier, Collection<RunListener> listeners )
     {
-        JUnitCore junitCore = new JUnitCore();
-        for ( RunListener runListener : listeners )
-        {
-            junitCore.addListener( runListener );
-        }
+        JUnitCore junitCore = new JUnitCore( notifier );
+
+        // custom listeners added last
+        notifier.addListeners( listeners );
+
         return junitCore;
     }
 
-    private static void executeEager( ConsoleLogger logger, TestsToRun testsToRun, Filter filter,
-                                      JUnitCoreParameters jUnitCoreParameters, JUnitCore junitCore )
+    private void executeEager( TestsToRun testsToRun, Filter filter, Collection<RunListener> listeners )
         throws TestSetFailedException
     {
+        JUnitCore junitCore = createJUnitCore( notifier, listeners );
         Class<?>[] tests = testsToRun.getLocatedClasses();
-        Computer computer = createComputer( logger, jUnitCoreParameters );
-        createRequestAndRun( filter, computer, junitCore, tests );
+        Computer computer = createComputer();
+        createRequestAndRun( filter, computer, junitCore.withReportedTests( tests ), tests );
     }
 
-    private static void executeLazy( ConsoleLogger logger, TestsToRun testsToRun, Filter filter,
-                                     JUnitCoreParameters jUnitCoreParameters, JUnitCore junitCore )
+    private void executeLazy( TestsToRun testsToRun, Filter filter, Collection<RunListener> listeners )
         throws TestSetFailedException
     {
+        JUnitCore junitCore = createJUnitCore( notifier, listeners );
         // in order to support LazyTestsToRun, the iterator must be used
-        for ( Class clazz : testsToRun )
+        for ( Class<?> clazz : testsToRun )
         {
-            Computer computer = createComputer( logger, jUnitCoreParameters );
-            createRequestAndRun( filter, computer, junitCore, clazz );
+            Computer computer = createComputer();
+            createRequestAndRun( filter, computer, junitCore.withReportedTests( clazz ), clazz );
         }
     }
 
-    private static void createRequestAndRun( Filter filter, Computer computer, JUnitCore junitCore,
-                                             Class<?>... classesToRun )
+    private void createRequestAndRun( Filter filter, Computer computer, JUnitCore junitCore, Class<?>... classesToRun )
         throws TestSetFailedException
     {
         Request req = Request.classes( computer, classesToRun );
@@ -105,8 +129,8 @@ class JUnitCoreWrapper
             }
         }
 
-        Result run = junitCore.run( req );
-        JUnit4RunListener.rethrowAnyTestMechanismFailures( run );
+        Result run = junitCore.run( req.getRunner() );
+        rethrowAnyTestMechanismFailures( run );
 
         if ( computer instanceof ParallelComputer )
         {
@@ -118,37 +142,78 @@ class JUnitCoreWrapper
         }
     }
 
-    private static Computer createComputer( ConsoleLogger logger, JUnitCoreParameters parameters )
-        throws TestSetFailedException
+    private Computer createComputer()
     {
-        return parameters.isNoThreading()
+        return jUnitCoreParameters.isNoThreading()
             ? Computer.serial()
-            : new ParallelComputerBuilder( logger, parameters ).buildComputer();
+            : new ParallelComputerBuilder( logger, jUnitCoreParameters ).buildComputer();
     }
 
-    private static class FilteringRequest
-        extends Request
+    private final class JUnitCore
+        extends org.apache.maven.surefire.junitcore.JUnitCore
     {
-        private Runner filteredRunner;
+        private final JUnit47FailFastListener failFastListener;
 
-        public FilteringRequest( Request req, Filter filter )
+        JUnitCore( Notifier notifier )
         {
-            try
+            super( notifier );
+            failFastListener = failFast ? new JUnit47FailFastListener( this ) : null;
+            if ( failFastListener != null )
             {
-                Runner runner = req.getRunner();
-                filter.apply( runner );
-                filteredRunner = runner;
+                notifier.addListener( failFastListener );
             }
-            catch ( NoTestsRemainException e )
+        }
+
+        JUnitCore withReportedTests( Class<?>... tests )
+        {
+            Queue<String> stoppedTests = getRemainingTestClasses();
+            if ( stoppedTests != null )
+            {
+                for ( Class<?> test : tests )
+                {
+                    stoppedTests.add( test.getName() );
+                }
+            }
+            return this;
+        }
+
+        @Override
+        @SuppressWarnings( "checkstyle:innerassignment" )
+        protected void afterException( Throwable e )
+            throws TestSetFailedException
+        {
+            if ( failFast && e instanceof StoppedByUserException )
+            {
+                Queue<String> stoppedTests = getRemainingTestClasses();
+                if ( stoppedTests != null )
+                {
+                    String reason = e.getClass().getName();
+                    Ignore reasonForIgnoredTest = createIgnored( reason );
+                    for ( String clazz; ( clazz = stoppedTests.poll() ) != null; )
+                    {
+                        notifier.fireTestIgnored( createDescription( clazz, reasonForIgnoredTest ) );
+                    }
+                }
+            }
+            else
             {
-                filteredRunner = null;
+                super.afterException( e );
             }
         }
 
         @Override
-        public Runner getRunner()
+        protected void afterFinished()
+        {
+            Queue<String> stoppedTests = getRemainingTestClasses();
+            if ( stoppedTests != null )
+            {
+                stoppedTests.clear();
+            }
+        }
+
+        private Queue<String> getRemainingTestClasses()
         {
-            return filteredRunner;
+            return failFastListener == null ? null : failFastListener.getRemainingTestClasses();
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/Stoppable.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/Stoppable.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/Stoppable.java
new file mode 100644
index 0000000..2450e64
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/Stoppable.java
@@ -0,0 +1,32 @@
+package org.apache.maven.surefire.junitcore;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+interface Stoppable
+{
+    /**
+     * Delegates this call to {@link org.junit.runner.notification.RunNotifier#pleaseStop()}.
+     */
+    void pleaseStop();
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnit4Reflector481Test.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnit4Reflector481Test.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnit4Reflector481Test.java
index 7d4b102..896af3e 100644
--- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnit4Reflector481Test.java
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnit4Reflector481Test.java
@@ -49,6 +49,37 @@ public class JUnit4Reflector481Test
             Description.createTestDescription( IgnoreWithDescription.class, "testSomething2", annotations );
         Ignore annotatedIgnore = JUnit4Reflector.getAnnotatedIgnore( desc );
         assertNotNull( annotatedIgnore );
+        assertEquals(
+            "testSomething2" + "(org.apache.maven.surefire.junitcore.JUnit4Reflector481Test$IgnoreWithDescription)",
+            desc.getDisplayName() );
+        assertEquals( "testSomething2"
+                          + "(org.apache.maven.surefire.junitcore.JUnit4Reflector481Test$IgnoreWithDescription)",
+                      desc.toString() );
+        assertEquals( "org.apache.maven.surefire.junitcore.JUnit4Reflector481Test$IgnoreWithDescription",
+                      desc.getClassName() );
+        assertEquals( "testSomething2", desc.getMethodName() );
+        assertEquals( 0, desc.getChildren().size() );
+        assertEquals( 2, desc.getAnnotations().size() );
+        assertSame( annotatedIgnore, desc.getAnnotation( Ignore.class ) );
+        assertEquals( reason, annotatedIgnore.value() );
+    }
+
+    @Test
+    public void testGetAnnotatedIgnoreWithoutClass()
+    {
+        final Method testSomething2 =
+            ReflectionUtils.getMethod( IgnoreWithDescription.class, "testSomething2", EMPTY_CLASS_ARRAY );
+        final Annotation[] annotations = testSomething2.getAnnotations();
+        Description desc = Description.createSuiteDescription( "testSomething2", annotations );
+        Ignore annotatedIgnore = JUnit4Reflector.getAnnotatedIgnore( desc );
+        assertNotNull( annotatedIgnore );
+        assertEquals( "testSomething2", desc.getDisplayName() );
+        assertEquals( "testSomething2", desc.toString() );
+        assertEquals( "testSomething2", desc.getClassName() );
+        assertNull( desc.getMethodName() );
+        assertEquals( 0, desc.getChildren().size() );
+        assertEquals( 2, desc.getAnnotations().size() );
+        assertSame( annotatedIgnore, desc.getAnnotation( Ignore.class ) );
         assertEquals( reason, annotatedIgnore.value() );
     }
 
@@ -64,5 +95,26 @@ public class JUnit4Reflector481Test
         }
     }
 
+    @Test
+    public void testCreatePureDescription()
+    {
+        Description description = JUnit4Reflector.createDescription( "exception" );
+        assertEquals( "exception", description.getDisplayName() );
+        assertEquals( "exception", description.toString() );
+        assertEquals( 0, description.getChildren().size() );
+    }
 
+    @Test
+    public void testCreateDescription()
+    {
+        Ignore ignore = JUnit4Reflector.createIgnored( "error" );
+        Description description = JUnit4Reflector.createDescription( "exception", ignore );
+        assertEquals( "exception", description.getDisplayName() );
+        assertEquals( "exception", description.toString() );
+        assertEquals( "exception", description.getClassName() );
+        assertEquals( 0, description.getChildren().size() );
+        Ignore annotatedIgnore = JUnit4Reflector.getAnnotatedIgnore( description );
+        assertNotNull( annotatedIgnore );
+        assertEquals( "error", annotatedIgnore.value() );
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
index 538b31a..01aa81f 100644
--- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
@@ -23,6 +23,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.surefire.booter.BaseProviderFactory;
 import org.apache.maven.surefire.booter.ProviderParameterNames;
+import org.apache.maven.surefire.common.junit4.Notifier;
 import org.apache.maven.surefire.report.ConsoleLogger;
 import org.apache.maven.surefire.report.DefaultConsoleReporter;
 import org.apache.maven.surefire.report.ReporterConfiguration;
@@ -111,7 +112,8 @@ public class Surefire746Test
             // JUnitCoreWrapper#execute() is calling JUnit4RunListener#rethrowAnyTestMechanismFailures()
             // and rethrows a failure which happened in listener
             exception.expect( TestSetFailedException.class );
-            JUnitCoreWrapper.execute( new Logger(), testsToRun, jUnitCoreParameters, customRunListeners, null );
+            new JUnitCoreWrapper( new Notifier(), jUnitCoreParameters, new Logger(), false )
+                .execute( testsToRun, customRunListeners, null );
         }
         finally
         {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-testng-utils/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng-utils/pom.xml b/surefire-providers/surefire-testng-utils/pom.xml
index 64d8518..8fc1513 100644
--- a/surefire-providers/surefire-testng-utils/pom.xml
+++ b/surefire-providers/surefire-testng-utils/pom.xml
@@ -49,7 +49,7 @@
     <dependency>
       <groupId>org.testng</groupId>
       <artifactId>testng</artifactId>
-      <version>5.7</version>
+      <version>5.10</version>
       <classifier>jdk15</classifier>
       <scope>provided</scope>
     </dependency>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastEventsSingleton.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastEventsSingleton.java b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastEventsSingleton.java
new file mode 100644
index 0000000..523ce8f
--- /dev/null
+++ b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastEventsSingleton.java
@@ -0,0 +1,53 @@
+package org.apache.maven.surefire.testng.utils;
+
+/*
+ * 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.
+ */
+
+/**
+ * Stores and retrieves atomic events
+ * used by {@link FailFastNotifier} and TestNG provider.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public final class FailFastEventsSingleton
+{
+    private static final FailFastEventsSingleton INSTANCE = new FailFastEventsSingleton();
+
+    private volatile boolean skipAfterFailure;
+
+    private FailFastEventsSingleton()
+    {
+    }
+
+    public static FailFastEventsSingleton getInstance()
+    {
+        return INSTANCE;
+    }
+
+    public boolean isSkipAfterFailure()
+    {
+        return skipAfterFailure;
+    }
+
+    public void setSkipOnNextTest()
+    {
+        this.skipAfterFailure = true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastListener.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastListener.java b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastListener.java
new file mode 100644
index 0000000..d3d5c00
--- /dev/null
+++ b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastListener.java
@@ -0,0 +1,77 @@
+package org.apache.maven.surefire.testng.utils;
+
+/*
+ * 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.testng.ITestContext;
+import org.testng.ITestListener;
+import org.testng.ITestResult;
+
+/**
+ * Sends an even in {@link FailFastEventsSingleton} that failure has appeared.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public class FailFastListener
+    implements ITestListener
+{
+    private final Stoppable stoppable;
+
+    public FailFastListener( Stoppable stoppable )
+    {
+        this.stoppable = stoppable;
+    }
+
+    public void onTestStart( ITestResult result )
+    {
+
+    }
+
+    public void onTestSuccess( ITestResult result )
+    {
+
+    }
+
+    public void onTestFailure( ITestResult result )
+    {
+        FailFastEventsSingleton.getInstance().setSkipOnNextTest();
+        stoppable.pleaseStop();
+    }
+
+    public void onTestSkipped( ITestResult result )
+    {
+
+    }
+
+    public void onTestFailedButWithinSuccessPercentage( ITestResult result )
+    {
+
+    }
+
+    public void onStart( ITestContext context )
+    {
+
+    }
+
+    public void onFinish( ITestContext context )
+    {
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastNotifier.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastNotifier.java b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastNotifier.java
new file mode 100644
index 0000000..735c2ce
--- /dev/null
+++ b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastNotifier.java
@@ -0,0 +1,50 @@
+package org.apache.maven.surefire.testng.utils;
+
+/*
+ * 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.testng.IInvokedMethod;
+import org.testng.IInvokedMethodListener;
+import org.testng.ITestResult;
+import org.testng.SkipException;
+
+/**
+ * Notifies TestNG core skipping remaining tests after first failure has appeared.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public class FailFastNotifier
+    implements IInvokedMethodListener
+{
+
+    public void beforeInvocation( IInvokedMethod iInvokedMethod, ITestResult iTestResult )
+    {
+        if ( FailFastEventsSingleton.getInstance().isSkipAfterFailure() )
+        {
+            throw new SkipException( "Skipped after failure. See parameter [skipAfterFailureCount] "
+                                         + "in surefire or failsafe plugin." );
+        }
+    }
+
+    public void afterInvocation( IInvokedMethod iInvokedMethod, ITestResult iTestResult )
+    {
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/Stoppable.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/Stoppable.java b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/Stoppable.java
new file mode 100644
index 0000000..c7be682
--- /dev/null
+++ b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/Stoppable.java
@@ -0,0 +1,32 @@
+package org.apache.maven.surefire.testng.utils;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public interface Stoppable
+{
+    /**
+     * Delegates this call to {@link org.apache.maven.surefire.report.RunListener#testExecutionSkippedByUser()}.
+     */
+    void pleaseStop();
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
index 678280f..5297276 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
@@ -79,9 +79,11 @@ public class TestNGDirectoryTestSuite
 
     private final List<CommandLineOption> mainCliOptions;
 
+    private final boolean isFailFast;
+
     public TestNGDirectoryTestSuite( String testSourceDirectory, Map<String, String> confOptions, File reportsDirectory,
                                      TestListResolver methodFilter, RunOrderCalculator runOrderCalculator,
-                                     ScanResult scanResult, List<CommandLineOption> mainCliOptions )
+                                     ScanResult scanResult, List<CommandLineOption> mainCliOptions, boolean isFailFast )
     {
         this.runOrderCalculator = runOrderCalculator;
         this.options = confOptions;
@@ -94,6 +96,7 @@ public class TestNGDirectoryTestSuite
         this.junitTestAnnotation = findJUnitTestAnnotation();
         this.junitOptions = createJUnitOptions();
         this.mainCliOptions = mainCliOptions;
+        this.isFailFast = isFailFast;
     }
 
     public void execute( TestsToRun testsToRun, ReporterFactory reporterManagerFactory )
@@ -128,7 +131,7 @@ public class TestNGDirectoryTestSuite
         final Map<String, String> optionsToUse = isJUnitTest( testClass ) ? junitOptions : options;
 
         TestNGExecutor.run( new Class<?>[]{ testClass }, testSourceDirectory, optionsToUse, reporter, this,
-                            reportsDirectory, methodFilter, mainCliOptions );
+                            reportsDirectory, methodFilter, mainCliOptions, isFailFast );
 
         finishTestSuite( reporter, this );
     }
@@ -218,14 +221,14 @@ public class TestNGDirectoryTestSuite
         Class<?>[] testClasses = testNgTestClasses.toArray( new Class<?>[testNgTestClasses.size()] );
 
         TestNGExecutor.run( testClasses, testSourceDirectory, options, reporterManager, this,
-                            testNgReportsDirectory, methodFilter, mainCliOptions );
+                            testNgReportsDirectory, methodFilter, mainCliOptions, isFailFast );
 
         if ( !junitTestClasses.isEmpty() )
         {
             testClasses = junitTestClasses.toArray( new Class[junitTestClasses.size()] );
 
             TestNGExecutor.run( testClasses, testSourceDirectory, junitOptions, reporterManager, this,
-                                junitReportsDirectory, methodFilter, mainCliOptions );
+                                junitReportsDirectory, methodFilter, mainCliOptions, isFailFast );
         }
 
         finishTestSuite( reporterManager, this );
@@ -295,7 +298,7 @@ public class TestNGDirectoryTestSuite
         startTestSuite( reporter, this );
 
         TestNGExecutor.run( new Class<?>[] { testSet.getTestClass() }, testSourceDirectory, options, reporter,
-                            this, reportsDirectory, methodFilter, mainCliOptions );
+                            this, reportsDirectory, methodFilter, mainCliOptions, isFailFast );
 
         finishTestSuite( reporter, this );
     }


[17/17] maven-surefire git commit: [SUREFIRE-580] fixed compilation error

Posted by ti...@apache.org.
[SUREFIRE-580] fixed compilation error


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

Branch: refs/heads/master
Commit: 86b253ba460ca1b943e54badbec2071385201e93
Parents: 62c7cd0
Author: Tibor17 <ti...@lycos.com>
Authored: Sun Sep 6 22:12:53 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:58:05 2015 +0200

----------------------------------------------------------------------
 .../org/apache/maven/surefire/junitcore/Surefire746Test.java    | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/86b253ba/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
index 01aa81f..46213df 100644
--- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
@@ -23,7 +23,9 @@ import java.util.concurrent.ConcurrentHashMap;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.surefire.booter.BaseProviderFactory;
 import org.apache.maven.surefire.booter.ProviderParameterNames;
+import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
 import org.apache.maven.surefire.common.junit4.Notifier;
+import org.apache.maven.surefire.junit4.MockReporter;
 import org.apache.maven.surefire.report.ConsoleLogger;
 import org.apache.maven.surefire.report.DefaultConsoleReporter;
 import org.apache.maven.surefire.report.ReporterConfiguration;
@@ -112,7 +114,8 @@ public class Surefire746Test
             // JUnitCoreWrapper#execute() is calling JUnit4RunListener#rethrowAnyTestMechanismFailures()
             // and rethrows a failure which happened in listener
             exception.expect( TestSetFailedException.class );
-            new JUnitCoreWrapper( new Notifier(), jUnitCoreParameters, new Logger(), false )
+            JUnit4RunListener dummy = new JUnit4RunListener( new MockReporter() );
+            new JUnitCoreWrapper( new Notifier( dummy, 0 ), jUnitCoreParameters, new Logger(), false )
                 .execute( testsToRun, customRunListeners, null );
         }
         finally


[03/17] maven-surefire git commit: [SUREFIRE] refactoring

Posted by ti...@apache.org.
[SUREFIRE] refactoring


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

Branch: refs/heads/master
Commit: 1e547c8c319d8ba38048f3caa5f86e4be5338a48
Parents: 8e37515
Author: Tibor17 <ti...@lycos.com>
Authored: Mon Aug 3 15:40:59 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:57:55 2015 +0200

----------------------------------------------------------------------
 .../TestSetMockReporterFactory.java             |  2 ++
 .../surefire/booter/MainCliOptionsAware.java    |  2 +-
 .../booter/SurefireClassLoadersAware.java       |  2 +-
 .../apache/maven/surefire/booter/Classpath.java |  5 ++-
 .../surefire/booter/PropertiesWrapper.java      |  5 ---
 .../maven/surefire/junitcore/TestSet.java       | 33 ++++++++++----------
 6 files changed, 22 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/1e547c8c/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
index df6c253..2e337e2 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
@@ -24,6 +24,8 @@ import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.surefire.report.RunListener;
 
 /**
+ * Internal tests use only.
+ *
  * @author Kristian Rosenvold
  */
 public class TestSetMockReporterFactory

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/1e547c8c/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
index b3b3c9d..eddebed 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
@@ -29,7 +29,7 @@ import java.util.List;
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.19
  */
-public interface MainCliOptionsAware
+interface MainCliOptionsAware
 {
     void setMainCliOptions( List<CommandLineOption> mainCliOptions );
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/1e547c8c/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireClassLoadersAware.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireClassLoadersAware.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireClassLoadersAware.java
index d55002e..351bb51 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireClassLoadersAware.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireClassLoadersAware.java
@@ -23,7 +23,7 @@ package org.apache.maven.surefire.booter;
  * @author Kristian Rosenvold
  * @noinspection UnusedDeclaration
  */
-public interface SurefireClassLoadersAware
+interface SurefireClassLoadersAware
 {
     void setClassLoaders( ClassLoader testClassLoader );
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/1e547c8c/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java
index ee093c9..a2a3d52 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java
@@ -155,11 +155,10 @@ public class Classpath implements Iterable<String>
     {
         try
         {
-            List urls = getAsUrlList();
+            List<URL> urls = getAsUrlList();
             IsolatedClassLoader classLoader = new IsolatedClassLoader( parent, childDelegation, roleName );
-            for ( Object url1 : urls )
+            for ( URL url : urls )
             {
-                URL url = (URL) url1;
                 classLoader.addURL( url );
             }
             if ( parent != null )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/1e547c8c/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PropertiesWrapper.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PropertiesWrapper.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PropertiesWrapper.java
index 8fec269..c4a7103 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PropertiesWrapper.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PropertiesWrapper.java
@@ -65,11 +65,6 @@ public class PropertiesWrapper
         return Boolean.valueOf( properties.get( propertyName ) );
     }
 
-    public boolean getBooleanObjectProperty( String propertyName )
-    {
-        return Boolean.valueOf( properties.get( propertyName ) );
-    }
-
     public int getIntProperty( String propertyName )
     {
         return Integer.parseInt( properties.get( propertyName ) );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/1e547c8c/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestSet.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestSet.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestSet.java
index aa03b97..d7a6a01 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestSet.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestSet.java
@@ -19,11 +19,10 @@ package org.apache.maven.surefire.junitcore;
  * under the License.
  */
 
-import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.SimpleReportEntry;
@@ -35,22 +34,22 @@ import org.junit.runner.Description;
  */
 public class TestSet
 {
-    private final Description testSetDescription;
+    private static final InheritableThreadLocal<TestSet> TEST_SET = new InheritableThreadLocal<TestSet>();
 
-    private final AtomicInteger numberOfCompletedChildren = new AtomicInteger( 0 );
+    private final Description testSetDescription;
 
-    // While the two parameters below may seem duplicated, it is not entirely the case,
-    // since numberOfTests has the correct value from the start, while testMethods grows as method execution starts.
+    private final Collection<TestMethod> testMethods = new ConcurrentLinkedQueue<TestMethod>();
 
-    private final AtomicInteger numberOfTests = new AtomicInteger( 0 );
+    private final AtomicBoolean played = new AtomicBoolean();
 
-    private final List<TestMethod> testMethods = Collections.synchronizedList( new ArrayList<TestMethod>() );
+    private volatile boolean allScheduled;
 
-    private static final InheritableThreadLocal<TestSet> TEST_SET = new InheritableThreadLocal<TestSet>();
+    private volatile int numberOfCompletedChildren;
 
-    private final AtomicBoolean allScheduled = new AtomicBoolean();
+    // While the two parameters may seem duplicated, it is not entirely the case,
+    // since numberOfTests has the correct value from the start, while testMethods grows as method execution starts.
 
-    private final AtomicBoolean played = new AtomicBoolean();
+    private volatile int numberOfTests;
 
     public TestSet( Description testSetDescription )
     {
@@ -124,7 +123,7 @@ public class TestSet
 
     public void incrementTestMethodCount()
     {
-        numberOfTests.incrementAndGet();
+        numberOfTests++;
     }
 
     private void addTestMethod( TestMethod testMethod )
@@ -134,8 +133,8 @@ public class TestSet
 
     public void incrementFinishedTests( RunListener reporterManager, boolean reportImmediately )
     {
-        numberOfCompletedChildren.incrementAndGet();
-        if ( allScheduled.get() && isAllTestsDone() && reportImmediately )
+        numberOfCompletedChildren++;
+        if ( allScheduled && isAllTestsDone() && reportImmediately )
         {
             replay( reporterManager );
         }
@@ -143,7 +142,7 @@ public class TestSet
 
     public void setAllScheduled( RunListener reporterManager )
     {
-        allScheduled.set( true );
+        allScheduled = true;
         if ( isAllTestsDone() )
         {
             replay( reporterManager );
@@ -152,7 +151,7 @@ public class TestSet
 
     private boolean isAllTestsDone()
     {
-        return numberOfTests.get() == numberOfCompletedChildren.get();
+        return numberOfTests == numberOfCompletedChildren;
     }
 
     public void attachToThread()


[12/17] maven-surefire git commit: [SUREFIRE-580] updated documentation

Posted by ti...@apache.org.
[SUREFIRE-580] updated documentation


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

Branch: refs/heads/master
Commit: 605708cabb584078beac02ff4f941081b37840fd
Parents: 3eb6fa7
Author: Tibor17 <ti...@lycos.com>
Authored: Thu Sep 3 16:56:59 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:58:00 2015 +0200

----------------------------------------------------------------------
 .../site/apt/examples/skip-after-failure.apt.vm | 22 +++++++++++++-------
 1 file changed, 14 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/605708ca/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm b/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
index 9e7af97..0fc1fc0 100644
--- a/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
@@ -31,28 +31,34 @@ Skipping Tests After First Failure
  To skip remaining tests after first failure or error has happened
  set configuration parameter <<<skipAfterFailureCount>>> to <<<1>>>.
  
- Prerequisite: use ${project.artifactId} 2.19 or higher, JUnit 4.0
- or higher, or TestNG 5.10 or higher.
- 
- Limitations: Although this feature works in forking modes as well, the
- functionality cannot be fully guaranteed (real first failure) in concurrent
- mode due to race conditions.
- 
  If version of TestNG is lover than 5.10 while the parameter 
  <<<skipAfterFailureCount>>> is set, the plugin fails with error:
  <<<[ERROR] org.apache.maven.surefire.util.SurefireReflectionException: 
  java.lang.NoClassDefFoundError: org/testng/IInvokedMethodListener>>>
  
  
-Skipping after the Nth failure or error
+Skipping Tests After the Nth Failure or Error
 
  To skip remaining tests after the Nth failure or error has happened
  set configuration parameter <<<skipAfterFailureCount>>> to N, where
  N is number greater than zero.
  
  
+Prerequisite
+ 
+ Use ${project.artifactId} 2.19 or higher, JUnit 4.0 or higher, or 
+ TestNG 5.10 or higher.
+ 
+ 
 Notices
  
  TestNG reports skipped methods however JUnit reports skipped classes.
  Preferably use JUnit 4.12 or higher version which fixed thread safety issues.
+ 
+ 
+Limitations
+ 
+ Although this feature works in forking modes as well, the functionality
+ cannot be fully guaranteed (real first failure) in concurrent mode
+ due to race conditions.
  
\ No newline at end of file


[05/17] maven-surefire git commit: [SUREFIRE] refactoring

Posted by ti...@apache.org.
[SUREFIRE] refactoring


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

Branch: refs/heads/master
Commit: d755ecfc45fcc68dd4b40953737dada0f2a00fcd
Parents: 1e547c8
Author: Tibor17 <ti...@lycos.com>
Authored: Mon Aug 3 22:48:36 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:57:56 2015 +0200

----------------------------------------------------------------------
 .../surefire/common/junit4/JUnitTestFailureListener.java     | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/d755ecfc/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnitTestFailureListener.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnitTestFailureListener.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnitTestFailureListener.java
index 473082d..e6fe2de 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnitTestFailureListener.java
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnitTestFailureListener.java
@@ -20,6 +20,7 @@ package org.apache.maven.surefire.common.junit4;
  */
 
 import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -30,14 +31,13 @@ import java.util.List;
  * @author Qingzhou Luo
  */
 public class JUnitTestFailureListener
-    extends org.junit.runner.notification.RunListener
+    extends RunListener
 {
-
-    List<Failure> allFailures = new ArrayList<Failure>();
+    private final List<Failure> allFailures = new ArrayList<Failure>();
 
     @Override
     public void testFailure( Failure failure )
-        throws java.lang.Exception
+        throws Exception
     {
         allFailures.add( failure );
     }


[13/17] maven-surefire git commit: [SUREFIRE-580] updated documentation

Posted by ti...@apache.org.
[SUREFIRE-580] updated documentation


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

Branch: refs/heads/master
Commit: deb3214e35d4f7db0048ad38a8c17c7cdc4f8c61
Parents: 605708c
Author: Tibor17 <ti...@lycos.com>
Authored: Thu Sep 3 16:59:09 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:58:01 2015 +0200

----------------------------------------------------------------------
 .../src/site/apt/examples/skip-after-failure.apt.vm       | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/deb3214e/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm b/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
index 0fc1fc0..31c5726 100644
--- a/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
@@ -31,11 +31,6 @@ Skipping Tests After First Failure
  To skip remaining tests after first failure or error has happened
  set configuration parameter <<<skipAfterFailureCount>>> to <<<1>>>.
  
- If version of TestNG is lover than 5.10 while the parameter 
- <<<skipAfterFailureCount>>> is set, the plugin fails with error:
- <<<[ERROR] org.apache.maven.surefire.util.SurefireReflectionException: 
- java.lang.NoClassDefFoundError: org/testng/IInvokedMethodListener>>>
- 
  
 Skipping Tests After the Nth Failure or Error
 
@@ -49,6 +44,11 @@ Prerequisite
  Use ${project.artifactId} 2.19 or higher, JUnit 4.0 or higher, or 
  TestNG 5.10 or higher.
  
+ If version of TestNG is lover than 5.10 while the parameter 
+ <<<skipAfterFailureCount>>> is set, the plugin fails with error:
+ <<<[ERROR] org.apache.maven.surefire.util.SurefireReflectionException: 
+ java.lang.NoClassDefFoundError: org/testng/IInvokedMethodListener>>>
+ 
  
 Notices
  


[02/17] maven-surefire git commit: [SUREFIRE] added missing interface MainCliOptionsAware

Posted by ti...@apache.org.
[SUREFIRE] added missing interface MainCliOptionsAware


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

Branch: refs/heads/master
Commit: 67af00f1cba38c56cb2036d247dd178016ebaf9c
Parents: 668b1b9
Author: Tibor17 <ti...@lycos.com>
Authored: Sat Jul 25 03:28:20 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:57:54 2015 +0200

----------------------------------------------------------------------
 .../surefire/booter/SurefireReflector.java      | 31 ++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/67af00f1/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
index 0e53a8f..b271171 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
@@ -24,6 +24,8 @@ import java.io.PrintStream;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
@@ -41,6 +43,8 @@ import org.apache.maven.surefire.util.ReflectionUtils;
 import org.apache.maven.surefire.util.RunOrder;
 import org.apache.maven.surefire.util.SurefireReflectionException;
 
+import static java.util.Collections.checkedList;
+
 /**
  * Does reflection based invocation of the surefire methods.
  * <p/>
@@ -84,7 +88,10 @@ public class SurefireReflector
 
     private final Class<?> mainCliOptions;
 
+    private final Class<Enum> commandLineOptionsClass;
+
 
+    @SuppressWarnings( "unchecked" )
     public SurefireReflector( ClassLoader surefireClassLoader )
     {
         this.surefireClassLoader = surefireClassLoader;
@@ -107,6 +114,8 @@ public class SurefireReflector
             booterParameters = surefireClassLoader.loadClass( ProviderParameters.class.getName() );
             testListResolver = surefireClassLoader.loadClass( TestListResolver.class.getName() );
             mainCliOptions = surefireClassLoader.loadClass( MainCliOptionsAware.class.getName() );
+            commandLineOptionsClass =
+                (Class<Enum>) surefireClassLoader.loadClass( CommandLineOption.class.getName() );
         }
         catch ( ClassNotFoundException e )
         {
@@ -272,7 +281,16 @@ public class SurefireReflector
     {
         if ( mainCliOptions.isAssignableFrom( o.getClass() ) )
         {
-            ReflectionUtils.invokeSetter( o, "setMainCliOptions", List.class, options );
+            List<Enum> newOptions = checkedList( new ArrayList<Enum>( options.size() ), commandLineOptionsClass );
+            Collection<Integer> ordinals = toOrdinals( options );
+            for ( Enum e : commandLineOptionsClass.getEnumConstants() )
+            {
+                if ( ordinals.contains( e.ordinal() ) )
+                {
+                    newOptions.add( e );
+                }
+            }
+            ReflectionUtils.invokeSetter( o, "setMainCliOptions", List.class, newOptions );
         }
     }
 
@@ -288,7 +306,6 @@ public class SurefireReflector
         ReflectionUtils.invokeSetter( o, "setRunOrderParameters", this.runOrderParameters, param );
     }
 
-
     public void setTestSuiteDefinitionAware( Object o, TestRequest testSuiteDefinition2 )
     {
         if ( testSuiteDefinitionAware.isAssignableFrom( o.getClass() ) )
@@ -365,4 +382,14 @@ public class SurefireReflector
         return runResult.isAssignableFrom( o.getClass() );
     }
 
+    private static Collection<Integer> toOrdinals( Collection<? extends Enum> enums )
+    {
+        Collection<Integer> ordinals = new ArrayList<Integer>( enums.size() );
+        for ( Enum e : enums )
+        {
+            ordinals.add( e.ordinal() );
+        }
+        return ordinals;
+    }
+
 }


[14/17] maven-surefire git commit: [SUREFIRE-580] more sanity check

Posted by ti...@apache.org.
[SUREFIRE-580] more sanity check


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

Branch: refs/heads/master
Commit: 1b7fe2d44e618cd7419755c74d75a484e49d761d
Parents: deb3214
Author: Tibor17 <ti...@lycos.com>
Authored: Sat Sep 5 00:16:37 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:58:02 2015 +0200

----------------------------------------------------------------------
 .../org/apache/maven/plugin/surefire/AbstractSurefireMojo.java   | 4 ++++
 1 file changed, 4 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/1b7fe2d4/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 e8f1f75..ff2e51e 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
@@ -2352,6 +2352,10 @@ public abstract class AbstractSurefireMojo
     private void warnIfRerunClashes()
         throws MojoFailureException
     {
+        if ( getSkipAfterFailureCount() < 0 )
+        {
+            throw new MojoFailureException( "Parameter rerunFailingTestsCount should not be negative." );
+        }
         boolean isRerun = getRerunFailingTestsCount() > 0;
         boolean isFailFast = getSkipAfterFailureCount() > 0;
         if ( isRerun && isFailFast )


[09/17] maven-surefire git commit: [SUREFIRE-580] Allow "fail fast" or stop running on first failure

Posted by ti...@apache.org.
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
new file mode 100644
index 0000000..c1a075f
--- /dev/null
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
@@ -0,0 +1,128 @@
+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.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import static org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
+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;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Testing cached and immediate commands in {@link TestLessInputStream}.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public class TestLessInputStreamBuilderTest
+{
+    @Rule
+    public final ExpectedException e = ExpectedException.none();
+
+    @Test
+    public void cachableCommandsShouldBeIterableWithStillOpenIterator()
+    {
+        TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder();
+        TestLessInputStream is = builder.build();
+        Iterator<Command> iterator = builder.getIterableCachable().iterator();
+
+        assertFalse( iterator.hasNext() );
+
+        builder.getCachableCommands().skipSinceNextTest();
+        assertTrue( iterator.hasNext() );
+        assertThat( iterator.next(), is( SKIP_SINCE_NEXT_TEST ) );
+
+        assertFalse( iterator.hasNext() );
+
+        builder.getCachableCommands().noop();
+        assertTrue( iterator.hasNext() );
+        assertThat( iterator.next(), is( NOOP ) );
+
+        builder.removeStream( is );
+    }
+
+    @Test
+    public void immediateCommands()
+        throws IOException
+    {
+        TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder();
+        TestLessInputStream is = builder.build();
+        assertThat( is.availablePermits(), is( 0 ) );
+        is.noop();
+        assertThat( is.availablePermits(), is( 1 ) );
+        is.beforeNextCommand();
+        assertThat( is.availablePermits(), is( 0 ) );
+        assertThat( is.nextCommand(), is( NOOP ) );
+        assertThat( is.availablePermits(), is( 0 ) );
+        e.expect( NoSuchElementException.class );
+        is.nextCommand();
+    }
+
+    @Test
+    public void combinedCommands()
+        throws IOException
+    {
+        TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder();
+        TestLessInputStream is = builder.build();
+        assertThat( is.availablePermits(), is( 0 ) );
+        builder.getCachableCommands().skipSinceNextTest();
+        is.noop();
+        assertThat( is.availablePermits(), is( 2 ) );
+        is.beforeNextCommand();
+        assertThat( is.availablePermits(), is( 1 ) );
+        assertThat( is.nextCommand(), is( NOOP ) );
+        assertThat( is.availablePermits(), is( 1 ) );
+        builder.getCachableCommands().skipSinceNextTest();
+        assertThat( is.availablePermits(), is( 1 ) );
+        builder.getImmediateCommands().shutdown();
+        assertThat( is.availablePermits(), is( 2 ) );
+        is.beforeNextCommand();
+        assertThat( is.nextCommand(), is( SHUTDOWN ) );
+        assertThat( is.availablePermits(), is( 1 ) );
+        is.beforeNextCommand();
+        assertThat( is.nextCommand(), is( SKIP_SINCE_NEXT_TEST ) );
+        assertThat( is.availablePermits(), is( 0 ) );
+        builder.getImmediateCommands().noop();
+        assertThat( is.availablePermits(), is( 1 ) );
+        builder.getCachableCommands().shutdown();
+        builder.getCachableCommands().shutdown();
+        assertThat( is.availablePermits(), is( 2 ) );
+        is.beforeNextCommand();
+        assertThat( is.nextCommand(), is( NOOP ) );
+        assertThat( is.availablePermits(), is( 1 ) );
+        is.beforeNextCommand();
+        assertThat( is.nextCommand(), is( SHUTDOWN ) );
+        assertThat( is.availablePermits(), is( 0 ) );
+        e.expect( NoSuchElementException.class );
+        is.nextCommand();
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
index feca48b..6fc171e 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
@@ -19,8 +19,6 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
  * under the License.
  */
 
-import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.booter.MasterProcessCommand;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -45,7 +43,7 @@ public class TestProvidingInputStreamTest
     public void closedStreamShouldReturnEndOfStream()
         throws IOException
     {
-        Queue<Command> commands = new ArrayDeque<Command>();
+        Queue<String> commands = new ArrayDeque<String>();
         TestProvidingInputStream is = new TestProvidingInputStream( commands );
         is.close();
         assertThat( is.read(), is( -1 ) );
@@ -55,7 +53,7 @@ public class TestProvidingInputStreamTest
     public void emptyStreamShouldWaitUntilClosed()
         throws Exception
     {
-        Queue<Command> commands = new ArrayDeque<Command>();
+        Queue<String> commands = new ArrayDeque<String>();
         final TestProvidingInputStream is = new TestProvidingInputStream( commands );
         final Thread streamThread = Thread.currentThread();
         FutureTask<Thread.State> futureTask = new FutureTask<Thread.State>( new Callable<Thread.State>()
@@ -79,9 +77,9 @@ public class TestProvidingInputStreamTest
     public void finishedTestsetShouldNotBlock()
         throws IOException
     {
-        Queue<Command> commands = new ArrayDeque<Command>();
-        commands.add( new Command( MasterProcessCommand.TEST_SET_FINISHED ) );
+        Queue<String> commands = new ArrayDeque<String>();
         final TestProvidingInputStream is = new TestProvidingInputStream( commands );
+        is.testSetFinished();
         new Thread( new Runnable()
         {
             public void run()
@@ -104,8 +102,8 @@ public class TestProvidingInputStreamTest
     public void shouldReadTest()
         throws IOException
     {
-        Queue<Command> commands = new ArrayDeque<Command>();
-        commands.add( new Command( MasterProcessCommand.RUN_CLASS, "Test" ) );
+        Queue<String> commands = new ArrayDeque<String>();
+        commands.add( "Test" );
         final TestProvidingInputStream is = new TestProvidingInputStream( commands );
         new Thread( new Runnable()
         {
@@ -136,6 +134,7 @@ public class TestProvidingInputStreamTest
         }
         catch ( InterruptedException e )
         {
+            // do nothing
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
index 995e2a1..80cf5ff 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
@@ -26,6 +26,7 @@ import org.apache.maven.plugin.surefire.booterclient.BooterDeserializerProviderC
 import org.apache.maven.plugin.surefire.booterclient.BooterDeserializerStartupConfigurationTest;
 import org.apache.maven.plugin.surefire.booterclient.ForkConfigurationTest;
 import org.apache.maven.plugin.surefire.booterclient.ForkingRunListenerTest;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStreamBuilderTest;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStreamTest;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactoryTest;
 import org.apache.maven.plugin.surefire.report.StatelessXmlReporterTest;
@@ -65,6 +66,7 @@ import org.junit.runners.Suite;
     BooterDeserializerStartupConfigurationTest.class,
     BooterDeserializerProviderConfigurationTest.class,
     TestProvidingInputStreamTest.class,
+    TestLessInputStreamBuilderTest.class
 } )
 @RunWith( Suite.class )
 public class JUnit4SuiteTest

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
index 4c06f6b..2e925cf 100644
--- a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
+++ b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
@@ -104,7 +104,6 @@ public class SurefirePlugin
     @Parameter( property = "surefire.useFile", defaultValue = "true" )
     private boolean useFile;
 
-
     /**
      * Set this to "true" to cause a failure if the none of the tests specified in -Dtest=... are run. Defaults to
      * "true".
@@ -279,6 +278,17 @@ public class SurefirePlugin
     @Parameter( property = "surefire.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 -Dsurefire.skipAfterFailureCount=1 or any number greater than zero.
+     * Defaults to "0".
+     *
+     * @since 2.19
+     */
+    @Parameter( property = "surefire.skipAfterFailureCount", defaultValue = "0" )
+    private int skipAfterFailureCount;
+
     protected int getRerunFailingTestsCount()
     {
         return rerunFailingTestsCount;
@@ -448,6 +458,11 @@ public class SurefirePlugin
         this.failIfNoSpecifiedTests = failIfNoSpecifiedTests;
     }
 
+    public int getSkipAfterFailureCount()
+    {
+        return skipAfterFailureCount;
+    }
+
     public boolean isPrintSummary()
     {
         return printSummary;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm b/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
new file mode 100644
index 0000000..9e7af97
--- /dev/null
+++ b/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
@@ -0,0 +1,58 @@
+  ------
+  Skipping Tests After Failure
+  ------
+  Tibor Digana
+  ------
+  July 2015
+  ------
+  
+ ~~ Licensed to the Apache Software Foundation (ASF) under one
+ ~~ or more contributor license agreements.  See the NOTICE file
+ ~~ distributed with this work for additional information
+ ~~ regarding copyright ownership.  The ASF licenses this file
+ ~~ to you under the Apache License, Version 2.0 (the
+ ~~ "License"); you may not use this file except in compliance
+ ~~ with the License.  You may obtain a copy of the License at
+ ~~
+ ~~   http://www.apache.org/licenses/LICENSE-2.0
+ ~~
+ ~~ Unless required by applicable law or agreed to in writing,
+ ~~ software distributed under the License is distributed on an
+ ~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~~ KIND, either express or implied.  See the License for the
+ ~~ specific language governing permissions and limitations
+ ~~ under the License.
+
+ ~~ NOTE: For help with the syntax of this file, see:
+ ~~ http://maven.apache.org/doxia/references/apt-format.html
+
+Skipping Tests After First Failure
+
+ To skip remaining tests after first failure or error has happened
+ set configuration parameter <<<skipAfterFailureCount>>> to <<<1>>>.
+ 
+ Prerequisite: use ${project.artifactId} 2.19 or higher, JUnit 4.0
+ or higher, or TestNG 5.10 or higher.
+ 
+ Limitations: Although this feature works in forking modes as well, the
+ functionality cannot be fully guaranteed (real first failure) in concurrent
+ mode due to race conditions.
+ 
+ If version of TestNG is lover than 5.10 while the parameter 
+ <<<skipAfterFailureCount>>> is set, the plugin fails with error:
+ <<<[ERROR] org.apache.maven.surefire.util.SurefireReflectionException: 
+ java.lang.NoClassDefFoundError: org/testng/IInvokedMethodListener>>>
+ 
+ 
+Skipping after the Nth failure or error
+
+ To skip remaining tests after the Nth failure or error has happened
+ set configuration parameter <<<skipAfterFailureCount>>> to N, where
+ N is number greater than zero.
+ 
+ 
+Notices
+ 
+ TestNG reports skipped methods however JUnit reports skipped classes.
+ Preferably use JUnit 4.12 or higher version which fixed thread safety issues.
+ 
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/maven-surefire-plugin/src/site/site.xml
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/site/site.xml b/maven-surefire-plugin/src/site/site.xml
index 1320caf..833c0a9 100644
--- a/maven-surefire-plugin/src/site/site.xml
+++ b/maven-surefire-plugin/src/site/site.xml
@@ -42,6 +42,7 @@
       <item name="Using JUnit" href="examples/junit.html"/>
       <item name="Using POJO Tests" href="examples/pojo-test.html"/>
       <item name="Skipping Tests" href="examples/skipping-test.html"/>
+      <item name="Skip After Failure" href="examples/skip-after-failure.html"/>
       <item name="Inclusions and Exclusions of Tests" href="examples/inclusion-exclusion.html"/>
       <item name="Running a Single Test" href="examples/single-test.html"/>
       <item name="Re-run Failing Tests" href="examples/rerun-failing-tests.html"/>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
index 980f6d9..0433a01 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
@@ -45,7 +45,8 @@ import java.util.Map;
  */
 public class BaseProviderFactory
     implements DirectoryScannerParametersAware, ReporterConfigurationAware, SurefireClassLoadersAware, TestRequestAware,
-    ProviderPropertiesAware, ProviderParameters, TestArtifactInfoAware, RunOrderParametersAware, MainCliOptionsAware
+    ProviderPropertiesAware, ProviderParameters, TestArtifactInfoAware, RunOrderParametersAware, MainCliOptionsAware,
+    FailFastAware
 {
     private static final int ROOT_CHANNEL = 0;
 
@@ -69,6 +70,8 @@ public class BaseProviderFactory
 
     private TestArtifactInfo testArtifactInfo;
 
+    private int skipAfterFailureCount;
+
     public BaseProviderFactory( ReporterFactory reporterFactory, boolean insideFork )
     {
         this.reporterFactory = reporterFactory;
@@ -188,4 +191,19 @@ public class BaseProviderFactory
     {
         this.mainCliOptions = mainCliOptions == null ? Collections.<CommandLineOption>emptyList() : mainCliOptions;
     }
+
+    public int getSkipAfterFailureCount()
+    {
+        return skipAfterFailureCount;
+    }
+
+    public void setSkipAfterFailureCount( int skipAfterFailureCount )
+    {
+        this.skipAfterFailureCount = skipAfterFailureCount;
+    }
+
+    public boolean isInsideFork()
+    {
+        return insideFork;
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
index 992c78c..25bb2d1 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
@@ -19,17 +19,27 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
+import static org.apache.maven.surefire.util.internal.StringUtils.requireNonNull;
+
 /**
  * Encapsulates data and command sent from master to forked process.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
  */
 public final class Command
 {
+    public static final Command TEST_SET_FINISHED = new Command( MasterProcessCommand.TEST_SET_FINISHED );
+    public static final Command SKIP_SINCE_NEXT_TEST = new Command( MasterProcessCommand.SKIP_SINCE_NEXT_TEST );
+    public static final Command SHUTDOWN = new Command( MasterProcessCommand.SHUTDOWN );
+    public static final Command NOOP = new Command( MasterProcessCommand.NOOP );
+
     private final MasterProcessCommand command;
     private final String data;
 
     public Command( MasterProcessCommand command, String data )
     {
-        this.command = command;
+        this.command = requireNonNull( command );
         this.data = data;
     }
 
@@ -47,4 +57,30 @@ public final class Command
     {
         return data;
     }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+
+        Command arg = (Command) o;
+
+        return command == arg.command && ( data == null ? arg.data == null : data.equals( arg.data ) );
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int result = command.hashCode();
+        result = 31 * result + ( data != null ? data.hashCode() : 0 );
+        return result;
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/booter/FailFastAware.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/FailFastAware.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/FailFastAware.java
new file mode 100644
index 0000000..06e47ba
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/FailFastAware.java
@@ -0,0 +1,31 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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.
+ */
+
+/**
+ * See the plugin configuration parameter <em>skipAfterFailureCount</em>.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+interface FailFastAware
+{
+    void setSkipAfterFailureCount( int skipAfterFailureCount );
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingReporterFactory.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingReporterFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingReporterFactory.java
index 78f29e2..8108609 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingReporterFactory.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingReporterFactory.java
@@ -48,7 +48,7 @@ public class ForkingReporterFactory
         this.originalSystemOut = originalSystemOut;
     }
 
-    public synchronized RunListener createReporter()
+    public RunListener createReporter()
     {
         return new ForkingRunListener( originalSystemOut, testSetChannelId++, isTrimstackTrace );
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 203651e..e56b94b 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
@@ -29,6 +29,7 @@ import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.SafeThrowable;
+import org.apache.maven.surefire.report.SimpleReportEntry;
 import org.apache.maven.surefire.report.StackTraceWriter;
 import org.apache.maven.surefire.util.internal.StringUtils;
 
@@ -80,6 +81,8 @@ public class ForkingRunListener
 
     public static final byte BOOTERCODE_NEXT_TEST = (byte) 'N';
 
+    public static final byte BOOTERCODE_STOP_ON_NEXT_TEST = (byte) 'S';
+
     public static final byte BOOTERCODE_ERROR = (byte) 'X';
 
     public static final byte BOOTERCODE_BYE = (byte) 'Z';
@@ -144,15 +147,18 @@ public class ForkingRunListener
         encodeAndWriteToTarget( toString( BOOTERCODE_TEST_SKIPPED, report, testSetChannelId ) );
     }
 
+    public void testExecutionSkippedByUser()
+    {
+        encodeAndWriteToTarget( toString( BOOTERCODE_STOP_ON_NEXT_TEST, new SimpleReportEntry(), testSetChannelId ) );
+    }
+
     void sendProps()
     {
         Properties systemProperties = System.getProperties();
 
         if ( systemProperties != null )
         {
-            Enumeration<?> propertyKeys = systemProperties.propertyNames();
-
-            while ( propertyKeys.hasMoreElements() )
+            for ( Enumeration<?> propertyKeys = systemProperties.propertyNames(); propertyKeys.hasMoreElements(); )
             {
                 String key = (String) propertyKeys.nextElement();
                 String value = systemProperties.getProperty( key );
@@ -202,7 +208,10 @@ public class ForkingRunListener
     private void encodeAndWriteToTarget( String string )
     {
         byte[] encodeBytes = encodeStringForForkCommunication( string );
-        target.write( encodeBytes, 0, encodeBytes.length );
+        synchronized ( target ) // See notes about synchronization/thread safety in class javadoc
+        {
+            target.write( encodeBytes, 0, encodeBytes.length );
+        }
     }
 
     private String toPropertyString( String key, String value )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
index 0a553f7..3512b5a 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
@@ -40,7 +40,7 @@ public enum MasterProcessCommand
 {
     RUN_CLASS( 0, String.class ),
     TEST_SET_FINISHED( 1, Void.class ),
-    STOP_ON_NEXT_TEST( 2, Void.class ),
+    SKIP_SINCE_NEXT_TEST( 2, Void.class ),
     SHUTDOWN( 3, Void.class ),
     /** To tell a forked process that the master process is still alive. Repeated after 30 seconds. */
     NOOP( 4, Void.class );
@@ -65,6 +65,11 @@ public enum MasterProcessCommand
         return dataType;
     }
 
+    public boolean hasDataType()
+    {
+        return dataType != Void.class;
+    }
+
     @SuppressWarnings( "checkstyle:magicnumber" )
     public byte[] encode( String data )
     {
@@ -119,7 +124,8 @@ public enum MasterProcessCommand
                 if ( command.getDataType() == Void.class )
                 {
                     // must read entire sequence to get to the next command; cannot be above the loop
-                    throw new IOException( "Command " + command + " read Void data with length " + dataLength );
+                    throw new IOException( format( "Command %s unexpectedly read Void data with length %d.",
+                                                   command, dataLength ) );
                 }
 
                 if ( total != dataLength )
@@ -129,7 +135,7 @@ public enum MasterProcessCommand
                         throw new EOFException( "stream closed" );
                     }
 
-                    throw new EOFException( format( "%s read %d out of %d bytes",
+                    throw new IOException( format( "%s read %d out of %d bytes",
                                                     MasterProcessCommand.class, total, dataLength ) );
                 }
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java
new file mode 100644
index 0000000..89a4f89
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java
@@ -0,0 +1,28 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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.
+ */
+
+/**
+ * listener interface
+ */
+public interface MasterProcessListener
+{
+    void update( Command command );
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
new file mode 100644
index 0000000..8c36bd2
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
@@ -0,0 +1,421 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static java.lang.Thread.State.NEW;
+import static java.lang.Thread.State.RUNNABLE;
+import static java.lang.Thread.State.TERMINATED;
+import static java.util.concurrent.locks.LockSupport.park;
+import static java.util.concurrent.locks.LockSupport.unpark;
+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.booter.MasterProcessCommand.decode;
+import static org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
+import static org.apache.maven.surefire.util.internal.StringUtils.isNotBlank;
+import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
+import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThread;
+import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_NEXT_TEST;
+
+/**
+ * Reader of commands coming from plugin(master) process.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public final class MasterProcessReader
+{
+    private static final MasterProcessReader READER = new MasterProcessReader();
+
+    private final Queue<TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener>> listeners
+        = new ConcurrentLinkedQueue<TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener>>();
+
+    private final Thread commandThread = newDaemonThread( new CommandRunnable(), "surefire-forkedjvm-command-thread" );
+
+    private final AtomicReference<Thread.State> state = new AtomicReference<Thread.State>( NEW );
+
+    private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();
+
+    private final Node head = new Node();
+
+    private volatile Node tail = head;
+
+    private static class Node
+    {
+        final AtomicReference<Node> successor = new AtomicReference<Node>();
+        volatile String item;
+    }
+
+    public static MasterProcessReader getReader()
+    {
+        final MasterProcessReader reader = READER;
+        if ( reader.state.compareAndSet( NEW, RUNNABLE ) )
+        {
+            reader.commandThread.start();
+        }
+        return reader;
+    }
+
+    /**
+     * @param listener listener called with <em>Any</em> {@link MasterProcessCommand command type}
+     */
+    public void addListener( MasterProcessListener listener )
+    {
+        listeners.add( new TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener>( null, listener ) );
+    }
+
+    public void addListener( MasterProcessCommand cmd, MasterProcessListener listener )
+    {
+        listeners.add( new TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener>( cmd, listener ) );
+    }
+
+    public void removeListener( MasterProcessListener listener )
+    {
+        for ( Iterator<TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener>> it = listeners.iterator();
+            it.hasNext(); )
+        {
+            TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener> listenerWrapper = it.next();
+            if ( listener == listenerWrapper.getP2() )
+            {
+                it.remove();
+            }
+        }
+    }
+
+    Iterable<String> getIterableClasses( PrintStream originalOutStream )
+    {
+        return new ClassesIterable( head, originalOutStream );
+    }
+
+    public void stop()
+    {
+        if ( state.compareAndSet( NEW, TERMINATED ) || state.compareAndSet( RUNNABLE, TERMINATED ) )
+        {
+            makeQueueFull();
+            listeners.clear();
+            commandThread.interrupt();
+        }
+    }
+
+    private static boolean isLastNode( Node current )
+    {
+        return current.successor.get() == current;
+    }
+
+    private boolean isQueueFull()
+    {
+        return isLastNode( tail );
+    }
+
+    private boolean addToQueue( String item )
+    {
+        if ( tail.item == null )
+        {
+            tail.item = item;
+            Node newNode = new Node();
+            tail.successor.set( newNode );
+            tail = newNode;
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    /**
+     * After this method returns the queue is closed, new item cannot be added and method
+     * {@link #isQueueFull()} returns true.
+     */
+    @SuppressWarnings( { "all", "checkstyle:needbraces", "checkstyle:emptystatement" } )
+    public void makeQueueFull()
+    {
+        // order between (#compareAndSet, and #get) matters in multithreading
+        for ( Node tail = this.tail;
+              !tail.successor.compareAndSet( null, tail ) && tail.successor.get() != tail;
+              tail = tail.successor.get() );
+    }
+
+    private void insertForQueue( Command cmd )
+    {
+        MasterProcessCommand expectedCommandType = cmd.getCommandType();
+        switch ( expectedCommandType )
+        {
+            case RUN_CLASS:
+                addToQueue( cmd.getData() );
+                break;
+            case TEST_SET_FINISHED:
+                makeQueueFull();
+                break;
+            default:
+                // checkstyle noop
+                break;
+        }
+    }
+
+    private void insertForListeners( Command cmd )
+    {
+        MasterProcessCommand expectedCommandType = cmd.getCommandType();
+        for ( TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener> listenerWrapper
+            : MasterProcessReader.this.listeners )
+        {
+            MasterProcessCommand commandType = listenerWrapper.getP1();
+            MasterProcessListener listener = listenerWrapper.getP2();
+            if ( commandType == null || commandType == expectedCommandType )
+            {
+                listener.update( cmd );
+            }
+        }
+    }
+
+    private void insert( Command cmd )
+    {
+        insertForQueue( cmd );
+        insertForListeners( cmd );
+    }
+
+    private final class ClassesIterable
+        implements Iterable<String>
+    {
+        private final Node head;
+        private final PrintStream originalOutStream;
+
+        ClassesIterable( Node head, PrintStream originalOutStream )
+        {
+            this.head = head;
+            this.originalOutStream = originalOutStream;
+        }
+
+        public Iterator<String> iterator()
+        {
+            return new ClassesIterator( head, originalOutStream );
+        }
+    }
+
+    private final class ClassesIterator
+        implements Iterator<String>
+    {
+        private final PrintStream originalOutStream;
+
+        private Node current;
+
+        private String clazz;
+
+        private ClassesIterator( Node current, PrintStream originalOutStream )
+        {
+            this.current = current;
+            this.originalOutStream = originalOutStream;
+        }
+
+        public boolean hasNext()
+        {
+            popUnread();
+            return isNotBlank( clazz );
+        }
+
+        public String next()
+        {
+            popUnread();
+            try
+            {
+                if ( isBlank( clazz ) )
+                {
+                    throw new NoSuchElementException();
+                }
+                else
+                {
+                    return clazz;
+                }
+            }
+            finally
+            {
+                clazz = null;
+            }
+        }
+
+        public void remove()
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        private void popUnread()
+        {
+            if ( state.get() == TERMINATED )
+            {
+                clazz = null;
+                return;
+            }
+
+            if ( isBlank( clazz ) )
+            {
+                do
+                {
+                    requestNextTest();
+                    if ( isLastNode( current ) )
+                    {
+                        clazz = null;
+                    }
+                    else if ( current.item == null )
+                    {
+                        do
+                        {
+                            await();
+                            /**
+                             * {@link java.util.concurrent.locks.LockSupport#park()}
+                             * may spuriously (that is, for no reason) return, therefore the loop here.
+                             */
+                        } while ( current.item == null && !isLastNode( current ) );
+                        clazz = current.item;
+                        current = current.successor.get();
+                    }
+                    else
+                    {
+                        clazz = current.item;
+                        current = current.successor.get();
+                    }
+                }
+                while ( tryNullWhiteClass() );
+            }
+
+            if ( state.get() == TERMINATED )
+            {
+                clazz = null;
+            }
+        }
+
+        private boolean tryNullWhiteClass()
+        {
+            if ( clazz != null && isBlank( clazz ) )
+            {
+                clazz = null;
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        private void requestNextTest()
+        {
+            byte[] encoded = encodeStringForForkCommunication( ( (char) BOOTERCODE_NEXT_TEST ) + ",0,want more!\n" );
+            originalOutStream.write( encoded, 0, encoded.length );
+        }
+    }
+
+    private Command read( DataInputStream stdIn )
+        throws IOException
+    {
+        Command command = decode( stdIn );
+        if ( command != null )
+        {
+            insertForQueue( command );
+        }
+        return command;
+    }
+
+    private void await()
+    {
+        final Thread currentThread = Thread.currentThread();
+        try
+        {
+            waiters.add( currentThread );
+            park();
+        }
+        finally
+        {
+            waiters.remove( currentThread );
+        }
+    }
+
+    private void wakeupWaiters()
+    {
+        for ( Thread waiter : waiters )
+        {
+            unpark( waiter );
+        }
+    }
+
+    private final class CommandRunnable
+        implements Runnable
+    {
+        public void run()
+        {
+            DataInputStream stdIn = new DataInputStream( System.in );
+            boolean isTestSetFinished = false;
+            try
+            {
+                while ( MasterProcessReader.this.state.get() == RUNNABLE )
+                {
+                    Command command = read( stdIn );
+                    if ( command == null )
+                    {
+                        System.err.println( "[SUREFIRE] std/in stream corrupted: first sequence not recognized" );
+                        break;
+                    }
+                    else
+                    {
+                        if ( command.getCommandType() == TEST_SET_FINISHED )
+                        {
+                            isTestSetFinished = true;
+                            wakeupWaiters();
+                        }
+                        else if ( command.getCommandType() == RUN_CLASS )
+                        {
+                            wakeupWaiters();
+                        }
+                        insertForListeners( command );
+                    }
+                }
+            }
+            catch ( EOFException e )
+            {
+                MasterProcessReader.this.state.set( TERMINATED );
+            }
+            catch ( IOException e )
+            {
+                MasterProcessReader.this.state.set( TERMINATED );
+                if ( !( e.getCause() instanceof InterruptedException ) )
+                {
+                    System.err.println( "[SUREFIRE] std/in stream corrupted" );
+                    e.printStackTrace();
+                }
+            }
+            finally
+            {
+                // ensure fail-safe iterator as well as safe to finish in for-each loop using ClassesIterator
+                if ( !isTestSetFinished )
+                {
+                    insert( new Command( TEST_SET_FINISHED ) );
+                }
+                wakeupWaiters();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
index b271171..cf07331 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
@@ -294,6 +294,11 @@ public class SurefireReflector
         }
     }
 
+    public void setSkipAfterFailureCount( Object o, int skipAfterFailureCount )
+    {
+        ReflectionUtils.invokeSetter( o, "setSkipAfterFailureCount", int.class, skipAfterFailureCount );
+    }
+
     public void setDirectoryScannerParameters( Object o, DirectoryScannerParameters dirScannerParams )
     {
         final Object param = createDirectoryScannerParameters( dirScannerParams );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/booter/TwoPropertiesWrapper.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/TwoPropertiesWrapper.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/TwoPropertiesWrapper.java
new file mode 100644
index 0000000..5c6e690
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/TwoPropertiesWrapper.java
@@ -0,0 +1,50 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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.
+ */
+
+/**
+ * Internal generic wrapper.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ * @param <P1> first property
+ * @param <P2> second property
+ */
+final class TwoPropertiesWrapper<P1, P2>
+{
+    private final P1 p1;
+    private final P2 p2;
+
+    TwoPropertiesWrapper( P1 p1, P2 p2 )
+    {
+        this.p1 = p1;
+        this.p2 = p2;
+    }
+
+    P1 getP1()
+    {
+        return p1;
+    }
+
+    P2 getP2()
+    {
+        return p2;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
index cb6da9a..708b436 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
@@ -132,4 +132,15 @@ public interface ProviderParameters
     TestArtifactInfo getTestArtifactInfo();
 
     List<CommandLineOption> getMainCliOptions();
+
+    /**
+     * Defaults to 0. Configured with parameter <em>skipAfterFailureCount</em> in POM.
+     */
+    int getSkipAfterFailureCount();
+
+    /**
+     * @return {@code true} if test provider appears in forked jvm; Otherwise {@code false} means
+     * in-plugin provider.
+     */
+    boolean isInsideFork();
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/report/RunListener.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/report/RunListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/report/RunListener.java
index e46ebcf..b964430 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/report/RunListener.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/report/RunListener.java
@@ -66,7 +66,6 @@ public interface RunListener
      */
     void testAssumptionFailure( ReportEntry report );
 
-
     /**
      * Event fired when a test ended with an error (non anticipated problem)
      *
@@ -87,4 +86,11 @@ public interface RunListener
      * @param report The report entry to log for
      */
     void testSkipped( ReportEntry report );
+
+    /**
+     * Event fired skipping an execution of remaining test-set in other fork(s); or does nothing if no forks.
+     * The method is called by {@link org.apache.maven.surefire.providerapi.SurefireProvider}.<p>
+     * (The event is fired after the Nth test failed to signal skipping the rest of test-set.)
+     */
+    void testExecutionSkippedByUser();
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
index 2229815..9455b04 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
@@ -35,6 +35,11 @@ public class SimpleReportEntry
 
     private final String message;
 
+    public SimpleReportEntry()
+    {
+        this( null, null );
+    }
+
     public SimpleReportEntry( String source, String name )
     {
         this( source, name, null, null );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
index 00e7bbc..717ab4e 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
@@ -29,7 +29,6 @@ import java.io.IOException;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.*;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
 import static org.junit.Assert.assertNotNull;
 
 /**
@@ -98,7 +97,7 @@ public class MasterProcessCommandTest
                     decoded = command.toDataTypeAsString( encoded );
                     assertNull( decoded );
                     break;
-                case STOP_ON_NEXT_TEST:
+                case SKIP_SINCE_NEXT_TEST:
                     assertEquals( Void.class, command.getDataType() );
                     encoded = command.fromDataType( dummyData );
                     assertThat( encoded.length, is( 0 ) );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-api/src/test/java/org/apache/maven/surefire/report/MockReporter.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/report/MockReporter.java b/surefire-api/src/test/java/org/apache/maven/surefire/report/MockReporter.java
index ad26f85..91ff446 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/report/MockReporter.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/report/MockReporter.java
@@ -114,6 +114,15 @@ public class MockReporter
         testIgnored.incrementAndGet();
     }
 
+    public void testExecutionSkippedByUser()
+    {
+    }
+
+    public void testSkippedByUser( ReportEntry report )
+    {
+        testSkipped( report );
+    }
+
 
     public String getFirstEvent()
     {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 be5c4c3..749f0e9 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
@@ -53,4 +53,5 @@ public final class BooterConstants
     public static final String FORKTESTSET_PREFER_TESTS_FROM_IN_STREAM = "preferTestsFromInStream";
     public static final String RERUN_FAILING_TESTS_COUNT = "rerunFailingTestsCount";
     public static final String MAIN_CLI_OPTIONS = "mainCliOptions";
+    public static final String FAIL_FAST_COUNT = "failFastCount";
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 5b89ed0..b8f2d0b 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
@@ -86,7 +86,7 @@ public class BooterDeserializer
 
         DirectoryScannerParameters dirScannerParams =
             new DirectoryScannerParameters( testClassesDirectory, includes, excludes, specificTests,
-                                            properties.getBooleanObjectProperty( FAILIFNOTESTS ), runOrder );
+                                            properties.getBooleanProperty( FAILIFNOTESTS ), runOrder );
 
         RunOrderParameters runOrderParameters = new RunOrderParameters( runOrder, runStatisticsFile );
 
@@ -96,14 +96,16 @@ public class BooterDeserializer
                              rerunFailingTestsCount );
 
         ReporterConfiguration reporterConfiguration =
-            new ReporterConfiguration( reportsDirectory, properties.getBooleanObjectProperty( ISTRIMSTACKTRACE ) );
+            new ReporterConfiguration( reportsDirectory, properties.getBooleanProperty( ISTRIMSTACKTRACE ) );
 
         Collection<String> cli = properties.getStringList( MAIN_CLI_OPTIONS );
 
+        int failFastCount = properties.getIntProperty( FAIL_FAST_COUNT );
+
         return new ProviderConfiguration( dirScannerParams, runOrderParameters,
                                           properties.getBooleanProperty( FAILIFNOTESTS ), reporterConfiguration, testNg,
                                           testSuiteDefinition, properties.getProperties(), typeEncodedTestForFork,
-                                          preferTestsFromInStream, fromStrings( cli ) );
+                                          preferTestsFromInStream, fromStrings( cli ), failFastCount );
     }
 
     public StartupConfiguration getProviderConfiguration()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 518c523..7567cd5 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
@@ -193,7 +193,7 @@ public final class ForkedBooter
     private static RunResult invokeProviderInSameClassLoader( Object testSet, Object factory,
                                                              ProviderConfiguration providerConfiguration,
                                                              boolean insideFork,
-                                                             StartupConfiguration startupConfiguration1,
+                                                             StartupConfiguration startupConfig,
                                                              boolean restoreStreams )
         throws TestSetFailedException, InvocationTargetException
     {
@@ -202,11 +202,10 @@ public final class ForkedBooter
         // Note that System.out/System.err are also read in the "ReporterConfiguration" instatiation
         // in createProvider below. These are the same values as here.
 
-        final SurefireProvider provider =
-            createProviderInCurrentClassloader( startupConfiguration1, insideFork, providerConfiguration, factory );
         try
         {
-            return provider.invoke( testSet );
+            return createProviderInCurrentClassloader( startupConfig, insideFork, providerConfiguration, factory )
+                .invoke( testSet );
         }
         finally
         {
@@ -233,6 +232,7 @@ public final class ForkedBooter
         bpf.setRunOrderParameters( providerConfiguration.getRunOrderParameters() );
         bpf.setDirectoryScannerParameters( providerConfiguration.getDirScannerParams() );
         bpf.setMainCliOptions( providerConfiguration.getMainCliOptions() );
+        bpf.setSkipAfterFailureCount( providerConfiguration.getSkipAfterFailureCount() );
         return (SurefireProvider) ReflectionUtils.instantiateOneArg( classLoader,
                                                                      startupConfiguration1.getActualClassName(),
                                                                      ProviderParameters.class, bpf );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java
deleted file mode 100644
index 89a4f89..0000000
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * 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.
- */
-
-/**
- * listener interface
- */
-public interface MasterProcessListener
-{
-    void update( Command command );
-}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
deleted file mode 100644
index 2dde2e1..0000000
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
+++ /dev/null
@@ -1,257 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * 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.util.internal.DaemonThreadFactory;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-import java.util.Queue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static java.lang.Thread.State.NEW;
-import static java.lang.Thread.State.RUNNABLE;
-import static java.lang.Thread.State.TERMINATED;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.decode;
-import static org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
-import static org.apache.maven.surefire.util.internal.StringUtils.isNotBlank;
-import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_NEXT_TEST;
-
-/**
- * Testing singleton {@code MasterProcessReader}.
- *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
- */
-public final class MasterProcessReader
-{
-    private static final MasterProcessReader READER = new MasterProcessReader();
-
-    private final BlockingQueue<Command> classes = new LinkedBlockingQueue<Command>();
-
-    private final Queue<MasterProcessListener> listeners = new ConcurrentLinkedQueue<MasterProcessListener>();
-
-    private final Thread commandThread =
-        DaemonThreadFactory.newDaemonThread( new CommandRunnable(), "surefire-forkedjvm-command-thread" );
-
-    private final AtomicReference<Thread.State> state = new AtomicReference<Thread.State>( NEW );
-
-    public static MasterProcessReader getReader()
-    {
-        if ( READER.state.compareAndSet( NEW, RUNNABLE ) )
-        {
-            READER.commandThread.start();
-        }
-        return READER;
-    }
-
-    private MasterProcessReader()
-    {
-        commandThread.setDaemon( true );
-    }
-
-    Iterable<String> getIterableClasses( PrintStream originalOutStream )
-    {
-        return new ClassesIterable( originalOutStream );
-    }
-
-    void stop( boolean interruptCurrentThread )
-    {
-        if ( state.compareAndSet( NEW, TERMINATED ) || state.compareAndSet( RUNNABLE, TERMINATED ) )
-        {
-            classes.drainTo( new ArrayList<Command>() );
-            listeners.clear();
-            commandThread.interrupt();
-            if ( interruptCurrentThread )
-            {
-                Thread.currentThread().interrupt();
-            }
-        }
-    }
-
-    private final class ClassesIterable
-        implements Iterable<String>
-    {
-        private final ClassesIterator it;
-
-        public ClassesIterable( PrintStream originalOutStream )
-        {
-            it = new ClassesIterator( originalOutStream );
-        }
-
-        public Iterator<String> iterator()
-        {
-            return it;
-        }
-    }
-
-    private final class ClassesIterator
-        implements Iterator<String>
-    {
-        private final PrintStream originalOutStream;
-
-        private String clazz;
-
-        private ClassesIterator( PrintStream originalOutStream )
-        {
-            this.originalOutStream = originalOutStream;
-        }
-
-        public boolean hasNext()
-        {
-            popUnread();
-            return isNotBlank( clazz );
-        }
-
-        public String next()
-        {
-            popUnread();
-            try
-            {
-                if ( isBlank( clazz ) )
-                {
-                    throw new NoSuchElementException();
-                }
-                else
-                {
-                    return clazz;
-                }
-            }
-            finally
-            {
-                clazz = null;
-            }
-        }
-
-        public void remove()
-        {
-            throw new UnsupportedOperationException();
-        }
-
-        private void popUnread()
-        {
-            if ( state.get() == TERMINATED )
-            {
-                clazz = null;
-                return;
-            }
-
-            if ( isBlank( clazz ) )
-            {
-                requestNextTest();
-                try
-                {
-                    do
-                    {
-                        Command command = MasterProcessReader.this.classes.take();
-                        clazz = command.getCommandType() == TEST_SET_FINISHED ? null : command.getData();
-                    }
-                    while ( tryNullWhiteClass() );
-                }
-                catch ( InterruptedException e )
-                {
-                    Thread.currentThread().interrupt();
-                    clazz = null;
-                }
-            }
-
-            if ( state.get() == TERMINATED )
-            {
-                clazz = null;
-            }
-        }
-
-        private boolean tryNullWhiteClass()
-        {
-            if ( clazz != null && isBlank( clazz ) )
-            {
-                clazz = null;
-                return true;
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        private void requestNextTest()
-        {
-            byte[] encoded = encodeStringForForkCommunication( ( (char) BOOTERCODE_NEXT_TEST ) + ",0,want more!\n" );
-            originalOutStream.write( encoded, 0, encoded.length );
-        }
-    }
-
-    private final class CommandRunnable
-        implements Runnable
-    {
-        public void run()
-        {
-            DataInputStream stdIn = new DataInputStream( System.in );
-            try
-            {
-                while ( MasterProcessReader.this.state.get() == RUNNABLE )
-                {
-                    Command command = decode( stdIn );
-                    if ( command != null )
-                    {
-                        // command is null if stream is corrupted, i.e. the first sequence could not be recognized
-                        insert( command );
-                    }
-                }
-            }
-            catch ( IOException e )
-            {
-                MasterProcessReader.this.state.set( TERMINATED );
-            }
-            finally
-            {
-                // ensure fail-safe iterator as well as safe to finish in for-each loop using ClassesIterator
-                insert( new Command( TEST_SET_FINISHED ) );
-            }
-        }
-
-        @SuppressWarnings( "unchecked" )
-        private void insert( Command cmd )
-        {
-            MasterProcessCommand commandType = cmd.getCommandType();
-            if ( commandType == RUN_CLASS || commandType == TEST_SET_FINISHED )
-            {
-                MasterProcessReader.this.classes.add( cmd );
-            }
-            else
-            {
-                for ( MasterProcessListener listener : MasterProcessReader.this.listeners )
-                {
-                    listener.update( cmd );
-                }
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/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 61f204d..69c7f2c 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
@@ -66,13 +66,15 @@ public class ProviderConfiguration
 
     private final List<CommandLineOption> mainCliOptions;
 
+    private int skipAfterFailureCount;
+
     @SuppressWarnings( "checkstyle:parameternumber" )
     public ProviderConfiguration( DirectoryScannerParameters directoryScannerParameters,
                                   RunOrderParameters runOrderParameters, boolean failIfNoTests,
                                   ReporterConfiguration reporterConfiguration, TestArtifactInfo testArtifact,
                                   TestRequest testSuiteDefinition, Map<String, String> providerProperties,
                                   TypeEncodedValue typeEncodedTestSet, boolean readTestsFromInStream,
-                                  List<CommandLineOption> mainCliOptions )
+                                  List<CommandLineOption> mainCliOptions, int skipAfterFailureCount )
     {
         this.runOrderParameters = runOrderParameters;
         this.providerProperties = providerProperties;
@@ -84,6 +86,7 @@ public class ProviderConfiguration
         this.forkTestSet = typeEncodedTestSet;
         this.readTestsFromInStream = readTestsFromInStream;
         this.mainCliOptions = mainCliOptions;
+        this.skipAfterFailureCount = skipAfterFailureCount;
     }
 
     public ReporterConfiguration getReporterConfiguration()
@@ -152,4 +155,14 @@ public class ProviderConfiguration
     {
         return mainCliOptions;
     }
+
+    public void setSkipAfterFailureCount( int skipAfterFailureCount )
+    {
+        this.skipAfterFailureCount = skipAfterFailureCount;
+    }
+
+    public int getSkipAfterFailureCount()
+    {
+        return skipAfterFailureCount;
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
index c5d93ab..5ad587f 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
@@ -46,8 +46,7 @@ public class ProviderFactory
 
     private final Object reporterManagerFactory;
 
-    private static final Class[] INVOKE_PARAMETERS = new Class[]{ Object.class };
-
+    private static final Class[] INVOKE_PARAMETERS = { Object.class };
 
     public ProviderFactory( StartupConfiguration startupConfiguration, ProviderConfiguration providerConfiguration,
                             ClassLoader testsClassLoader, Object reporterManagerFactory )
@@ -61,7 +60,7 @@ public class ProviderFactory
 
     public static RunResult invokeProvider( Object testSet, ClassLoader testsClassLoader, Object factory,
                                             ProviderConfiguration providerConfiguration, boolean insideFork,
-                                            StartupConfiguration startupConfiguration1, boolean restoreStreams )
+                                            StartupConfiguration startupConfig, boolean restoreStreams )
         throws TestSetFailedException, InvocationTargetException
     {
         final PrintStream orgSystemOut = System.out;
@@ -69,12 +68,11 @@ public class ProviderFactory
         // Note that System.out/System.err are also read in the "ReporterConfiguration" instantiation
         // in createProvider below. These are the same values as here.
 
-        ProviderFactory providerFactory =
-            new ProviderFactory( startupConfiguration1, providerConfiguration, testsClassLoader, factory );
-        final SurefireProvider provider = providerFactory.createProvider( insideFork );
         try
         {
-            return provider.invoke( testSet );
+            return new ProviderFactory( startupConfig, providerConfiguration, testsClassLoader, factory )
+                .createProvider( insideFork )
+                .invoke( testSet );
         }
         finally
         {
@@ -101,6 +99,7 @@ public class ProviderFactory
         surefireReflector.setRunOrderParameters( o, providerConfiguration.getRunOrderParameters() );
         surefireReflector.setIfDirScannerAware( o, providerConfiguration.getDirScannerParams() );
         surefireReflector.setMainCliOptions( o, providerConfiguration.getMainCliOptions() );
+        surefireReflector.setSkipAfterFailureCount( o, providerConfiguration.getSkipAfterFailureCount() );
 
         Object provider = surefireReflector.instantiateProvider( startupConfiguration.getActualClassName(), o );
         currentThread.setContextClassLoader( systemClassLoader );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-booter/src/test/java/org/apache/maven/surefire/booter/MasterProcessReaderTest.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/MasterProcessReaderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/MasterProcessReaderTest.java
index 9440e0d..ae299c1 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/MasterProcessReaderTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/MasterProcessReaderTest.java
@@ -30,13 +30,19 @@ import java.io.InputStream;
 import java.io.PrintStream;
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
+import java.util.Random;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
 
 import static org.apache.maven.surefire.util.internal.StringUtils.FORK_STREAM_CHARSET_NAME;
 import static org.junit.Assert.assertFalse;
@@ -56,6 +62,7 @@ public class MasterProcessReaderTest
 {
     private final BlockingQueue<Byte> blockingStream = new LinkedBlockingQueue<Byte>();
     private InputStream realInputStream;
+    private MasterProcessReader reader;
 
     @Before
     public void init()
@@ -63,25 +70,26 @@ public class MasterProcessReaderTest
     {
         Thread.interrupted();
         realInputStream = System.in;
-        addThisTestToPipeline( MasterProcessReaderTest.class.getName() );
+        addThisTestToPipeline( getClass().getName() );
         System.setIn( new SystemInputStream() );
+        reader = MasterProcessReader.getReader();
     }
 
     @After
     public void deinit()
     {
+        reader.stop();
         System.setIn( realInputStream );
     }
 
     @Test
     public void readJustOneClass() throws Exception
     {
-        MasterProcessReader reader = MasterProcessReader.getReader();
         Iterator<String> it = reader.getIterableClasses( new PrintStream( new ByteArrayOutputStream() ) )
             .iterator();
         assertTrue( it.hasNext() );
         assertThat( it.next(), is( getClass().getName() ) );
-        reader.stop( true );
+        reader.stop();
         assertFalse( it.hasNext() );
         try
         {
@@ -98,7 +106,6 @@ public class MasterProcessReaderTest
     public void stopBeforeReadInThread()
         throws Throwable
     {
-        final MasterProcessReader reader = MasterProcessReader.getReader();
         Runnable runnable = new Runnable()
         {
             public void run()
@@ -110,7 +117,7 @@ public class MasterProcessReaderTest
         };
         FutureTask<Object> futureTask = new FutureTask<Object>( runnable, null );
         Thread t = new Thread( futureTask );
-        reader.stop( false );
+        reader.stop();
         t.start();
         try
         {
@@ -126,7 +133,6 @@ public class MasterProcessReaderTest
     public void readTwoClassesInThread()
         throws Throwable
     {
-        final MasterProcessReader reader = MasterProcessReader.getReader();
         final CountDownLatch counter = new CountDownLatch( 1 );
         Runnable runnable = new Runnable()
         {
@@ -152,9 +158,45 @@ public class MasterProcessReaderTest
         {
             throw e.getCause();
         }
-        finally
+    }
+
+    @Test
+    public void readTwoClassesInTwoThreads()
+        throws Throwable
+    {
+        final Iterable<String> lazyTestSet =
+            reader.getIterableClasses( new PrintStream( new ByteArrayOutputStream() ) );
+
+        class Provider implements Runnable
+        {
+            public void run()
+            {
+                Iterator<String> it = lazyTestSet.iterator();
+                assertThat( it.next(), is( MasterProcessReaderTest.class.getName() ) );
+                assertThat( it.next(), is( PropertiesWrapperTest.class.getName() ) );
+                assertFalse( it.hasNext() );
+            }
+        }
+
+        ExecutorService es = Executors.newFixedThreadPool( 2 );
+        Future<?> result1 = es.submit( new Provider() );
+        Random rnd = new Random();
+        TimeUnit.MILLISECONDS.sleep( rnd.nextInt( 50 ) );
+        Future<?> result2 = es.submit( new Provider() );
+        TimeUnit.MILLISECONDS.sleep( rnd.nextInt( 50 ) );
+        addThisTestToPipeline( PropertiesWrapperTest.class.getName() );
+        TimeUnit.MILLISECONDS.sleep( rnd.nextInt( 50 ) );
+        addTestSetFinishedToPipeline();
+        for ( Future<?> result : Arrays.asList( result1, result2 ) )
         {
-            reader.stop( false );
+            try
+            {
+                result.get();
+            }
+            catch ( ExecutionException e )
+            {
+                throw e.getCause();
+            }
         }
     }
 
@@ -190,4 +232,13 @@ public class MasterProcessReaderTest
             blockingStream.add( buffer.get() );
         }
     }
+
+    private void addTestSetFinishedToPipeline()
+        throws UnsupportedEncodingException
+    {
+        for ( byte b : MasterProcessCommand.TEST_SET_FINISHED.encode() )
+        {
+            blockingStream.add( b );
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/2a944f06/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector.java
index 35dc888..e43a71e 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector.java
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector.java
@@ -22,38 +22,36 @@ package org.apache.maven.surefire.common.junit4;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import org.apache.maven.surefire.util.ReflectionUtils;
 
 import org.apache.maven.surefire.util.SurefireReflectionException;
 import org.junit.Ignore;
 import org.junit.runner.Description;
 import org.junit.runner.Request;
 
+import static org.apache.maven.surefire.util.ReflectionUtils.tryGetMethod;
+import static org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray;
+
 /**
  * JUnit4 reflection helper
  *
  */
 public final class JUnit4Reflector
 {
-    private static final Class[] PARAMS = new Class[]{ Class.class };
+    private static final Class[] PARAMS = { Class.class };
+
+    private static final Class[] IGNORE_PARAMS = { Ignore.class };
 
-    private static final Class[] IGNORE_PARAMS = new Class[]{ Ignore.class };
+    private static final Class[] PARAMS_WITH_ANNOTATIONS = { String.class, Annotation[].class };
 
     private JUnit4Reflector()
     {
         throw new IllegalStateException( "not instantiable constructor" );
     }
 
-    public static Ignore getAnnotatedIgnore( Description description )
+    public static Ignore getAnnotatedIgnore( Description d )
     {
-        Method getAnnotation = ReflectionUtils.tryGetMethod( description.getClass(), "getAnnotation", PARAMS );
-
-        if ( getAnnotation == null )
-        {
-            return null;
-        }
-
-        return (Ignore) ReflectionUtils.invokeMethodWithArray( description, getAnnotation, IGNORE_PARAMS );
+        Method getAnnotation = tryGetMethod( d.getClass(), "getAnnotation", PARAMS );
+        return getAnnotation == null ? null : (Ignore) invokeMethodWithArray( d, getAnnotation, IGNORE_PARAMS );
     }
 
     public static String getAnnotatedIgnoreValue( Description description )
@@ -92,20 +90,79 @@ public final class JUnit4Reflector
         }
         catch ( NoSuchMethodError e )
         {
-            try
-            {
-                return (Description) Description.class.getDeclaredMethod( "createSuiteDescription",
-                                                                          String.class, Annotation[].class )
-                    .invoke( null, description, new Annotation[0] );
-            }
-            catch ( InvocationTargetException e1 )
+            Method method = tryGetMethod( Description.class, "createSuiteDescription", PARAMS_WITH_ANNOTATIONS );
+            Object[] parameters = { description, new Annotation[0] };
+            // may throw exception probably with JUnit 5.x
+            return (Description) invokeMethodWithArray( null, method, parameters );
+        }
+    }
+
+    public static Description createDescription( String description, Annotation... annotations )
+    {
+        Method method = tryGetMethod( Description.class, "createSuiteDescription", PARAMS_WITH_ANNOTATIONS );
+        return method == null
+            ? Description.createSuiteDescription( description )
+            : (Description) invokeMethodWithArray( null, method, new Object[] { description, annotations } );
+    }
+
+    public static Ignore createIgnored( String value )
+    {
+        return new IgnoredWithUserError( value );
+    }
+
+    private static class IgnoredWithUserError
+        implements Annotation, Ignore
+    {
+        private final String value;
+
+        public IgnoredWithUserError( String value )
+        {
+            this.value = value;
+        }
+
+        public IgnoredWithUserError()
+        {
+            this( "" );
+        }
+
+        public String value()
+        {
+            return value;
+        }
+
+        public Class<? extends Annotation> annotationType()
+        {
+            return Ignore.class;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return value == null ? 0 : value.hashCode();
+        }
+
+        @Override
+        public boolean equals( Object obj )
+        {
+            return obj instanceof Annotation && obj instanceof Ignore && equalValue( ( Ignore ) obj );
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format( "%s(%s)", Ignore.class, value );
+        }
+
+        private boolean equalValue( Ignore ignore )
+        {
+            if ( ignore == null )
             {
-                throw new SurefireReflectionException( e1.getCause() );
+                return false;
             }
-            catch ( Exception e1 )
+            else
             {
-                // probably JUnit 5.x
-                throw new SurefireReflectionException( e1 );
+                String val = ignore.value();
+                return val == null ? value == null : val.equals( value );
             }
         }
     }


[16/17] maven-surefire git commit: [SUREFIRE-580] integration tests

Posted by ti...@apache.org.
[SUREFIRE-580] integration tests


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

Branch: refs/heads/master
Commit: 62c7cd0d6885c073efc48d8fdc3c6c77cf05ae03
Parents: 779e381
Author: Tibor17 <ti...@lycos.com>
Authored: Sun Sep 6 10:50:12 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:58:04 2015 +0200

----------------------------------------------------------------------
 .../maven/surefire/its/AbstractFailFastIT.java  | 104 +++++++++++++++++
 .../maven/surefire/its/FailFastJUnitIT.java     |  68 +++++++++++
 .../maven/surefire/its/FailFastTestNgIT.java    |  62 ++++++++++
 .../surefire/its/fixture/MavenLauncher.java     |   9 ++
 .../src/test/resources/fail-fast-junit/pom.xml  | 112 +++++++++++++++++++
 .../src/test/resources/fail-fast-testng/pom.xml |  67 +++++++++++
 6 files changed, 422 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/62c7cd0d/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/AbstractFailFastIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/AbstractFailFastIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/AbstractFailFastIT.java
new file mode 100644
index 0000000..5b79a4d
--- /dev/null
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/AbstractFailFastIT.java
@@ -0,0 +1,104 @@
+package org.apache.maven.surefire.its;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.its.fixture.MavenLauncher;
+import org.apache.maven.surefire.its.fixture.OutputValidator;
+import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.runners.Parameterized.Parameter;
+
+/**
+ * Base test class for SUREFIRE-580, configuration parameter <em>skipAfterFailureCount</em>.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+@RunWith( Parameterized.class )
+public abstract class AbstractFailFastIT
+    extends SurefireJUnit4IntegrationTestCase
+{
+    @Parameter( 0 )
+    public String description;
+
+    @Parameter( 1 )
+    public String profile;
+
+    @Parameter( 2 )
+    public Map<String, String> properties;
+
+    @Parameter( 3 )
+    public int total;
+
+    @Parameter( 4 )
+    public int failures;
+
+    @Parameter( 5 )
+    public int errors;
+
+    @Parameter( 6 )
+    public int skipped;
+
+    protected abstract String withProvider();
+
+    protected final OutputValidator prepare( String description, String profile, Map<String, String> properties )
+    {
+        MavenLauncher launcher = unpack( "/fail-fast-" + withProvider(), "_" + description )
+            .maven();
+
+        if ( profile != null )
+        {
+            launcher.addGoal( "-P " + profile );
+        }
+
+        if ( failures != 0 || errors != 0 )
+        {
+            launcher.withFailure();
+        }
+
+        return launcher.sysProp( properties ).executeTest();
+    }
+
+    protected final OutputValidator prepare( String description, Map<String, String> properties )
+    {
+        return prepare( description, null, properties );
+    }
+
+    protected static Map<String, String> props( int forkCount, int skipAfterFailureCount )
+    {
+        Map<String, String> props = new HashMap<String, String>( 2 );
+        props.put( "surefire.skipAfterFailureCount", "" + skipAfterFailureCount );
+        props.put( "forkCount", "" + forkCount );
+        return props;
+    }
+
+    @Test
+    public void test()
+    {
+        prepare( description, profile, properties )
+            .assertTestSuiteResults( total, errors, failures, skipped );
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/62c7cd0d/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/FailFastJUnitIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/FailFastJUnitIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/FailFastJUnitIT.java
new file mode 100644
index 0000000..3a85d5b
--- /dev/null
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/FailFastJUnitIT.java
@@ -0,0 +1,68 @@
+package org.apache.maven.surefire.its;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+
+import static org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Test class for SUREFIRE-580, configuration parameter <em>skipAfterFailureCount</em>.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public class FailFastJUnitIT
+    extends AbstractFailFastIT
+{
+
+    @Parameters(name = "{0}")
+    public static Iterable<Object[]> data()
+    {
+        ArrayList<Object[]> args = new ArrayList<Object[]>();
+        //                        description
+        //                                             profile
+        //                                                       forkCount,
+        //                                                       fail-fast-count
+        //                                                                        total
+        //                                                                             failures
+        //                                                                                     errors
+        //                                                                                           skipped
+        args.add( new Object[] { "junit4-oneFork-ff1", "junit4",   props( 1, 1 ), 5,   0,      1,    4 } );
+        args.add( new Object[] { "junit47-oneFork-ff1", "junit47", props( 1, 1 ), 5,   0,      1,    4 } );
+        args.add( new Object[] { "junit4-oneFork-ff2", "junit4",   props( 1, 2 ), 5,   0,      2,    3 } );
+        args.add( new Object[] { "junit47-oneFork-ff2", "junit47", props( 1, 2 ), 5,   0,      2,    3 } );
+        args.add( new Object[] { "junit4-twoForks-ff1", "junit4",  props( 2, 1 ), 5,   0,      1,    4 } );
+        args.add( new Object[] { "junit47-twoForks-ff1", "junit47",props( 2, 1 ), 5,   0,      1,    4 } );
+        args.add( new Object[] { "junit4-twoForks-ff2", "junit4",  props( 2, 2 ), 5,   0,      2,    3 } );
+        args.add( new Object[] { "junit47-twoForks-ff2", "junit47",props( 2, 2 ), 5,   0,      2,    3 } );
+        args.add( new Object[] { "junit4-oneFork-ff3", "junit4",   props( 1, 3 ), 5,   0,      2,    0 } );
+        args.add( new Object[] { "junit47-oneFork-ff3", "junit47", props( 1, 3 ), 5,   0,      2,    0 } );
+        args.add( new Object[] { "junit4-twoForks-ff3", "junit4",  props( 2, 3 ), 5,   0,      2,    0 } );
+        args.add( new Object[] { "junit47-twoForks-ff3", "junit47",props( 2, 3 ), 5,   0,      2,    0 } );
+        return args;
+    }
+
+    @Override
+    protected String withProvider()
+    {
+        return "junit";
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/62c7cd0d/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/FailFastTestNgIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/FailFastTestNgIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/FailFastTestNgIT.java
new file mode 100644
index 0000000..1c2a3bd
--- /dev/null
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/FailFastTestNgIT.java
@@ -0,0 +1,62 @@
+package org.apache.maven.surefire.its;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+
+import static org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Test class for SUREFIRE-580, configuration parameter <em>skipAfterFailureCount</em>.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public class FailFastTestNgIT
+    extends AbstractFailFastIT
+{
+
+    @Parameters(name = "{0}")
+    public static Iterable<Object[]> data()
+    {
+        ArrayList<Object[]> args = new ArrayList<Object[]>();
+        //                        description
+        //                                             profile
+        //                                                       forkCount,
+        //                                                       fail-fast-count
+        //                                                                        total
+        //                                                                             failures
+        //                                                                                     errors
+        //                                                                                           skipped
+        args.add( new Object[] { "testng-oneFork-ff1", null,     props( 1, 1 ),   5,   1,      0,    4 } );
+        args.add( new Object[] { "testng-oneFork-ff2", null,     props( 1, 2 ),   5,   2,      0,    3 } );
+        args.add( new Object[] { "testng-twoForks-ff1", null,    props( 2, 1 ),   5,   1,      0,    4 } );
+        args.add( new Object[] { "testng-twoForks-ff2", null,    props( 2, 2 ),   5,   2,      0,    3 } );
+        args.add( new Object[] { "testng-oneFork-ff3", null,     props( 2, 3 ),   5,   2,      0,    0 } );
+        args.add( new Object[] { "testng-twoForks-ff3", null,    props( 2, 3 ),   5,   2,      0,    0 } );
+        return args;
+    }
+
+    @Override
+    protected String withProvider()
+    {
+        return "testng";
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/62c7cd0d/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java
index ef5dc33..1198fcb 100755
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java
@@ -337,6 +337,15 @@ public class MavenLauncher
         return addGoal( "-D" + variable + "=" + value );
     }
 
+    public MavenLauncher sysProp( Map<String, String> properties )
+    {
+        for ( Map.Entry<String, String> property : properties.entrySet() )
+        {
+            sysProp( property.getKey(), property.getValue() );
+        }
+        return this;
+    }
+
     public MavenLauncher sysProp( String variable, boolean value )
     {
         return addGoal( "-D" + variable + "=" + value );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/62c7cd0d/surefire-integration-tests/src/test/resources/fail-fast-junit/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/fail-fast-junit/pom.xml b/surefire-integration-tests/src/test/resources/fail-fast-junit/pom.xml
new file mode 100644
index 0000000..6a6be11
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/fail-fast-junit/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven.surefire</groupId>
+    <artifactId>it-parent</artifactId>
+    <version>1.0</version>
+  </parent>
+
+  <groupId>org.apache.maven.plugins.surefire</groupId>
+  <artifactId>jiras-surefire-580-junit</artifactId>
+  <version>1.0</version>
+
+  <properties>
+    <junit>4.0</junit>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>${junit}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.5</source>
+          <target>1.5</target>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <runOrder>alphabetical</runOrder>
+          <reuseForks>true</reuseForks>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>junit4</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <dependencies>
+              <dependency>
+                <groupId>org.apache.maven.surefire</groupId>
+                <artifactId>surefire-junit4</artifactId>
+                <version>${surefire.version}</version>
+              </dependency>
+            </dependencies>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>junit47</id>
+      <properties>
+        <junit>4.7</junit>
+      </properties>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <dependencies>
+              <dependency>
+                <groupId>org.apache.maven.surefire</groupId>
+                <artifactId>surefire-junit47</artifactId>
+                <version>${surefire.version}</version>
+              </dependency>
+            </dependencies>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/62c7cd0d/surefire-integration-tests/src/test/resources/fail-fast-testng/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/fail-fast-testng/pom.xml b/surefire-integration-tests/src/test/resources/fail-fast-testng/pom.xml
new file mode 100644
index 0000000..4fb8385
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/fail-fast-testng/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven.surefire</groupId>
+    <artifactId>it-parent</artifactId>
+    <version>1.0</version>
+  </parent>
+
+  <groupId>org.apache.maven.plugins.surefire</groupId>
+  <artifactId>jiras-surefire-580-testng</artifactId>
+  <version>1.0</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <version>5.10</version>
+      <classifier>jdk15</classifier>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.5</source>
+          <target>1.5</target>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <runOrder>alphabetical</runOrder>
+          <reuseForks>true</reuseForks>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>


[04/17] maven-surefire git commit: [SUREFIRE] MasterProcessReader Thread should be quite after throwing exception

Posted by ti...@apache.org.
[SUREFIRE] MasterProcessReader Thread should be quite after throwing exception


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

Branch: refs/heads/master
Commit: 8e37515a06a804a154713daf0b0b53dc319a6e0a
Parents: 67af00f
Author: Tibor17 <ti...@lycos.com>
Authored: Sat Jul 25 03:30:55 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:57:55 2015 +0200

----------------------------------------------------------------------
 .../org/apache/maven/surefire/booter/MasterProcessReader.java  | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/8e37515a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
index 1d86167..2dde2e1 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
@@ -228,10 +228,12 @@ public final class MasterProcessReader
             }
             catch ( IOException e )
             {
+                MasterProcessReader.this.state.set( TERMINATED );
+            }
+            finally
+            {
                 // ensure fail-safe iterator as well as safe to finish in for-each loop using ClassesIterator
                 insert( new Command( TEST_SET_FINISHED ) );
-                // and let us know what has happened with the stream
-                throw new IllegalStateException( e.getLocalizedMessage(), e );
             }
         }
 


[15/17] maven-surefire git commit: [SUREFIRE-580] configuration parameter 'skipAfterFailureCount'

Posted by ti...@apache.org.
[SUREFIRE-580] configuration parameter 'skipAfterFailureCount'


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

Branch: refs/heads/master
Commit: 779e381102a8f882e58c95ad3e50eadca170cba7
Parents: 1b7fe2d
Author: Tibor17 <ti...@lycos.com>
Authored: Sat Sep 5 01:15:07 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:58:04 2015 +0200

----------------------------------------------------------------------
 .../surefire/booterclient/ForkStarter.java      |  12 ++-
 surefire-api/pom.xml                            |   6 ++
 .../util/internal/ConcurrencyUtils.java         |  64 ++++++++++++
 .../java/org/apache/maven/JUnit4SuiteTest.java  |  78 ++++++++++++++
 .../util/internal/ConcurrencyUtilsTest.java     | 104 +++++++++++++++++++
 .../maven/surefire/common/junit4/Notifier.java  |  35 ++++---
 .../maven/surefire/common/junit4/Stoppable.java |  35 +++++++
 .../surefire/junit4/JUnit4FailFastListener.java |  13 +--
 .../maven/surefire/junit4/JUnit4Provider.java   |  11 +-
 .../junitcore/JUnit47FailFastListener.java      |   5 +-
 .../maven/surefire/junitcore/JUnitCore.java     |  11 --
 .../surefire/junitcore/JUnitCoreProvider.java   |   9 +-
 .../surefire/junitcore/JUnitCoreWrapper.java    |  15 ++-
 .../maven/surefire/junitcore/Stoppable.java     |  32 ------
 .../surefire/testng/utils/FailFastListener.java |   3 +-
 .../maven/surefire/testng/utils/Stoppable.java  |   4 +-
 .../testng/TestNGDirectoryTestSuite.java        |  15 +--
 .../maven/surefire/testng/TestNGExecutor.java   |  43 +++++---
 .../maven/surefire/testng/TestNGProvider.java   |  10 +-
 .../surefire/testng/TestNGXmlTestSuite.java     |   8 +-
 20 files changed, 397 insertions(+), 116 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/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 632834f..396e99f 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
@@ -67,7 +67,7 @@ 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.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 import static org.apache.maven.shared.utils.cli.CommandLineUtils.executeCommandLine;
@@ -79,6 +79,7 @@ import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDae
 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.util.internal.ConcurrencyUtils.countDownToZero;
 import static org.apache.maven.surefire.booter.Classpath.join;
 import static org.apache.maven.surefire.booter.SystemPropertyManager.writePropertiesFile;
 import static org.apache.maven.surefire.suite.RunResult.timeout;
@@ -248,7 +249,7 @@ public class ForkStarter
                 testStreams.add( new TestProvidingInputStream( tests ) );
             }
 
-            final AtomicBoolean notifyStreamsToSkipTestsJustNow = new AtomicBoolean();
+            final AtomicInteger notifyStreamsToSkipTestsJustNow = new AtomicInteger();
             Collection<Future<RunResult>> results = new ArrayList<Future<RunResult>>( forkCount );
             for ( final TestProvidingInputStream testProvidingInputStream : testStreams )
             {
@@ -267,7 +268,7 @@ public class ForkStarter
                             @Override
                             protected void stopOnNextTest()
                             {
-                                if ( notifyStreamsToSkipTestsJustNow.compareAndSet( false, true ) )
+                                if ( countDownToZero( notifyStreamsToSkipTestsJustNow ) )
                                 {
                                     notifyStreamsToSkipTests( testStreams );
                                 }
@@ -306,7 +307,8 @@ public class ForkStarter
         executorService.setThreadFactory( threadFactory );
         try
         {
-            final AtomicBoolean notifyStreamsToSkipTestsJustNow = new AtomicBoolean();
+            int failFastCount = providerConfiguration.getSkipAfterFailureCount();
+            final AtomicInteger notifyStreamsToSkipTestsJustNow = new AtomicInteger( failFastCount );
             final TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder();
             for ( final Object testSet : getSuitesIterator() )
             {
@@ -324,7 +326,7 @@ public class ForkStarter
                             @Override
                             protected void stopOnNextTest()
                             {
-                                if ( notifyStreamsToSkipTestsJustNow.compareAndSet( false, true ) )
+                                if ( countDownToZero( notifyStreamsToSkipTestsJustNow ) )
                                 {
                                     builder.getCachableCommands().skipSinceNextTest();
                                 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-api/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-api/pom.xml b/surefire-api/pom.xml
index 6f66e66..3567b93 100644
--- a/surefire-api/pom.xml
+++ b/surefire-api/pom.xml
@@ -42,6 +42,12 @@
     <plugins>
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <redirectTestOutputToFile>true</redirectTestOutputToFile>
+          <includes>
+            <include>**/JUnit4SuiteTest.java</include>
+          </includes>
+        </configuration>
         <dependencies>
           <dependency>
             <groupId>org.apache.maven.surefire</groupId>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ConcurrencyUtils.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ConcurrencyUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ConcurrencyUtils.java
new file mode 100644
index 0000000..6cfc6bf
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ConcurrencyUtils.java
@@ -0,0 +1,64 @@
+package org.apache.maven.surefire.util.internal;
+
+/*
+ * 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.concurrent.atomic.AtomicInteger;
+
+/**
+ * Concurrency utilities.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public final class ConcurrencyUtils
+{
+    private ConcurrencyUtils()
+    {
+        throw new IllegalStateException( "not instantiable constructor" );
+    }
+
+    /**
+     * Decreases {@code counter} to zero, or does not change the counter if negative.
+     * This method pretends been atomic. Only one thread can succeed setting the counter to zero.
+     *
+     * @param counter atomic counter
+     * @return {@code true} if this Thread modified concurrent counter from any positive number down to zero.
+     */
+    @SuppressWarnings( "checkstyle:emptyforiteratorpad" )
+    public static boolean countDownToZero( AtomicInteger counter )
+    {
+        for (;;)
+        {
+            int c = counter.get();
+            if ( c > 0 )
+            {
+                int newCounter = c - 1;
+                if ( counter.compareAndSet( c, newCounter ) )
+                {
+                    return newCounter == 0;
+                }
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
new file mode 100644
index 0000000..c2ec153
--- /dev/null
+++ b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
@@ -0,0 +1,78 @@
+package org.apache.maven;
+
+/*
+ * 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 junit.framework.JUnit4TestAdapter;
+import junit.framework.Test;
+import org.apache.maven.plugin.surefire.runorder.ThreadedExecutionSchedulerTest;
+import org.apache.maven.surefire.SpecificTestClassFilterTest;
+import org.apache.maven.surefire.booter.ForkingRunListenerTest;
+import org.apache.maven.surefire.booter.MasterProcessCommandTest;
+import org.apache.maven.surefire.booter.SurefireReflectorTest;
+import org.apache.maven.surefire.report.LegacyPojoStackTraceWriterTest;
+import org.apache.maven.surefire.suite.RunResultTest;
+import org.apache.maven.surefire.testset.ResolvedTestTest;
+import org.apache.maven.surefire.testset.TestListResolverTest;
+import org.apache.maven.surefire.util.DefaultDirectoryScannerTest;
+import org.apache.maven.surefire.util.RunOrderCalculatorTest;
+import org.apache.maven.surefire.util.RunOrderTest;
+import org.apache.maven.surefire.util.ScanResultTest;
+import org.apache.maven.surefire.util.TestsToRunTest;
+import org.apache.maven.surefire.util.UrlUtilsTest;
+import org.apache.maven.surefire.util.internal.ByteBufferTest;
+import org.apache.maven.surefire.util.internal.ConcurrencyUtilsTest;
+import org.apache.maven.surefire.util.internal.StringUtilsTest;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Adapt the JUnit4 tests which use only annotations to the JUnit3 test suite.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 2.19
+ */
+@Suite.SuiteClasses( {
+    ThreadedExecutionSchedulerTest.class,
+    ForkingRunListenerTest.class,
+    MasterProcessCommandTest.class,
+    SurefireReflectorTest.class,
+    LegacyPojoStackTraceWriterTest.class,
+    RunResultTest.class,
+    ResolvedTestTest.class,
+    TestListResolverTest.class,
+    ByteBufferTest.class,
+    ConcurrencyUtilsTest.class,
+    StringUtilsTest.class,
+    DefaultDirectoryScannerTest.class,
+    RunOrderCalculatorTest.class,
+    RunOrderTest.class,
+    ScanResultTest.class,
+    TestsToRunTest.class,
+    UrlUtilsTest.class,
+    SpecificTestClassFilterTest.class
+} )
+@RunWith( Suite.class )
+public class JUnit4SuiteTest
+{
+    public static Test suite()
+    {
+        return new JUnit4TestAdapter( JUnit4SuiteTest.class );
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ConcurrencyUtilsTest.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ConcurrencyUtilsTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ConcurrencyUtilsTest.java
new file mode 100644
index 0000000..516f885
--- /dev/null
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ConcurrencyUtilsTest.java
@@ -0,0 +1,104 @@
+package org.apache.maven.surefire.util.internal;
+
+/*
+ * 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.junit.Test;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.apache.maven.surefire.util.internal.ConcurrencyUtils.countDownToZero;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Concurrency utilities.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public class ConcurrencyUtilsTest
+{
+
+    @Test
+    public void countDownShouldBeUnchangedAsZero$NegativeTest()
+    {
+        AtomicInteger atomicCounter = new AtomicInteger( 0 );
+        assertFalse( countDownToZero( atomicCounter ) );
+        assertThat( atomicCounter.get(), is( 0 ) );
+    }
+
+    @Test
+    public void countDownShouldBeUnchangedAsNegative$NegativeTest()
+    {
+        AtomicInteger atomicCounter = new AtomicInteger( -1 );
+        assertFalse( countDownToZero( atomicCounter ) );
+        assertThat( atomicCounter.get(), is( -1 ) );
+    }
+
+    @Test
+    public void countDownShouldBeDecreasedByOneThreadModification()
+    {
+        AtomicInteger atomicCounter = new AtomicInteger( 10 );
+        assertFalse( countDownToZero( atomicCounter ) );
+        assertThat( atomicCounter.get(), is( 9 ) );
+    }
+
+    @Test
+    public void countDownToZeroShouldBeDecreasedByOneThreadModification()
+    {
+        AtomicInteger atomicCounter = new AtomicInteger( 1 );
+        assertTrue( countDownToZero( atomicCounter ) );
+        assertThat( atomicCounter.get(), is( 0 ) );
+    }
+
+    @Test
+    public void countDownShouldBeDecreasedByTwoThreadsModification()
+        throws ExecutionException, InterruptedException
+    {
+        final AtomicInteger atomicCounter = new AtomicInteger( 3 );
+
+        FutureTask<Boolean> task = new FutureTask<Boolean>( new Callable<Boolean>()
+        {
+            public Boolean call()
+                throws Exception
+            {
+                return countDownToZero( atomicCounter );
+            }
+        } );
+        Thread t = new Thread( task );
+        t.start();
+
+        assertFalse( countDownToZero( atomicCounter ) );
+
+        assertFalse( task.get() );
+
+        assertThat( atomicCounter.get(), is( 1 ) );
+
+        assertTrue( countDownToZero( atomicCounter ) );
+
+        assertThat( atomicCounter.get(), is( 0 ) );
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java
index e9136e1..5748e4b 100644
--- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java
@@ -25,6 +25,9 @@ import org.junit.runner.notification.RunNotifier;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.apache.maven.surefire.util.internal.ConcurrencyUtils.countDownToZero;
 
 /**
  * Extends {@link RunNotifier JUnit notifier},
@@ -35,31 +38,29 @@ import java.util.Iterator;
  * @since 2.19
  */
 public class Notifier
-    extends RunNotifier
+    extends RunNotifier implements Stoppable
 {
     private final Collection<RunListener> listeners = new ArrayList<RunListener>();
 
-    private JUnit4RunListener reporter;
+    private final AtomicInteger skipAfterFailureCount;
 
-    @Override
-    public void pleaseStop()
+    private final JUnit4RunListener reporter;
+
+    public Notifier( JUnit4RunListener reporter, int skipAfterFailureCount )
     {
-        super.pleaseStop();
-        reporter.testExecutionSkippedByUser();
+        addListener( reporter );
+        this.reporter = reporter;
+        this.skipAfterFailureCount = new AtomicInteger( skipAfterFailureCount );
     }
 
-    /**
-     * Adds reporter listener to the bottom of queue and retrieves old reporter if any exists.
-     *
-     * @param reporter registered listener
-     * @return old listener; or null if did not exist
-     */
-    public JUnit4RunListener setReporter( JUnit4RunListener reporter )
+    public void fireStopEvent()
     {
-        JUnit4RunListener old = this.reporter;
-        this.reporter = reporter;
-        addListener( reporter );
-        return old;
+        if ( countDownToZero( skipAfterFailureCount ) )
+        {
+            pleaseStop();
+        }
+
+        reporter.testExecutionSkippedByUser();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Stoppable.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Stoppable.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Stoppable.java
new file mode 100644
index 0000000..376d631
--- /dev/null
+++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Stoppable.java
@@ -0,0 +1,35 @@
+package org.apache.maven.surefire.common.junit4;
+
+/*
+ * 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.
+ */
+
+/**
+ * Covers configuration parameter <em>skipAfterFailureCount</em>.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ * @see org.apache.maven.surefire.common.junit4.Notifier
+ */
+public interface Stoppable
+{
+    /**
+     * Fire stop even to plugin process and/or call {@link org.junit.runner.notification.RunNotifier#pleaseStop()}.
+     */
+    void fireStopEvent();
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4FailFastListener.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4FailFastListener.java b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4FailFastListener.java
index d45b678..84346e7 100644
--- a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4FailFastListener.java
+++ b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4FailFastListener.java
@@ -19,12 +19,13 @@ package org.apache.maven.surefire.junit4;
  * under the License.
  */
 
+import org.apache.maven.surefire.common.junit4.Notifier;
+import org.apache.maven.surefire.common.junit4.Stoppable;
 import org.junit.runner.notification.Failure;
 import org.junit.runner.notification.RunListener;
-import org.junit.runner.notification.RunNotifier;
 
 /**
- * Calling {@link RunNotifier#pleaseStop()} if failure appeared.
+ * Calling {@link Notifier#fireStopEvent()} if failure happens.
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.19
@@ -32,17 +33,17 @@ import org.junit.runner.notification.RunNotifier;
 final class JUnit4FailFastListener
     extends RunListener
 {
-    private final RunNotifier notifier;
+    private final Stoppable stoppable;
 
-    JUnit4FailFastListener( RunNotifier notifier )
+    JUnit4FailFastListener( Notifier stoppable )
     {
-        this.notifier = notifier;
+        this.stoppable = stoppable;
     }
 
     @Override
     public void testFailure( Failure failure )
         throws Exception
     {
-        notifier.pleaseStop();
+        stoppable.fireStopEvent();
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/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 e8f041d..2276eac 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
@@ -146,8 +146,7 @@ public class JUnit4Provider
 
         startCapture( (ConsoleOutputReceiver) reporter );
 
-        Notifier notifier = new Notifier();
-        notifier.setReporter( new JUnit4RunListener( reporter ) );
+        Notifier notifier = new Notifier( new JUnit4RunListener( reporter ), getSkipAfterFailureCount() );
         if ( isFailFast() )
         {
             notifier.addListener( new JUnit4FailFastListener( notifier ) );
@@ -193,6 +192,11 @@ public class JUnit4Provider
         return providerParameters.getSkipAfterFailureCount() > 0;
     }
 
+    private int getSkipAfterFailureCount()
+    {
+        return isFailFast() && !isRerunFailingTests() ? providerParameters.getSkipAfterFailureCount() : 0;
+    }
+
     private void closeCommandsReader()
     {
         if ( commandsReader != null )
@@ -227,7 +231,8 @@ public class JUnit4Provider
             if ( isFailFast() && e instanceof StoppedByUserException )
             {
                 String reason = e.getClass().getName();
-                notifier.fireTestIgnored( createDescription( clazz.getName(), createIgnored( reason ) ) );
+                Description skippedTest = createDescription( clazz.getName(), createIgnored( reason ) );
+                notifier.fireTestIgnored( skippedTest );
             }
             else
             {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnit47FailFastListener.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnit47FailFastListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnit47FailFastListener.java
index dda6b68..0f6efc9 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnit47FailFastListener.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnit47FailFastListener.java
@@ -19,6 +19,7 @@ package org.apache.maven.surefire.junitcore;
  * under the License.
  */
 
+import org.apache.maven.surefire.common.junit4.Stoppable;
 import org.junit.runner.Description;
 import org.junit.runner.notification.Failure;
 import org.junit.runner.notification.RunListener;
@@ -27,7 +28,7 @@ import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 /**
- * Calling {@link Stoppable#pleaseStop()} if failure appeared.
+ * Calling {@link Stoppable#fireStopEvent()} if failure happens.
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.19
@@ -60,6 +61,6 @@ final class JUnit47FailFastListener
     public void testFailure( Failure failure )
         throws Exception
     {
-        stoppable.pleaseStop();
+        stoppable.fireStopEvent();
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCore.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCore.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCore.java
index 7f1a03a..9935d9a 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCore.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCore.java
@@ -29,15 +29,12 @@ import org.junit.runner.notification.RunListener;
  * JUnitCore solves bugs in original junit class {@link org.junit.runner.JUnitCore}.<p>
  * The notifier method {@link org.junit.runner.notification.RunNotifier#fireTestRunFinished}
  * is called anyway in finally block.
- * This class provides method {@link #pleaseStop()} without any need to retrieve
- * {@link org.junit.runner.notification.RunNotifier} outside.
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.19
  * @see https://github.com/junit-team/junit/issues/1186
  */
 class JUnitCore
-    implements Stoppable
 {
     private final Notifier notifier;
 
@@ -79,12 +76,4 @@ class JUnitCore
     protected void afterFinished()
     {
     }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void pleaseStop()
-    {
-        notifier.pleaseStop();
-    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/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 243cca0..29687ef 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
@@ -145,8 +145,8 @@ public class JUnitCoreProvider
             }
         }
 
-        Notifier notifier = new Notifier();
-        notifier.setReporter( createRunListener( reporterFactory, consoleLogger ) );
+        Notifier notifier =
+            new Notifier( createRunListener( reporterFactory, consoleLogger ), getSkipAfterFailureCount() );
 
         // Add test failure listener
         JUnitTestFailureListener testFailureListener = new JUnitTestFailureListener();
@@ -195,6 +195,11 @@ public class JUnitCoreProvider
         return providerParameters.getSkipAfterFailureCount() > 0;
     }
 
+    private int getSkipAfterFailureCount()
+    {
+        return isFailFast() && !isRerunFailingTests() ? providerParameters.getSkipAfterFailureCount() : 0;
+    }
+
     private void closeCommandsReader()
     {
         if ( commandsReader != null )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/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 e0c9f9e..eb41217 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
@@ -27,6 +27,7 @@ import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.apache.maven.surefire.util.TestsToRun;
 import org.junit.Ignore;
 import org.junit.runner.Computer;
+import org.junit.runner.Description;
 import org.junit.runner.Request;
 import org.junit.runner.Result;
 import org.junit.runner.manipulation.Filter;
@@ -36,7 +37,6 @@ import org.junit.runner.notification.StoppedByUserException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Queue;
-import java.util.Random;
 
 import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createDescription;
 import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createIgnored;
@@ -54,8 +54,6 @@ final class JUnitCoreWrapper
     private final ConsoleLogger logger;
     private final boolean failFast;
 
-    private Object o = new Random().nextInt();
-
     JUnitCoreWrapper( Notifier notifier, JUnitCoreParameters jUnitCoreParameters, ConsoleLogger logger,
                       boolean failFast )
     {
@@ -86,7 +84,7 @@ final class JUnitCoreWrapper
 
     private JUnitCore createJUnitCore( final Notifier notifier, Collection<RunListener> listeners )
     {
-        JUnitCore junitCore = new JUnitCore( notifier );
+        JUnitCore junitCore = new JUnitCore();
 
         // custom listeners added last
         notifier.addListeners( listeners );
@@ -154,10 +152,10 @@ final class JUnitCoreWrapper
     {
         private final JUnit47FailFastListener failFastListener;
 
-        JUnitCore( Notifier notifier )
+        JUnitCore()
         {
             super( notifier );
-            failFastListener = failFast ? new JUnit47FailFastListener( this ) : null;
+            failFastListener = failFast ? new JUnit47FailFastListener( notifier ) : null;
             if ( failFastListener != null )
             {
                 notifier.addListener( failFastListener );
@@ -188,10 +186,11 @@ final class JUnitCoreWrapper
                 if ( stoppedTests != null )
                 {
                     String reason = e.getClass().getName();
-                    Ignore reasonForIgnoredTest = createIgnored( reason );
+                    Ignore reasonForSkippedTest = createIgnored( reason );
                     for ( String clazz; ( clazz = stoppedTests.poll() ) != null; )
                     {
-                        notifier.fireTestIgnored( createDescription( clazz, reasonForIgnoredTest ) );
+                        Description skippedTest = createDescription( clazz, reasonForSkippedTest );
+                        notifier.fireTestIgnored( skippedTest );
                     }
                 }
             }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/Stoppable.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/Stoppable.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/Stoppable.java
deleted file mode 100644
index 2450e64..0000000
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/Stoppable.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.apache.maven.surefire.junitcore;
-
-/*
- * 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.
- */
-
-/**
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
- */
-interface Stoppable
-{
-    /**
-     * Delegates this call to {@link org.junit.runner.notification.RunNotifier#pleaseStop()}.
-     */
-    void pleaseStop();
-}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastListener.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastListener.java b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastListener.java
index d3d5c00..91f3461 100644
--- a/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastListener.java
+++ b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/FailFastListener.java
@@ -51,8 +51,7 @@ public class FailFastListener
 
     public void onTestFailure( ITestResult result )
     {
-        FailFastEventsSingleton.getInstance().setSkipOnNextTest();
-        stoppable.pleaseStop();
+        stoppable.fireStopEvent();
     }
 
     public void onTestSkipped( ITestResult result )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/Stoppable.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/Stoppable.java b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/Stoppable.java
index c7be682..dd172d2 100644
--- a/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/Stoppable.java
+++ b/surefire-providers/surefire-testng-utils/src/main/java/org/apache/maven/surefire/testng/utils/Stoppable.java
@@ -20,6 +20,8 @@ package org.apache.maven.surefire.testng.utils;
  */
 
 /**
+ * Covers configuration parameter <em>skipAfterFailureCount</em>.
+ *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.19
  */
@@ -28,5 +30,5 @@ public interface Stoppable
     /**
      * Delegates this call to {@link org.apache.maven.surefire.report.RunListener#testExecutionSkippedByUser()}.
      */
-    void pleaseStop();
+    void fireStopEvent();
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
index 5297276..a721bbc 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
@@ -79,11 +79,12 @@ public class TestNGDirectoryTestSuite
 
     private final List<CommandLineOption> mainCliOptions;
 
-    private final boolean isFailFast;
+    private final int skipAfterFailureCount;
 
     public TestNGDirectoryTestSuite( String testSourceDirectory, Map<String, String> confOptions, File reportsDirectory,
                                      TestListResolver methodFilter, RunOrderCalculator runOrderCalculator,
-                                     ScanResult scanResult, List<CommandLineOption> mainCliOptions, boolean isFailFast )
+                                     ScanResult scanResult, List<CommandLineOption> mainCliOptions,
+                                     int skipAfterFailureCount )
     {
         this.runOrderCalculator = runOrderCalculator;
         this.options = confOptions;
@@ -96,7 +97,7 @@ public class TestNGDirectoryTestSuite
         this.junitTestAnnotation = findJUnitTestAnnotation();
         this.junitOptions = createJUnitOptions();
         this.mainCliOptions = mainCliOptions;
-        this.isFailFast = isFailFast;
+        this.skipAfterFailureCount = skipAfterFailureCount;
     }
 
     public void execute( TestsToRun testsToRun, ReporterFactory reporterManagerFactory )
@@ -131,7 +132,7 @@ public class TestNGDirectoryTestSuite
         final Map<String, String> optionsToUse = isJUnitTest( testClass ) ? junitOptions : options;
 
         TestNGExecutor.run( new Class<?>[]{ testClass }, testSourceDirectory, optionsToUse, reporter, this,
-                            reportsDirectory, methodFilter, mainCliOptions, isFailFast );
+                            reportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
 
         finishTestSuite( reporter, this );
     }
@@ -221,14 +222,14 @@ public class TestNGDirectoryTestSuite
         Class<?>[] testClasses = testNgTestClasses.toArray( new Class<?>[testNgTestClasses.size()] );
 
         TestNGExecutor.run( testClasses, testSourceDirectory, options, reporterManager, this,
-                            testNgReportsDirectory, methodFilter, mainCliOptions, isFailFast );
+                            testNgReportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
 
         if ( !junitTestClasses.isEmpty() )
         {
             testClasses = junitTestClasses.toArray( new Class[junitTestClasses.size()] );
 
             TestNGExecutor.run( testClasses, testSourceDirectory, junitOptions, reporterManager, this,
-                                junitReportsDirectory, methodFilter, mainCliOptions, isFailFast );
+                                junitReportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
         }
 
         finishTestSuite( reporterManager, this );
@@ -298,7 +299,7 @@ public class TestNGDirectoryTestSuite
         startTestSuite( reporter, this );
 
         TestNGExecutor.run( new Class<?>[] { testSet.getTestClass() }, testSourceDirectory, options, reporter,
-                            this, reportsDirectory, methodFilter, mainCliOptions, isFailFast );
+                            this, reportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
 
         finishTestSuite( reporter, this );
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
index d481bb2..5a91db0 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java
@@ -23,6 +23,7 @@ import org.apache.maven.surefire.booter.ProviderParameterNames;
 import org.apache.maven.surefire.cli.CommandLineOption;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.testng.conf.Configurator;
+import org.apache.maven.surefire.testng.utils.FailFastEventsSingleton;
 import org.apache.maven.surefire.testng.utils.FailFastListener;
 import org.apache.maven.surefire.testng.utils.Stoppable;
 import org.apache.maven.surefire.testset.TestListResolver;
@@ -45,8 +46,10 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.apache.maven.surefire.util.ReflectionUtils.instantiate;
+import static org.apache.maven.surefire.util.internal.ConcurrencyUtils.countDownToZero;
 
 /**
  * Contains utility methods for executing TestNG.
@@ -73,7 +76,8 @@ public class TestNGExecutor
     public static void run( Class<?>[] testClasses, String testSourceDirectory,
                             Map<String, String> options, // string,string because TestNGMapConfigurator#configure()
                             RunListener reportManager, TestNgTestSuite suite, File reportsDirectory,
-                            TestListResolver methodFilter, List<CommandLineOption> mainCliOptions, boolean isFailFast )
+                            TestListResolver methodFilter, List<CommandLineOption> mainCliOptions,
+                            int skipAfterFailureCount )
         throws TestSetFailedException
     {
         TestNG testng = new TestNG( true );
@@ -123,7 +127,7 @@ public class TestNGExecutor
 
         testng.setXmlSuites( xmlSuites );
         configurator.configure( testng, options );
-        postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory, isFailFast );
+        postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory, skipAfterFailureCount );
         testng.run();
     }
 
@@ -265,13 +269,13 @@ public class TestNGExecutor
     public static void run( List<String> suiteFiles, String testSourceDirectory,
                             Map<String, String> options, // string,string because TestNGMapConfigurator#configure()
                             RunListener reportManager, TestNgTestSuite suite, File reportsDirectory,
-                            boolean isFailFast )
+                            int skipAfterFailureCount )
         throws TestSetFailedException
     {
         TestNG testng = new TestNG( true );
         Configurator configurator = getConfigurator( options.get( "testng.configurator" ) );
         configurator.configure( testng, options );
-        postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory, isFailFast );
+        postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory, skipAfterFailureCount );
         testng.setTestSuites( suiteFiles );
         testng.run();
     }
@@ -297,7 +301,7 @@ public class TestNGExecutor
     }
 
     private static void postConfigure( TestNG testNG, String sourcePath, final RunListener reportManager,
-                                       TestNgTestSuite suite, File reportsDirectory, boolean skipAfterFailure )
+                                       TestNgTestSuite suite, File reportsDirectory, int skipAfterFailureCount )
         throws TestSetFailedException
     {
         // turn off all TestNG output
@@ -306,19 +310,12 @@ public class TestNGExecutor
         TestNGReporter reporter = createTestNGReporter( reportManager, suite );
         testNG.addListener( (Object) reporter );
 
-        if ( skipAfterFailure )
+        if ( skipAfterFailureCount > 0 )
         {
             ClassLoader cl = Thread.currentThread().getContextClassLoader();
             testNG.addListener( instantiate( cl, "org.apache.maven.surefire.testng.utils.FailFastNotifier",
                                              Object.class ) );
-            Stoppable stoppable = new Stoppable()
-            {
-                public void pleaseStop()
-                {
-                    reportManager.testExecutionSkippedByUser();
-                }
-            };
-            testNG.addListener( new FailFastListener( stoppable ) );
+            testNG.addListener( new FailFastListener( createStoppable( reportManager, skipAfterFailureCount ) ) );
         }
 
         // FIXME: use classifier to decide if we need to pass along the source dir (only for JDK14)
@@ -330,6 +327,24 @@ public class TestNGExecutor
         testNG.setOutputDirectory( reportsDirectory.getAbsolutePath() );
     }
 
+    private static Stoppable createStoppable( final RunListener reportManager, int skipAfterFailureCount )
+    {
+        final AtomicInteger currentFaultCount = new AtomicInteger( skipAfterFailureCount );
+
+        return new Stoppable()
+        {
+            public void fireStopEvent()
+            {
+                if ( countDownToZero( currentFaultCount ) )
+                {
+                    FailFastEventsSingleton.getInstance().setSkipOnNextTest();
+                }
+
+                reportManager.testExecutionSkippedByUser();
+            }
+        };
+    }
+
     // If we have access to IResultListener, return a ConfigurationAwareTestNGReporter
     // But don't cause NoClassDefFoundErrors if it isn't available; just return a regular TestNGReporter instead
     private static TestNGReporter createTestNGReporter( RunListener reportManager, TestNgTestSuite suite )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
index 8777ceb..77b5c75 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
@@ -149,6 +149,11 @@ public class TestNGProvider
         return providerParameters.getSkipAfterFailureCount() > 0;
     }
 
+    private int getSkipAfterFailureCount()
+    {
+        return isFailFast() ? providerParameters.getSkipAfterFailureCount() : 0;
+    }
+
     private void closeCommandsReader()
     {
         if ( commandsReader != null )
@@ -174,7 +179,8 @@ public class TestNGProvider
     {
         return new TestNGDirectoryTestSuite( testRequest.getTestSourceDirectory().toString(), providerProperties,
                                              reporterConfiguration.getReportsDirectory(), createMethodFilter(),
-                                             runOrderCalculator, scanResult, mainCliOptions, isFailFast() );
+                                             runOrderCalculator, scanResult, mainCliOptions,
+                                             getSkipAfterFailureCount() );
     }
 
     private TestNGXmlTestSuite newXmlSuite()
@@ -182,7 +188,7 @@ public class TestNGProvider
         return new TestNGXmlTestSuite( testRequest.getSuiteXmlFiles(),
                                        testRequest.getTestSourceDirectory().toString(),
                                        providerProperties,
-                                       reporterConfiguration.getReportsDirectory(), isFailFast() );
+                                       reporterConfiguration.getReportsDirectory(), getSkipAfterFailureCount() );
     }
 
     @SuppressWarnings( "unchecked" )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/779e3811/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
index bf105fe..ab9ae7f 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGXmlTestSuite.java
@@ -49,7 +49,7 @@ public class TestNGXmlTestSuite
 
     private final File reportsDirectory;
 
-    private final boolean isFailFast;
+    private final int skipAfterFailureCount;
 
     // Not really used
     private Map<File, String> testSets;
@@ -59,13 +59,13 @@ public class TestNGXmlTestSuite
      * xml file(s). The XML files are suite definitions files according to TestNG DTD.
      */
     public TestNGXmlTestSuite( List<File> suiteFiles, String testSourceDirectory, Map<String, String> confOptions,
-                               File reportsDirectory, boolean isFailFast )
+                               File reportsDirectory, int skipAfterFailureCount )
     {
         this.suiteFiles = suiteFiles;
         this.options = confOptions;
         this.testSourceDirectory = testSourceDirectory;
         this.reportsDirectory = reportsDirectory;
-        this.isFailFast = isFailFast;
+        this.skipAfterFailureCount = skipAfterFailureCount;
     }
 
     public void execute( ReporterFactory reporterManagerFactory )
@@ -81,7 +81,7 @@ public class TestNGXmlTestSuite
 
         TestNGDirectoryTestSuite.startTestSuite( reporter, this );
         TestNGExecutor.run( suiteFilePaths, testSourceDirectory, options, reporter, this, reportsDirectory,
-                            isFailFast );
+                            skipAfterFailureCount );
         TestNGDirectoryTestSuite.finishTestSuite( reporter, this );
     }
 


[11/17] maven-surefire git commit: [SUREFIRE-580] sanity check

Posted by ti...@apache.org.
[SUREFIRE-580] sanity check


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

Branch: refs/heads/master
Commit: 3eb6fa7259846ec514f943cbf0b16450efde56b0
Parents: 2a944f0
Author: Tibor17 <ti...@lycos.com>
Authored: Thu Sep 3 11:20:29 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:58:00 2015 +0200

----------------------------------------------------------------------
 .../plugin/surefire/AbstractSurefireMojo.java   | 23 +++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/3eb6fa72/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 f847604..e8f1f75 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
@@ -826,6 +826,7 @@ public abstract class AbstractSurefireMojo
             ensureThreadCountWithPerThread();
             warnIfUselessUseSystemClassLoaderParameter();
             warnIfDefunctGroupsCombinations();
+            warnIfRerunClashes();
         }
         return true;
     }
@@ -2256,7 +2257,7 @@ public abstract class AbstractSurefireMojo
     }
 
 
-    void ensureWorkingDirectoryExists()
+    private void ensureWorkingDirectoryExists()
         throws MojoFailureException
     {
         if ( getWorkingDirectory() == null )
@@ -2286,7 +2287,7 @@ public abstract class AbstractSurefireMojo
         }
     }
 
-    void ensureParallelRunningCompatibility()
+    private void ensureParallelRunningCompatibility()
         throws MojoFailureException
     {
         if ( isMavenParallel() && isNotForking() )
@@ -2295,7 +2296,7 @@ public abstract class AbstractSurefireMojo
         }
     }
 
-    void ensureThreadCountWithPerThread()
+    private void ensureThreadCountWithPerThread()
         throws MojoFailureException
     {
         if ( ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) && getThreadCount() < 1 )
@@ -2304,7 +2305,7 @@ public abstract class AbstractSurefireMojo
         }
     }
 
-    void warnIfUselessUseSystemClassLoaderParameter()
+    private void warnIfUselessUseSystemClassLoaderParameter()
     {
         if ( isUseSystemClassLoader() && isNotForking() )
         {
@@ -2322,7 +2323,7 @@ public abstract class AbstractSurefireMojo
         return SurefireHelper.commandLineOptions( getSession(), getLog() );
     }
 
-    void warnIfDefunctGroupsCombinations()
+    private void warnIfDefunctGroupsCombinations()
         throws MojoFailureException, MojoExecutionException
     {
         if ( isAnyGroupsSelected() )
@@ -2348,6 +2349,18 @@ public abstract class AbstractSurefireMojo
         }
     }
 
+    private void warnIfRerunClashes()
+        throws MojoFailureException
+    {
+        boolean isRerun = getRerunFailingTestsCount() > 0;
+        boolean isFailFast = getSkipAfterFailureCount() > 0;
+        if ( isRerun && isFailFast )
+        {
+            throw new MojoFailureException( "Parameters [rerunFailingTestsCount, skipAfterFailureCount] "
+                                                + "should not be enabled together." );
+        }
+    }
+
     final class TestNgProviderInfo
         implements ProviderInfo
     {


[10/17] maven-surefire git commit: [SUREFIRE-580] Allow "fail fast" or stop running on first failure

Posted by ti...@apache.org.
[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 <ti...@lycos.com>
Authored: Thu Sep 3 01:33:26 2015 +0200
Committer: Tibor17 <ti...@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:tibordigana@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:tibordigana@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:tibordigana@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 ) );
+    }
+}


[06/17] maven-surefire git commit: [SUREFIRE] refactoring

Posted by ti...@apache.org.
[SUREFIRE] refactoring


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

Branch: refs/heads/master
Commit: 8c34806bd7d2a2e384823a6febe1f4131a9c5ee5
Parents: d755ecf
Author: Tibor17 <ti...@lycos.com>
Authored: Wed Aug 5 00:00:28 2015 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Sun Sep 6 22:57:57 2015 +0200

----------------------------------------------------------------------
 .../common/junit4/JUnit4ProviderUtilTest.java   |  2 --
 .../common/junit4/JUnit4Reflector40Test.java    |  4 +--
 .../common/junit4/JUnit4RunListenerTest.java    |  5 +--
 .../surefire/junit4/JUnit4TestCheckerTest.java  | 38 +++++++-------------
 4 files changed, 16 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/8c34806b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtilTest.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtilTest.java b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtilTest.java
index 6a6e2e7..b05a562 100644
--- a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtilTest.java
+++ b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4ProviderUtilTest.java
@@ -21,12 +21,10 @@ package org.apache.maven.surefire.common.junit4;
 
 import junit.framework.TestCase;
 import org.apache.maven.surefire.testset.TestSetFailedException;
-import org.apache.maven.surefire.util.TestsToRun;
 import org.junit.runner.Description;
 import org.junit.runner.notification.Failure;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/8c34806b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector40Test.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector40Test.java b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector40Test.java
index 979adb8..28b5fed 100644
--- a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector40Test.java
+++ b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4Reflector40Test.java
@@ -19,7 +19,6 @@ package org.apache.maven.surefire.common.junit4;
  */
 
 import junit.framework.TestCase;
-import org.junit.Assert;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.Description;
@@ -30,12 +29,11 @@ import org.junit.runner.Description;
 public class JUnit4Reflector40Test
     extends TestCase
 {
-    @Test
     public void testGetAnnotatedIgnore()
     {
         Description desc = Description.createTestDescription( IgnoreWithDescription.class, "testSomething2" );
         Ignore annotatedIgnore = JUnit4Reflector.getAnnotatedIgnore( desc );
-        Assert.assertNull( annotatedIgnore );
+        assertNull( annotatedIgnore );
     }
 
     private static final String reason = "Ignorance is bliss";

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/8c34806b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4RunListenerTest.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4RunListenerTest.java b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4RunListenerTest.java
index c4765e9..deda538 100644
--- a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4RunListenerTest.java
+++ b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/common/junit4/JUnit4RunListenerTest.java
@@ -20,6 +20,8 @@ package org.apache.maven.surefire.common.junit4;
  */
 
 import java.util.concurrent.CountDownLatch;
+
+import junit.framework.TestCase;
 import org.apache.maven.surefire.junit4.MockReporter;
 
 import junit.framework.Assert;
@@ -36,8 +38,8 @@ import org.junit.runner.notification.RunNotifier;
  * @author Kristian Rosenvold
  */
 public class JUnit4RunListenerTest
+    extends TestCase
 {
-    @Test
     public void testTestStarted()
         throws Exception
     {
@@ -48,7 +50,6 @@ public class JUnit4RunListenerTest
         junitTestRunner.run( runNotifier );
     }
 
-    @Test
     public void testParallelInvocations()
         throws Exception
     {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/8c34806b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/junit4/JUnit4TestCheckerTest.java
----------------------------------------------------------------------
diff --git a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/junit4/JUnit4TestCheckerTest.java b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/junit4/JUnit4TestCheckerTest.java
index 3f2ed2b..a9f1b80 100644
--- a/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/junit4/JUnit4TestCheckerTest.java
+++ b/surefire-providers/common-junit4/src/test/java/org/apache/maven/surefire/junit4/JUnit4TestCheckerTest.java
@@ -33,91 +33,77 @@ import org.junit.runner.Runner;
 import org.junit.runner.notification.RunNotifier;
 import org.junit.runners.Suite;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 /**
  * @author Kristian Rosenvold
  */
 public class JUnit4TestCheckerTest
+    extends TestCase
 {
     private final JUnit4TestChecker jUnit4TestChecker = new JUnit4TestChecker( this.getClass().getClassLoader() );
 
-    @Test
-    public void validJunit4Annotated()
+    public void testValidJunit4Annotated()
         throws TestSetFailedException
     {
         assertTrue( jUnit4TestChecker.accept( JUnit4TestCheckerTest.class ) );
     }
 
-    @Test
-    public void validJunit4itsAJunit3Test()
+    public void testValidJunit4itsAJunit3Test()
         throws TestSetFailedException
     {
         assertTrue( jUnit4TestChecker.accept( AlsoValid.class ) );
     }
 
-    @Test
-    public void validJunitSubclassWithoutOwnTestmethods()
+    public void testValidJunitSubclassWithoutOwnTestmethods()
         throws TestSetFailedException
     {
         assertTrue( jUnit4TestChecker.accept( SubClassWithoutOwnTestMethods.class ) );
     }
 
-    @Test
-    public void validSuite()
+    public void testValidSuite()
         throws TestSetFailedException
     {
         assertTrue( jUnit4TestChecker.accept( SuiteValid1.class ) );
     }
 
-    @Test
-    public void validCustomSuite()
+    public void testValidCustomSuite()
         throws TestSetFailedException
     {
         assertTrue( jUnit4TestChecker.accept( SuiteValid2.class ) );
     }
 
-    @Test
-    public void validCustomRunner()
+    public void testValidCustomRunner()
         throws TestSetFailedException
     {
         assertTrue( jUnit4TestChecker.accept( SuiteValidCustomRunner.class ) );
     }
 
-    @Test
-    public void invalidTest()
+    public void testInvalidTest()
         throws TestSetFailedException
     {
         assertFalse( jUnit4TestChecker.accept( NotValidTest.class ) );
     }
 
-    @Test
-    public void dontAcceptAbstractClasses()
+    public void testDontAcceptAbstractClasses()
     {
         assertFalse( jUnit4TestChecker.accept( BaseClassWithTest.class ) );
     }
 
-    @Test
-    public void suiteOnlyTest()
+    public void testSuiteOnlyTest()
     {
         assertTrue( jUnit4TestChecker.accept( SuiteOnlyTest.class ) );
     }
 
-    @Test
-    public void customSuiteOnlyTest()
+    public void testCustomSuiteOnlyTest()
     {
         assertTrue( jUnit4TestChecker.accept( CustomSuiteOnlyTest.class ) );
     }
 
-    @Test
-    public void innerClassNotAutomaticallyTc()
+    public void testInnerClassNotAutomaticallyTc()
     {
         assertTrue( jUnit4TestChecker.accept( NestedTC.class ) );
         assertFalse( jUnit4TestChecker.accept( NestedTC.Inner.class ) );
     }
 
-    @Test
     public void testCannotLoadRunWithAnnotation()
         throws Exception
     {