[SUREFIRE-1710] skipAfterFailureCount in JUnit5 provider
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index 40ed199..26208a6 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
@@ -3054,30 +3054,43 @@
         {
             try
             {
-                Artifact testng = getTestNgArtifact();
-                if ( testng != null )
+                Artifact junit5 = getJUnit5Artifact();
+                if ( junit5 != null )
                 {
-                    VersionRange range = VersionRange.createFromVersionSpec( "[5.10,)" );
-                    if ( !range.containsVersion( new DefaultArtifactVersion( testng.getVersion() ) ) )
+                    VersionRange range = VersionRange.createFromVersionSpec( "[1.4.0,)" );
+                    if ( !range.containsVersion( new DefaultArtifactVersion( junit5.getVersion() ) ) )
                     {
                         throw new MojoFailureException(
-                            "Parameter \"skipAfterFailureCount\" expects TestNG Version 5.10 or higher. "
-                                + "java.lang.NoClassDefFoundError: org/testng/IInvokedMethodListener" );
+                            "Parameter \"skipAfterFailureCount\" expects JUnit5 Version 5.4.0 or higher." );
                     }
                 }
                 else
                 {
-                    // TestNG is dependent on JUnit
-                    Artifact junit = getJunitArtifact();
-                    if ( junit != null )
+                    Artifact testng = getTestNgArtifact();
+                    if ( testng != null )
                     {
-                        VersionRange range = VersionRange.createFromVersionSpec( "[4.0,)" );
-                        if ( !range.containsVersion( new DefaultArtifactVersion( junit.getVersion() ) ) )
+                        VersionRange range = VersionRange.createFromVersionSpec( "[5.10,)" );
+                        if ( !range.containsVersion( new DefaultArtifactVersion( testng.getVersion() ) ) )
                         {
                             throw new MojoFailureException(
-                                "Parameter \"skipAfterFailureCount\" expects JUnit Version 4.0 or higher. "
-                                    + "java.lang.NoSuchMethodError: "
-                                    + "org.junit.runner.notification.RunNotifier.pleaseStop()V" );
+                                "Parameter \"skipAfterFailureCount\" expects TestNG Version 5.10 or higher. "
+                                    + "java.lang.NoClassDefFoundError: org/testng/IInvokedMethodListener" );
+                        }
+                    }
+                    else
+                    {
+                        // TestNG is dependent on JUnit
+                        Artifact junit = getJunitArtifact();
+                        if ( junit != null )
+                        {
+                            VersionRange range = VersionRange.createFromVersionSpec( "[4.0,)" );
+                            if ( !range.containsVersion( new DefaultArtifactVersion( junit.getVersion() ) ) )
+                            {
+                                throw new MojoFailureException(
+                                    "Parameter \"skipAfterFailureCount\" expects JUnit Version 4.0 or higher. "
+                                        + "java.lang.NoSuchMethodError: "
+                                        + "org.junit.runner.notification.RunNotifier.pleaseStop()V" );
+                            }
                         }
                     }
                 }
diff --git a/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm b/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
index 258f88b..5714a28 100644
--- a/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/examples/skip-after-failure.apt.vm
@@ -42,7 +42,7 @@
 Prerequisite
  
  Use ${project.artifactId} 2.19 or higher, JUnit 4.0 or higher, or 
- TestNG 5.10 or higher.
+ TestNG 5.10 or higher, JUnit5 5.4.0 or higher.
  
  If version of TestNG is lower than 5.10 while the parameter 
  <<<skipAfterFailureCount>>> is set, the plugin fails with error:
diff --git a/pom.xml b/pom.xml
index ab43bbd..46ab25f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -296,12 +296,6 @@
         <groupId>org.powermock</groupId>
         <artifactId>powermock-reflect</artifactId>
         <version>${powermockVersion}</version>
-        <exclusions>
-          <exclusion>
-            <groupId>org.objenesis</groupId>
-            <artifactId>objenesis</artifactId>
-          </exclusion>
-        </exclusions>
       </dependency>
       <dependency>
         <groupId>org.javassist</groupId>
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/FailFastJUnit5IT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/FailFastJUnit5IT.java
new file mode 100644
index 0000000..5759edb
--- /dev/null
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/FailFastJUnit5IT.java
@@ -0,0 +1,61 @@
+package org.apache.maven.surefire.its;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ *
+ */
+public class FailFastJUnit5IT
+    extends AbstractFailFastIT
+{
+
+    @Parameters( name = "{0}" )
+    @SuppressWarnings( "checkstyle:linelength" )
+    public static Iterable<Object[]> data()
+    {
+        /*
+         * reuseForks=false is not used because of race conditions between forks.
+         */
+        ArrayList<Object[]> args = new ArrayList<>();
+        //                        description
+        //                                             profile
+        //                                                       forkCount,
+        //                                                       fail-fast-count,
+        //                                                       reuseForks
+        //                                                                               total
+        //                                                                                    failures
+        //                                                                                            errors
+        //                                                                                                  skipped
+        //                                                                                                        pipes
+        args.add( new Object[] { "fc1", null,     props( 1, 3, true ),    5,   0,      3,    0, true } );
+        args.add( new Object[] { "fc2", null,     props( 2, 3, true ),    5,   0,      3,    0, true } );
+        return args;
+    }
+
+    @Override
+    protected String withProvider()
+    {
+        return "junit5";
+    }
+}
diff --git a/surefire-its/src/test/resources/fail-fast-junit5/pom.xml b/surefire-its/src/test/resources/fail-fast-junit5/pom.xml
new file mode 100644
index 0000000..3390fe8
--- /dev/null
+++ b/surefire-its/src/test/resources/fail-fast-junit5/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.plugins.surefire</groupId>
+  <artifactId>fail-fast-junit5</artifactId>
+  <version>1.0</version>
+
+  <properties>
+    <maven.compiler.source>${java.specification.version}</maven.compiler.source>
+    <maven.compiler.target>${java.specification.version}</maven.compiler.target>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <version>5.8.2</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>${surefire.version}</version>
+        <configuration>
+          <runOrder>alphabetical</runOrder>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/ATest.java b/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/ATest.java
new file mode 100644
index 0000000..1db9bb2
--- /dev/null
+++ b/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/ATest.java
@@ -0,0 +1,18 @@
+package pkg;
+
+import org.junit.jupiter.api.Test;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class ATest
+{
+    @Test
+    public void someMethod()
+        throws InterruptedException
+    {
+        MILLISECONDS.sleep( 1000L );
+        throw new RuntimeException( "assert \"foo\" == \"bar\"\n" +
+            "             |\n"
+            + "             false" );
+    }
+}
diff --git a/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/BTest.java b/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/BTest.java
new file mode 100644
index 0000000..aee436f
--- /dev/null
+++ b/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/BTest.java
@@ -0,0 +1,17 @@
+package pkg;
+
+import org.junit.jupiter.api.Test;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class BTest
+{
+    @Test
+    public void test()
+        throws InterruptedException
+    {
+        MILLISECONDS.sleep( 1000L );
+        throw new RuntimeException();
+    }
+
+}
diff --git a/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/CTest.java b/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/CTest.java
new file mode 100644
index 0000000..f8fceb2
--- /dev/null
+++ b/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/CTest.java
@@ -0,0 +1,16 @@
+package pkg;
+
+import org.junit.jupiter.api.Test;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class CTest
+{
+    @Test
+    public void test()
+        throws InterruptedException
+    {
+        MILLISECONDS.sleep( 1000L );
+    }
+
+}
diff --git a/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/DTest.java b/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/DTest.java
new file mode 100644
index 0000000..0e99270
--- /dev/null
+++ b/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/DTest.java
@@ -0,0 +1,16 @@
+package pkg;
+
+import org.junit.jupiter.api.Test;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class DTest
+{
+    @Test
+    public void test()
+        throws InterruptedException
+    {
+        MILLISECONDS.sleep( 1000L );
+    }
+
+}
diff --git a/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/ETest.java b/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/ETest.java
new file mode 100644
index 0000000..50fb60e
--- /dev/null
+++ b/surefire-its/src/test/resources/fail-fast-junit5/src/test/java/pkg/ETest.java
@@ -0,0 +1,12 @@
+package pkg;
+
+import org.junit.jupiter.api.Test;
+
+public class ETest
+{
+    @Test
+    public void test()
+    {
+    }
+
+}
diff --git a/surefire-providers/common-junit5/pom.xml b/surefire-providers/common-junit5/pom.xml
new file mode 100644
index 0000000..b8cbac6
--- /dev/null
+++ b/surefire-providers/common-junit5/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven.surefire</groupId>
+    <artifactId>surefire-providers</artifactId>
+    <version>3.0.0-M6-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>common-junit5</artifactId>
+  <name>Shared JUnit5 Provider Code</name>
+  <description>Shared JUnit5 Provider Code</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-api</artifactId>
+      <version>5.4.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.code.findbugs</groupId>
+      <artifactId>jsr305</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.powermock</groupId>
+      <artifactId>powermock-reflect</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/surefire-providers/common-junit5/src/main/java/org/apache/maven/surefire/common/junit5/SkipTestsAfterFailureSingleton.java b/surefire-providers/common-junit5/src/main/java/org/apache/maven/surefire/common/junit5/SkipTestsAfterFailureSingleton.java
new file mode 100644
index 0000000..d4c6133
--- /dev/null
+++ b/surefire-providers/common-junit5/src/main/java/org/apache/maven/surefire/common/junit5/SkipTestsAfterFailureSingleton.java
@@ -0,0 +1,91 @@
+package org.apache.maven.surefire.common.junit5;
+
+/*
+ * 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 javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.apache.maven.surefire.api.util.internal.ConcurrencyUtils.runIfZeroCountDown;
+
+/**
+ *
+ */
+public final class SkipTestsAfterFailureSingleton
+{
+    private static final SkipTestsAfterFailureSingleton SINGLETON = new SkipTestsAfterFailureSingleton();
+
+    public static SkipTestsAfterFailureSingleton getSingleton()
+    {
+        return SINGLETON;
+    }
+
+    private final AtomicInteger skipAfterFailureCount = new AtomicInteger();
+    private volatile boolean active;
+    private volatile Runnable onThresholdReached;
+    private volatile boolean disableTests;
+
+    private SkipTestsAfterFailureSingleton()
+    {
+    }
+
+    void setDisableTests( boolean disableTests )
+    {
+        this.disableTests = disableTests;
+    }
+
+    public void setDisableTests()
+    {
+        setDisableTests( true );
+    }
+
+    public boolean isDisableTests()
+    {
+        return disableTests;
+    }
+
+    public void setReRunMode()
+    {
+        active = false;
+    }
+
+    /**
+     * Sets the threshold of failure count in current JVM.
+     *
+     * @param count threshold
+     */
+    public void init( @Nonnegative int count, @Nonnull Runnable onThresholdReached )
+    {
+        this.onThresholdReached = onThresholdReached;
+        active = count > 0;
+        skipAfterFailureCount.set( count );
+    }
+
+    public void runIfFailureCountReachedThreshold()
+    {
+        if ( active )
+        {
+            runIfZeroCountDown( () -> disableTests = true, skipAfterFailureCount );
+            onThresholdReached.run();
+        }
+    }
+
+}
diff --git a/surefire-providers/common-junit5/src/main/java/org/apache/maven/surefire/common/junit5/TestExecutionWatcherSPI.java b/surefire-providers/common-junit5/src/main/java/org/apache/maven/surefire/common/junit5/TestExecutionWatcherSPI.java
new file mode 100644
index 0000000..07f54b7
--- /dev/null
+++ b/surefire-providers/common-junit5/src/main/java/org/apache/maven/surefire/common/junit5/TestExecutionWatcherSPI.java
@@ -0,0 +1,75 @@
+package org.apache.maven.surefire.common.junit5;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.ConditionEvaluationResult;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExecutionCondition;
+import org.junit.jupiter.api.extension.TestWatcher;
+
+import static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;
+import static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;
+
+/**
+ * Supports the parameter <code>skipAfterFailureCount</code>.
+ * <br>
+ * Disables the execution if the number of failures has exceeded
+ * the threshold defined in <code>skipAfterFailureCount</code>.
+ *
+ * @since 3.0.0-M5
+ */
+public class TestExecutionWatcherSPI
+    implements ExecutionCondition, TestWatcher
+{
+    private static final SkipTestsAfterFailureSingleton FAILURES_COUNTER =
+        SkipTestsAfterFailureSingleton.getSingleton();
+
+    @Override
+    public ConditionEvaluationResult evaluateExecutionCondition( ExtensionContext context )
+    {
+        return FAILURES_COUNTER.isDisableTests() ? disabled( "" ) : enabled( "" );
+    }
+
+    @Override
+    public void testDisabled( ExtensionContext extensionContext, Optional<String> optional )
+    {
+
+    }
+
+    @Override
+    public void testSuccessful( ExtensionContext extensionContext )
+    {
+
+    }
+
+    @Override
+    public void testAborted( ExtensionContext extensionContext, Throwable throwable )
+    {
+
+    }
+
+    @Override
+    public void testFailed( ExtensionContext extensionContext, Throwable throwable )
+    {
+        FAILURES_COUNTER.runIfFailureCountReachedThreshold();
+    }
+}
diff --git a/surefire-providers/common-junit5/src/test/java/org/apache/maven/surefire/common/junit5/SkipTestsAfterFailureSingletonTest.java b/surefire-providers/common-junit5/src/test/java/org/apache/maven/surefire/common/junit5/SkipTestsAfterFailureSingletonTest.java
new file mode 100644
index 0000000..a38f8a4
--- /dev/null
+++ b/surefire-providers/common-junit5/src/test/java/org/apache/maven/surefire/common/junit5/SkipTestsAfterFailureSingletonTest.java
@@ -0,0 +1,89 @@
+package org.apache.maven.surefire.common.junit5;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.powermock.reflect.Whitebox.getInternalState;
+
+/**
+ *
+ */
+public class SkipTestsAfterFailureSingletonTest
+{
+    @Test
+    public void shouldBeActive()
+    {
+        boolean[] onThresholdReached = {false};
+        SkipTestsAfterFailureSingleton singleton = SkipTestsAfterFailureSingleton.getSingleton();
+        singleton.setDisableTests( false );
+        singleton.init( 2, () -> onThresholdReached[0] = true );
+
+        TestExecutionWatcherSPI spi = new TestExecutionWatcherSPI();
+
+        spi.testFailed( null, null );
+        assertFalse( singleton.isDisableTests() );
+        assertFalse( spi.evaluateExecutionCondition( null ).isDisabled() );
+        assertTrue( onThresholdReached[0] );
+
+        spi.testFailed( null, null );
+        assertTrue( singleton.isDisableTests() );
+        assertTrue( spi.evaluateExecutionCondition( null ).isDisabled() );
+        assertTrue( onThresholdReached[0] );
+    }
+
+    @Test
+    public void shouldNotBeActive()
+    {
+        boolean[] onThresholdReached = {false};
+        SkipTestsAfterFailureSingleton singleton = SkipTestsAfterFailureSingleton.getSingleton();
+        singleton.setDisableTests( false );
+        singleton.init( 0, () -> onThresholdReached[0] = true );
+
+        TestExecutionWatcherSPI spi = new TestExecutionWatcherSPI();
+
+        for ( int i = 0; i < 10; i++ )
+        {
+            spi.testFailed( null, null );
+            assertFalse( singleton.isDisableTests() );
+            assertFalse( spi.evaluateExecutionCondition( null ).isDisabled() );
+            assertFalse( onThresholdReached[0] );
+        }
+    }
+
+    @Test
+    public void shouldNotBeActiveIfRerun()
+    {
+        boolean[] onThresholdReached = {false};
+        SkipTestsAfterFailureSingleton singleton = SkipTestsAfterFailureSingleton.getSingleton();
+        singleton.setDisableTests( false );
+        singleton.init( 1, () -> onThresholdReached[0] = true );
+        assertTrue( getInternalState( singleton, "active" ) );
+        singleton.setReRunMode();
+        assertFalse( getInternalState( singleton, "active" ) );
+        assertFalse( singleton.isDisableTests() );
+        assertFalse( onThresholdReached[0] );
+        singleton.runIfFailureCountReachedThreshold();
+        assertFalse( singleton.isDisableTests() );
+        assertFalse( onThresholdReached[0] );
+    }
+}
diff --git a/surefire-providers/common-junit5/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.ExecutionCondition b/surefire-providers/common-junit5/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.ExecutionCondition
new file mode 100644
index 0000000..3beefd1
--- /dev/null
+++ b/surefire-providers/common-junit5/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.ExecutionCondition
@@ -0,0 +1 @@
+org.apache.maven.surefire.common.junit5.TestExecutionWatcherSPI
\ No newline at end of file
diff --git a/surefire-providers/common-junit5/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.TestWatcher b/surefire-providers/common-junit5/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.TestWatcher
new file mode 100644
index 0000000..3beefd1
--- /dev/null
+++ b/surefire-providers/common-junit5/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.TestWatcher
@@ -0,0 +1 @@
+org.apache.maven.surefire.common.junit5.TestExecutionWatcherSPI
\ No newline at end of file
diff --git a/surefire-providers/pom.xml b/surefire-providers/pom.xml
index 6c072d3..cf15c9f 100644
--- a/surefire-providers/pom.xml
+++ b/surefire-providers/pom.xml
@@ -38,6 +38,7 @@
     <module>common-java5</module>
     <module>common-junit4</module>
     <module>common-junit48</module>
+    <module>common-junit5</module>
     <module>surefire-junit3</module>
     <module>surefire-junit4</module>
     <module>surefire-junit47</module>
diff --git a/surefire-providers/surefire-junit-platform/pom.xml b/surefire-providers/surefire-junit-platform/pom.xml
index 264e9a3..8b8443c 100644
--- a/surefire-providers/surefire-junit-platform/pom.xml
+++ b/surefire-providers/surefire-junit-platform/pom.xml
@@ -82,6 +82,11 @@
             <artifactId>common-java5</artifactId>
             <version>${project.version}</version>
         </dependency>
+      <dependency>
+        <groupId>org.apache.maven.surefire</groupId>
+        <artifactId>common-junit5</artifactId>
+        <version>${project.version}</version>
+      </dependency>
         <dependency>
             <groupId>org.junit.platform</groupId>
             <artifactId>junit-platform-launcher</artifactId>
diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
index ad2ec94..6ba7f37 100644
--- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
@@ -52,14 +52,17 @@
 import java.util.logging.Logger;
 
 import org.apache.maven.surefire.api.provider.AbstractProvider;
+import org.apache.maven.surefire.api.provider.CommandChainReader;
 import org.apache.maven.surefire.api.provider.ProviderParameters;
 import org.apache.maven.surefire.api.report.ReporterException;
 import org.apache.maven.surefire.api.report.ReporterFactory;
+import org.apache.maven.surefire.api.report.TestReportListener;
 import org.apache.maven.surefire.api.suite.RunResult;
 import org.apache.maven.surefire.api.testset.TestSetFailedException;
 import org.apache.maven.surefire.api.util.ScanResult;
 import org.apache.maven.surefire.api.util.SurefireReflectionException;
 import org.apache.maven.surefire.api.util.TestsToRun;
+import org.apache.maven.surefire.common.junit5.SkipTestsAfterFailureSingleton;
 import org.apache.maven.surefire.shared.utils.StringUtils;
 import org.junit.platform.engine.DiscoverySelector;
 import org.junit.platform.engine.Filter;
@@ -88,6 +91,8 @@
 
     private final Map<String, String> configurationParameters;
 
+    private final CommandChainReader commandsReader;
+
     public JUnitPlatformProvider( ProviderParameters parameters )
     {
         this( parameters, new LazyLauncher() );
@@ -99,6 +104,8 @@
         this.launcher = launcher;
         filters = newFilters();
         configurationParameters = newConfigurationParameters();
+        // don't start a thread in CommandReader while we are in in-plugin process
+        commandsReader = parameters.isInsideFork() ? parameters.getCommandReader() : null;
     }
 
     @Override
@@ -122,10 +129,23 @@
         final RunResult runResult;
         try
         {
-            RunListenerAdapter adapter = new RunListenerAdapter( reporterFactory.createTestReportListener() );
+            TestReportListener listener = reporterFactory.createTestReportListener();
+
+            SkipTestsAfterFailureSingleton.getSingleton()
+                .init( getSkipAfterFailureCount(), listener::testExecutionSkippedByUser );
+
+            RunListenerAdapter adapter = new RunListenerAdapter( listener );
+
             adapter.setRunMode( NORMAL_RUN );
             startCapture( adapter );
+
             setupJunitLogger();
+
+            if ( isFailFast() && commandsReader != null )
+            {
+                registerPleaseStopJUnit();
+            }
+
             if ( forkTestSet instanceof TestsToRun )
             {
                 invokeAllTests( (TestsToRun) forkTestSet, adapter );
@@ -151,6 +171,27 @@
         return runResult;
     }
 
+    private boolean isFailFast()
+    {
+        return parameters.getSkipAfterFailureCount() > 0;
+    }
+
+    private int getSkipAfterFailureCount()
+    {
+        return isFailFast() ? parameters.getSkipAfterFailureCount() : 0;
+    }
+
+    private void registerShutdownListener( final TestsToRun testsToRun )
+    {
+        commandsReader.addShutdownListener( command -> testsToRun.markTestSetFinished() );
+    }
+
+    private void registerPleaseStopJUnit()
+    {
+        commandsReader.addSkipNextTestsListener( command ->
+            SkipTestsAfterFailureSingleton.getSingleton().setDisableTests() );
+    }
+
     private static void setupJunitLogger()
     {
         Logger logger = Logger.getLogger( "org.junit" );
@@ -169,7 +210,14 @@
     }
 
     private void invokeAllTests( TestsToRun testsToRun, RunListenerAdapter adapter )
+        throws TestSetFailedException
     {
+        if ( commandsReader != null )
+        {
+            registerShutdownListener( testsToRun );
+            commandsReader.awaitStarted();
+        }
+
         try
         {
             execute( testsToRun, adapter );
@@ -179,6 +227,7 @@
             closeLauncher();
         }
         // Rerun failing tests if requested
+        SkipTestsAfterFailureSingleton.getSingleton().setReRunMode();
         int count = parameters.getTestRequest().getRerunFailingTestsCount();
         if ( count > 0 && adapter.hasFailingTests() )
         {