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

Reply via email to