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:tibordig...@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:tibordig...@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:tibordig...@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:tibordig...@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:tibordig...@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:tibordig...@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 );
             }
         }
     }

Reply via email to