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 40ed1996cc..26208a6b92 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 @@ else if ( skipAfterFailureCount > 0 ) { 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 258f88b693..5714a28e82 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 @@ Skipping Tests After the Nth Failure or Error 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 <<>> is set, the plugin fails with error: diff --git a/pom.xml b/pom.xml index ab43bbdef5..46ab25fc6e 100644 --- a/pom.xml +++ b/pom.xml @@ -296,12 +296,6 @@ org.powermock powermock-reflect ${powermockVersion} - - - org.objenesis - objenesis - - org.javassist 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 0000000000..5759edb20f --- /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 data() + { + /* + * reuseForks=false is not used because of race conditions between forks. + */ + ArrayList 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 0000000000..3390fe83ab --- /dev/null +++ b/surefire-its/src/test/resources/fail-fast-junit5/pom.xml @@ -0,0 +1,56 @@ + + + + + 4.0.0 + + org.apache.maven.plugins.surefire + fail-fast-junit5 + 1.0 + + + ${java.specification.version} + ${java.specification.version} + UTF-8 + + + + + org.junit.jupiter + junit-jupiter-engine + 5.8.2 + test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.version} + + alphabetical + + + + + 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 0000000000..1db9bb2378 --- /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 0000000000..aee436ffd3 --- /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 0000000000..f8fceb23ef --- /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 0000000000..0e99270132 --- /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 0000000000..50fb60ebe8 --- /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 0000000000..b8cbac629e --- /dev/null +++ b/surefire-providers/common-junit5/pom.xml @@ -0,0 +1,55 @@ + + + + + 4.0.0 + + + org.apache.maven.surefire + surefire-providers + 3.0.0-M6-SNAPSHOT + + + common-junit5 + Shared JUnit5 Provider Code + Shared JUnit5 Provider Code + + + + org.junit.jupiter + junit-jupiter-api + 5.4.0 + provided + + + com.google.code.findbugs + jsr305 + provided + + + org.powermock + powermock-reflect + test + + + + 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 0000000000..d4c6133529 --- /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 0000000000..07f54b7f8f --- /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 skipAfterFailureCount. + *
+ * Disables the execution if the number of failures has exceeded + * the threshold defined in skipAfterFailureCount. + * + * @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 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 0000000000..a38f8a4e7c --- /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 0000000000..3beefd11ed --- /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 0000000000..3beefd11ed --- /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 6c072d38ab..cf15c9fb56 100644 --- a/surefire-providers/pom.xml +++ b/surefire-providers/pom.xml @@ -38,6 +38,7 @@ common-java5 common-junit4 common-junit48 + common-junit5 surefire-junit3 surefire-junit4 surefire-junit47 diff --git a/surefire-providers/surefire-junit-platform/pom.xml b/surefire-providers/surefire-junit-platform/pom.xml index 264e9a3417..8b8443c3c1 100644 --- a/surefire-providers/surefire-junit-platform/pom.xml +++ b/surefire-providers/surefire-junit-platform/pom.xml @@ -82,6 +82,11 @@ common-java5 ${project.version}
+ + org.apache.maven.surefire + common-junit5 + ${project.version} + org.junit.platform junit-platform-launcher 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 ad2ec944d2..6ba7f37b03 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 @@ public class JUnitPlatformProvider private final Map configurationParameters; + private final CommandChainReader commandsReader; + public JUnitPlatformProvider( ProviderParameters parameters ) { this( parameters, new LazyLauncher() ); @@ -99,6 +104,8 @@ public JUnitPlatformProvider( ProviderParameters parameters ) 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 @@ public RunResult invoke( Object forkTestSet ) 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 @@ else if ( forkTestSet == null ) 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 TestsToRun scanClasspath() } private void invokeAllTests( TestsToRun testsToRun, RunListenerAdapter adapter ) + throws TestSetFailedException { + if ( commandsReader != null ) + { + registerShutdownListener( testsToRun ); + commandsReader.awaitStarted(); + } + try { execute( testsToRun, adapter ); @@ -179,6 +227,7 @@ private void invokeAllTests( TestsToRun testsToRun, RunListenerAdapter adapter ) closeLauncher(); } // Rerun failing tests if requested + SkipTestsAfterFailureSingleton.getSingleton().setReRunMode(); int count = parameters.getTestRequest().getRerunFailingTestsCount(); if ( count > 0 && adapter.hasFailingTests() ) {