This is an automated email from the ASF dual-hosted git repository. tibordigana pushed a commit to branch it-946 in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
commit 7eaac290e3be1f0604db28d76acb503d01838756 Author: tibor.digana <[email protected]> AuthorDate: Tue Feb 22 23:49:06 2022 +0100 JIRA 946 --- .../plugin/surefire/AbstractSurefireMojo.java | 48 +++-- .../plugin/surefire/booterclient/ForkStarter.java | 201 +++++++++------------ .../plugin/surefire/booterclient/Platform.java | 17 ++ .../surefire/booterclient/output/ForkClient.java | 45 ++--- .../booterclient/output/ForkClientTest.java | 11 +- .../api/util/internal/ConcurrencyUtils.java | 13 +- .../api/util/internal/ConcurrencyUtilsTest.java | 41 +++-- ...Surefire946KillMainProcessInReusableForkIT.java | 2 +- .../maven/surefire/common/junit4/Notifier.java | 8 +- .../maven/surefire/testng/TestNGExecutor.java | 18 +- 10 files changed, 195 insertions(+), 209 deletions(-) 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 a2d76c8..30912de 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 @@ -154,6 +154,8 @@ import static org.apache.maven.surefire.shared.utils.StringUtils.isEmpty; import static org.apache.maven.surefire.shared.utils.StringUtils.isNotBlank; import static org.apache.maven.surefire.shared.utils.StringUtils.isNotEmpty; import static org.apache.maven.surefire.shared.utils.StringUtils.split; +import static org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils.addShutDownHook; +import static org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils.removeShutdownHook; /** * Abstract base class for running tests using Surefire. @@ -922,29 +924,39 @@ public abstract class AbstractSurefireMojo // Stuff that should have been final setupStuff(); Platform platform = PLATFORM.withJdkExecAttributesForTests( getEffectiveJvm() ); - - if ( verifyParameters() && !hasExecutedBefore() ) + Thread shutdownThread = new Thread( platform::setShutdownState ); + addShutDownHook( shutdownThread ); + try { - DefaultScanResult scan = scanForTestClasses(); - if ( !hasSuiteXmlFiles() && scan.isEmpty() ) + if ( verifyParameters() && !hasExecutedBefore() ) { - switch ( getEffectiveFailIfNoTests() ) + DefaultScanResult scan = scanForTestClasses(); + if ( !hasSuiteXmlFiles() && scan.isEmpty() ) { - case COULD_NOT_RUN_DEFAULT_TESTS: - throw new MojoFailureException( - "No tests were executed! (Set -DfailIfNoTests=false to ignore this error.)" ); - case COULD_NOT_RUN_SPECIFIED_TESTS: - throw new MojoFailureException( "No tests matching pattern \"" - + getSpecificTests().toString() - + "\" were executed! (Set " - + "-D" + getPluginName() + ".failIfNoSpecifiedTests=false to ignore this error.)" ); - default: - handleSummary( noTestsRun(), null ); - return; + switch ( getEffectiveFailIfNoTests() ) + { + case COULD_NOT_RUN_DEFAULT_TESTS: + throw new MojoFailureException( + "No tests were executed! (Set -DfailIfNoTests=false to ignore this error.)" ); + case COULD_NOT_RUN_SPECIFIED_TESTS: + throw new MojoFailureException( "No tests matching pattern \"" + + getSpecificTests().toString() + + "\" were executed! (Set " + + "-D" + getPluginName() + + ".failIfNoSpecifiedTests=false to ignore this error.)" ); + default: + handleSummary( noTestsRun(), null ); + return; + } } + logReportsDirectory(); + executeAfterPreconditionsChecked( scan, platform ); } - logReportsDirectory(); - executeAfterPreconditionsChecked( scan, platform ); + } + finally + { + platform.clearShutdownState(); + removeShutdownHook( shutdownThread ); } } diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java index 2a2c820..17b0e54 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java @@ -61,7 +61,6 @@ import javax.annotation.Nonnull; import java.io.Closeable; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.Queue; @@ -88,6 +87,8 @@ import static java.util.UUID.randomUUID; import static java.util.concurrent.Executors.newScheduledThreadPool; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static java.util.stream.Collectors.toList; +import static java.util.stream.StreamSupport.stream; import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.createCopyAndReplaceForkNumPlaceholder; import static org.apache.maven.plugin.surefire.SurefireHelper.DUMP_FILE_PREFIX; import static org.apache.maven.plugin.surefire.SurefireHelper.replaceForkThreadsInPath; @@ -101,7 +102,7 @@ import static org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils.remov import static org.apache.maven.surefire.api.suite.RunResult.SUCCESS; import static org.apache.maven.surefire.api.suite.RunResult.failure; import static org.apache.maven.surefire.api.suite.RunResult.timeout; -import static org.apache.maven.surefire.api.util.internal.ConcurrencyUtils.countDownToZero; +import static org.apache.maven.surefire.api.util.internal.ConcurrencyUtils.runIfZeroCountDown; import static org.apache.maven.surefire.api.util.internal.DaemonThreadFactory.newDaemonThread; import static org.apache.maven.surefire.api.util.internal.DaemonThreadFactory.newDaemonThreadFactory; import static org.apache.maven.surefire.api.util.internal.StringUtils.NL; @@ -288,15 +289,15 @@ public class ForkStarter } } - private RunResult run( SurefireProperties effectiveSystemProperties, Map<String, String> providerProperties ) + private RunResult run( SurefireProperties effectiveSystemProps, Map<String, String> providerProperties ) throws SurefireBooterForkException { TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder(); - PropertiesWrapper props = new PropertiesWrapper( providerProperties ); TestLessInputStream stream = builder.build(); Thread shutdown = createImmediateShutdownHookThread( builder, providerConfiguration.getShutdown() ); ScheduledFuture<?> ping = triggerPingTimerForShutdown( builder ); int forkNumber = drawNumber(); + PropertiesWrapper props = new PropertiesWrapper( providerProperties ); try { addShutDownHook( shutdown ); @@ -304,8 +305,10 @@ public class ForkStarter new DefaultReporterFactory( startupReportConfiguration, log, forkNumber ); defaultReporterFactories.add( forkedReporterFactory ); ForkClient forkClient = new ForkClient( forkedReporterFactory, stream, forkNumber ); - return fork( null, props, forkClient, effectiveSystemProperties, forkNumber, stream, - forkConfiguration.getForkNodeFactory(), false ); + ForkNodeFactory node = forkConfiguration.getForkNodeFactory(); + return forkConfiguration.getPluginPlatform().isShutdown() + ? new RunResult( 0, 0, 0, 0 ) + : fork( null, props, forkClient, effectiveSystemProps, forkNumber, stream, node, false ); } finally { @@ -336,81 +339,68 @@ public class ForkStarter } @SuppressWarnings( "checkstyle:magicnumber" ) - private RunResult runSuitesForkOnceMultiple( final SurefireProperties effectiveSystemProperties, int forkCount ) + private RunResult runSuitesForkOnceMultiple( SurefireProperties effectiveSystemProps, int forkCount ) throws SurefireBooterForkException { - ThreadPoolExecutor executorService = new ThreadPoolExecutor( forkCount, forkCount, 60, SECONDS, - new ArrayBlockingQueue<Runnable>( forkCount ) ); - executorService.setThreadFactory( FORKED_JVM_DAEMON_THREAD_FACTORY ); + ThreadPoolExecutor executor + = new ThreadPoolExecutor( forkCount, forkCount, 60L, SECONDS, new ArrayBlockingQueue<>( forkCount ) ); + executor.setThreadFactory( FORKED_JVM_DAEMON_THREAD_FACTORY ); - final Queue<String> tests = new ConcurrentLinkedQueue<>(); + Queue<String> tests = new ConcurrentLinkedQueue<>(); for ( Class<?> clazz : getSuitesIterator() ) { tests.add( clazz.getName() ); } - final Queue<TestProvidingInputStream> testStreams = new ConcurrentLinkedQueue<>(); + Queue<TestProvidingInputStream> forks = new ConcurrentLinkedQueue<>(); for ( int forkNum = 0, total = min( forkCount, tests.size() ); forkNum < total; forkNum++ ) { - testStreams.add( new TestProvidingInputStream( tests ) ); + forks.add( new TestProvidingInputStream( tests ) ); } - ScheduledFuture<?> ping = triggerPingTimerForShutdown( testStreams ); - Thread shutdown = createShutdownHookThread( testStreams, providerConfiguration.getShutdown() ); + Thread shutdown = createShutdownHookThread( forks, providerConfiguration.getShutdown() ); + addShutDownHook( shutdown ); + ScheduledFuture<?> ping = triggerPingTimerForShutdown( forks ); try { - addShutDownHook( shutdown ); int failFastCount = providerConfiguration.getSkipAfterFailureCount(); - final AtomicInteger notifyStreamsToSkipTestsJustNow = new AtomicInteger( failFastCount ); - final Collection<Future<RunResult>> results = new ArrayList<>( forkCount ); - for ( final TestProvidingInputStream testProvidingInputStream : testStreams ) - { - Callable<RunResult> pf = new Callable<RunResult>() + AtomicInteger notifyForksToSkipTestsNow = new AtomicInteger( failFastCount ); + Collection<Future<RunResult>> results = + forks.stream() + .filter( fork -> !forkConfiguration.getPluginPlatform().isShutdown() ) + .map( fork -> (Callable<RunResult>) () -> { - @Override - public RunResult call() - throws Exception + int forkNumber = drawNumber(); + DefaultReporterFactory reporter = + new DefaultReporterFactory( startupReportConfiguration, log, forkNumber ); + defaultReporterFactories.add( reporter ); + ForkClient client = new ForkClient( reporter, fork, forkNumber ); + client.setStopOnNextTestListener( () -> + runIfZeroCountDown( () -> notifyStreamsToSkipTests( forks ), notifyForksToSkipTestsNow ) ); + Map<String, String> providerProperties = providerConfiguration.getProviderProperties(); + PropertiesWrapper keyValues = new PropertiesWrapper( providerProperties ); + ForkNodeFactory node = forkConfiguration.getForkNodeFactory(); + try { - int forkNumber = drawNumber(); - DefaultReporterFactory reporter = - new DefaultReporterFactory( startupReportConfiguration, log, forkNumber ); - defaultReporterFactories.add( reporter ); - ForkClient forkClient = new ForkClient( reporter, testProvidingInputStream, forkNumber ) - { - @Override - protected void stopOnNextTest() - { - if ( countDownToZero( notifyStreamsToSkipTestsJustNow ) ) - { - notifyStreamsToSkipTests( testStreams ); - } - } - }; - Map<String, String> providerProperties = providerConfiguration.getProviderProperties(); - try - { - return fork( null, new PropertiesWrapper( providerProperties ), forkClient, - effectiveSystemProperties, forkNumber, testProvidingInputStream, - forkConfiguration.getForkNodeFactory(), true ); - } - finally - { - returnNumber( forkNumber ); - } + return fork( null, keyValues, client, effectiveSystemProps, forkNumber, fork, node, true ); } - }; - results.add( executorService.submit( pf ) ); - } - return awaitResultsDone( results, executorService ); + finally + { + returnNumber( forkNumber ); + } + } ).map( executor::submit ) + .collect( toList() ); + + return awaitResultsDone( results, executor ); } finally { removeShutdownHook( shutdown ); ping.cancel( true ); - closeExecutor( executorService ); + closeExecutor( executor ); } } @@ -423,68 +413,57 @@ public class ForkStarter } @SuppressWarnings( "checkstyle:magicnumber" ) - private RunResult runSuitesForkPerTestSet( final SurefireProperties effectiveSystemProperties, int forkCount ) + private RunResult runSuitesForkPerTestSet( SurefireProperties effectiveSystemProps, int forkCount ) throws SurefireBooterForkException { - ArrayList<Future<RunResult>> results = new ArrayList<>( 500 ); - ThreadPoolExecutor executorService = - new ThreadPoolExecutor( forkCount, forkCount, 60, SECONDS, new LinkedBlockingQueue<Runnable>() ); - executorService.setThreadFactory( FORKED_JVM_DAEMON_THREAD_FACTORY ); - final TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder(); - ScheduledFuture<?> ping = triggerPingTimerForShutdown( builder ); + TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder(); Thread shutdown = createCachableShutdownHookThread( builder, providerConfiguration.getShutdown() ); + ThreadPoolExecutor executor = + new ThreadPoolExecutor( forkCount, forkCount, 60, SECONDS, new LinkedBlockingQueue<>() ); + executor.setThreadFactory( FORKED_JVM_DAEMON_THREAD_FACTORY ); + ScheduledFuture<?> ping = triggerPingTimerForShutdown( builder ); try { addShutDownHook( shutdown ); int failFastCount = providerConfiguration.getSkipAfterFailureCount(); - final AtomicInteger notifyStreamsToSkipTestsJustNow = new AtomicInteger( failFastCount ); - for ( final Object testSet : getSuitesIterator() ) + AtomicInteger notifyForksToSkipTestsNow = new AtomicInteger( failFastCount ); + Collection<Future<RunResult>> results = + stream( ( (Iterable<?>) getSuitesIterator() ).spliterator(), false ) + .filter( fork -> !forkConfiguration.getPluginPlatform().isShutdown() ) + .map( testSet -> (Callable<RunResult>) () -> { - Callable<RunResult> pf = new Callable<RunResult>() + int forkNumber = drawNumber(); + DefaultReporterFactory forkedReporterFactory = + new DefaultReporterFactory( startupReportConfiguration, log, forkNumber ); + defaultReporterFactories.add( forkedReporterFactory ); + TestLessInputStream stream = builder.build(); + ForkClient forkClient = new ForkClient( forkedReporterFactory, stream, forkNumber ); + NotifiableTestStream notifiable = builder.getCachableCommands(); + forkClient.setStopOnNextTestListener( () -> + runIfZeroCountDown( notifiable::skipSinceNextTest, notifyForksToSkipTestsNow ) ); + Map<String, String> providerProperties = providerConfiguration.getProviderProperties(); + PropertiesWrapper keyValues = new PropertiesWrapper( providerProperties ); + ForkNodeFactory node = forkConfiguration.getForkNodeFactory(); + try { - @Override - public RunResult call() - throws Exception - { - int forkNumber = drawNumber(); - DefaultReporterFactory forkedReporterFactory = - new DefaultReporterFactory( startupReportConfiguration, log, forkNumber ); - defaultReporterFactories.add( forkedReporterFactory ); - TestLessInputStream stream = builder.build(); - ForkClient forkClient = new ForkClient( forkedReporterFactory, stream, forkNumber ) - { - @Override - protected void stopOnNextTest() - { - if ( countDownToZero( notifyStreamsToSkipTestsJustNow ) ) - { - builder.getCachableCommands().skipSinceNextTest(); - } - } - }; - try - { - return fork( testSet, - new PropertiesWrapper( providerConfiguration.getProviderProperties() ), - forkClient, effectiveSystemProperties, forkNumber, stream, - forkConfiguration.getForkNodeFactory(), false ); - } - finally - { - returnNumber( forkNumber ); - builder.removeStream( stream ); - } - } - }; - results.add( executorService.submit( pf ) ); - } - return awaitResultsDone( results, executorService ); + return fork( testSet, keyValues, forkClient, effectiveSystemProps, forkNumber, stream, node, + false ); + } + finally + { + returnNumber( forkNumber ); + builder.removeStream( stream ); + } + } ).map( executor::submit ) + .collect( toList() ); + + return awaitResultsDone( results, executor ); } finally { removeShutdownHook( shutdown ); ping.cancel( true ); - closeExecutor( executorService ); + closeExecutor( executor ); } } @@ -662,7 +641,10 @@ public class ForkStarter log.error( "Closing the streams after (InterruptedException) '" + e.getLocalizedMessage() + "'" ); // maybe implement it in the Future.cancel() of the extension or similar forkChannel.disable(); - err.disable(); + if ( err != null ) + { + err.disable(); + } } catch ( Exception e ) { @@ -678,16 +660,7 @@ public class ForkStarter log.debug( "Closing the fork " + forkNumber + " after " + ( forkClient.isSaidGoodBye() ? "saying GoodBye." : "not saying Good Bye." ) ); currentForkClients.remove( forkClient ); - try - { - Closeable c = forkClient.isSaidGoodBye() ? closer : commandReader; - c.close(); - } - catch ( IOException e ) - { - InPluginProcessDumpSingleton.getSingleton() - .dumpException( e, e.getLocalizedMessage(), dumpLogDir, forkNumber ); - } + closer.close(); if ( runResult == null ) { diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java index 97a3121..852b274 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java @@ -40,6 +40,8 @@ public final class Platform private final JdkAttributes jdk; + private volatile boolean shutdown; + public Platform() { // the job may take 50 or 80 ms @@ -53,6 +55,21 @@ public final class Platform this.jdk = jdk; } + public boolean isShutdown() + { + return shutdown; + } + + public void setShutdownState() + { + this.shutdown = true; + } + + public void clearShutdownState() + { + this.shutdown = false; + } + public Long getPluginPid() { try diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java index c8eb672..37e50f5 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java @@ -56,7 +56,7 @@ import static org.apache.maven.surefire.api.report.CategorizedReportEntry.report * * @author Kristian Rosenvold */ -public class ForkClient +public final class ForkClient implements EventHandler<Event> { private static final long START_TIME_ZERO = 0L; @@ -89,7 +89,8 @@ public class ForkClient private volatile StackTraceWriter errorInFork; - public ForkClient( DefaultReporterFactory defaultReporterFactory, NotifiableTestStream notifiableTestStream, + public ForkClient( DefaultReporterFactory defaultReporterFactory, + NotifiableTestStream notifiableTestStream, int forkNumber ) { this.defaultReporterFactory = defaultReporterFactory; @@ -110,12 +111,16 @@ public class ForkClient notifier.setAcquireNextTestListener( new AcquireNextTestListener() ); notifier.setConsoleErrorListener( new ErrorListener() ); notifier.setByeListener( new ByeListener() ); - notifier.setStopOnNextTestListener( new StopOnNextTestListener() ); notifier.setConsoleDebugListener( new DebugListener() ); notifier.setConsoleWarningListener( new WarningListener() ); notifier.setExitErrorEventListener( new ExitErrorEventListener() ); } + public void setStopOnNextTestListener( ForkedProcessEventListener listener ) + { + notifier.setStopOnNextTestListener( listener ); + } + private final class TestSetStartingListener implements ForkedProcessReportEventListener<TestSetReportEntry> { @@ -277,15 +282,6 @@ public class ForkClient } } - private final class StopOnNextTestListener implements ForkedProcessEventListener - { - @Override - public void handle() - { - stopOnNextTest(); - } - } - private final class DebugListener implements ForkedProcessStringEventListener { @Override @@ -316,13 +312,6 @@ public class ForkClient } } - /** - * Overridden by a subclass, see {@link org.apache.maven.plugin.surefire.booterclient.ForkStarter}. - */ - protected void stopOnNextTest() - { - } - public void kill() { if ( !saidGoodBye ) @@ -338,7 +327,7 @@ public class ForkClient * @param currentTimeMillis current time in millis seconds * @param forkedProcessTimeoutInSeconds timeout in seconds given by MOJO */ - public final void tryToTimeout( long currentTimeMillis, int forkedProcessTimeoutInSeconds ) + public void tryToTimeout( long currentTimeMillis, int forkedProcessTimeoutInSeconds ) { if ( forkedProcessTimeoutInSeconds > 0 ) { @@ -352,13 +341,13 @@ public class ForkClient } } - public final DefaultReporterFactory getDefaultReporterFactory() + public DefaultReporterFactory getDefaultReporterFactory() { return defaultReporterFactory; } @Override - public final void handleEvent( @Nonnull Event event ) + public void handleEvent( @Nonnull Event event ) { notifier.notifyEvent( event ); } @@ -373,7 +362,7 @@ public class ForkClient } } - public final boolean hadTimeout() + public boolean hadTimeout() { return testSetStartedAt.get() == START_TIME_NEGATIVE_TIMEOUT; } @@ -409,7 +398,7 @@ public class ForkClient .writeTestOutput( new TestOutputReportEntry( output, isStdout, newLine, /*todo*/ null, null ) ); } - public final Map<String, String> getTestVmSystemProperties() + public Map<String, String> getTestVmSystemProperties() { return unmodifiableMap( testVmSystemProperties ); } @@ -420,7 +409,7 @@ public class ForkClient * * @return A mock provider reporter */ - public final RunListener getReporter() + public RunListener getReporter() { return getTestSetReporter(); } @@ -440,17 +429,17 @@ public class ForkClient // no op } - public final boolean isSaidGoodBye() + public boolean isSaidGoodBye() { return saidGoodBye; } - public final StackTraceWriter getErrorInFork() + public StackTraceWriter getErrorInFork() { return errorInFork; } - public final boolean isErrorInFork() + public boolean isErrorInFork() { return errorInFork != null; } diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java index 7822300..c440114 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java @@ -438,15 +438,8 @@ public class ForkClientTest .thenReturn( new File( target, "surefire-reports" ) ); NotifiableTestStream notifiableTestStream = mock( NotifiableTestStream.class ); final boolean[] verified = {false}; - ForkClient client = new ForkClient( factory, notifiableTestStream, 0 ) - { - @Override - protected void stopOnNextTest() - { - super.stopOnNextTest(); - verified[0] = true; - } - }; + ForkClient client = new ForkClient( factory, notifiableTestStream, 0 ); + client.setStopOnNextTestListener( () -> verified[0] = true ); client.handleEvent( new ControlStopOnNextTestEvent() ); verifyZeroInteractions( notifiableTestStream ); verifyZeroInteractions( factory ); diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/ConcurrencyUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/ConcurrencyUtils.java index a43d8f3..fd2c189 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/ConcurrencyUtils.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/ConcurrencyUtils.java @@ -38,11 +38,11 @@ public final class ConcurrencyUtils * Decreases {@code counter} to zero, or does not change the counter if negative. * This method pretends been atomic. Only one thread can succeed setting the counter to zero. * + * @param runner run if this Thread has concurrently decremented the counter down to zero * @param counter atomic counter - * @return {@code true} if this Thread modified concurrent counter from any positive number down to zero. */ @SuppressWarnings( "checkstyle:emptyforiteratorpad" ) - public static boolean countDownToZero( AtomicInteger counter ) + public static void runIfZeroCountDown( Runnable runner, AtomicInteger counter ) { for (;;) { @@ -52,12 +52,17 @@ public final class ConcurrencyUtils int newCounter = c - 1; if ( counter.compareAndSet( c, newCounter ) ) { - return newCounter == 0; + boolean isZero = newCounter == 0; + if ( isZero ) + { + runner.run(); + } + break; } } else { - return false; + break; } } } diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ConcurrencyUtilsTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ConcurrencyUtilsTest.java index a2181c1..10c03e3 100644 --- a/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ConcurrencyUtilsTest.java +++ b/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ConcurrencyUtilsTest.java @@ -21,12 +21,12 @@ package org.apache.maven.surefire.api.util.internal; import org.junit.Test; -import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import static org.apache.maven.surefire.api.util.internal.ConcurrencyUtils.countDownToZero; +import static org.apache.maven.surefire.api.util.internal.ConcurrencyUtils.runIfZeroCountDown; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertTrue; @@ -45,7 +45,9 @@ public class ConcurrencyUtilsTest public void countDownShouldBeUnchangedAsZeroNegativeTest() { AtomicInteger atomicCounter = new AtomicInteger( 0 ); - assertFalse( countDownToZero( atomicCounter ) ); + AtomicBoolean runner = new AtomicBoolean(); + runIfZeroCountDown( () -> runner.set( true ), atomicCounter ); + assertFalse( runner.get() ); assertThat( atomicCounter.get(), is( 0 ) ); } @@ -53,7 +55,9 @@ public class ConcurrencyUtilsTest public void countDownShouldBeUnchangedAsNegativeNegativeTest() { AtomicInteger atomicCounter = new AtomicInteger( -1 ); - assertFalse( countDownToZero( atomicCounter ) ); + AtomicBoolean runner = new AtomicBoolean(); + runIfZeroCountDown( () -> runner.set( true ), atomicCounter ); + assertFalse( runner.get() ); assertThat( atomicCounter.get(), is( -1 ) ); } @@ -61,7 +65,9 @@ public class ConcurrencyUtilsTest public void countDownShouldBeDecreasedByOneThreadModification() { AtomicInteger atomicCounter = new AtomicInteger( 10 ); - assertFalse( countDownToZero( atomicCounter ) ); + AtomicBoolean runner = new AtomicBoolean(); + runIfZeroCountDown( () -> runner.set( true ), atomicCounter ); + assertFalse( runner.get() ); assertThat( atomicCounter.get(), is( 9 ) ); } @@ -69,7 +75,9 @@ public class ConcurrencyUtilsTest public void countDownToZeroShouldBeDecreasedByOneThreadModification() { AtomicInteger atomicCounter = new AtomicInteger( 1 ); - assertTrue( countDownToZero( atomicCounter ) ); + AtomicBoolean runner = new AtomicBoolean(); + runIfZeroCountDown( () -> runner.set( true ), atomicCounter ); + assertTrue( runner.get() ); assertThat( atomicCounter.get(), is( 0 ) ); } @@ -77,27 +85,28 @@ public class ConcurrencyUtilsTest public void countDownShouldBeDecreasedByTwoThreadsModification() throws ExecutionException, InterruptedException { - final AtomicInteger atomicCounter = new AtomicInteger( 3 ); + AtomicInteger atomicCounter = new AtomicInteger( 3 ); - FutureTask<Boolean> task = new FutureTask<Boolean>( new Callable<Boolean>() + FutureTask<Boolean> task = new FutureTask<>( () -> { - @Override - public Boolean call() - throws Exception - { - return countDownToZero( atomicCounter ); - } + AtomicBoolean runner = new AtomicBoolean(); + runIfZeroCountDown( () -> runner.set( true ), atomicCounter ); + return runner.get(); } ); Thread t = new Thread( task ); t.start(); - assertFalse( countDownToZero( atomicCounter ) ); + AtomicBoolean runner = new AtomicBoolean(); + runIfZeroCountDown( () -> runner.set( true ), atomicCounter ); + assertFalse( runner.get() ); assertFalse( task.get() ); assertThat( atomicCounter.get(), is( 1 ) ); - assertTrue( countDownToZero( atomicCounter ) ); + runner.set( false ); + runIfZeroCountDown( () -> runner.set( true ), atomicCounter ); + assertTrue( runner.get() ); assertThat( atomicCounter.get(), is( 0 ) ); } diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire946KillMainProcessInReusableForkIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire946KillMainProcessInReusableForkIT.java index 730786d..cdd3236 100644 --- a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire946KillMainProcessInReusableForkIT.java +++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire946KillMainProcessInReusableForkIT.java @@ -95,7 +95,7 @@ public class Surefire946KillMainProcessInReusableForkIT "-" + shutdownMavenMethod + "-" + shutdownSurefireMethod ) .sysProp( "distinct.classifier", classifierOfDummyDependency ) .sysProp( "surefire.shutdown", shutdownSurefireMethod ) - .sysProp( "selfdestruct.timeoutInMillis", "10000" ) + .sysProp( "selfdestruct.timeoutInMillis", "20000" ) .sysProp( "selfdestruct.method", shutdownMavenMethod ) .sysProp( "testSleepTime", String.valueOf( TEST_SLEEP_TIME ) ) .addGoal( "org.apache.maven.plugins.surefire:maven-selfdestruct-plugin:selfdestruct" ) diff --git a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java index ab70395..927df00 100644 --- a/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java +++ b/surefire-providers/common-junit4/src/main/java/org/apache/maven/surefire/common/junit4/Notifier.java @@ -33,7 +33,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.toClassMethod; -import static org.apache.maven.surefire.api.util.internal.ConcurrencyUtils.countDownToZero; +import static org.apache.maven.surefire.api.util.internal.ConcurrencyUtils.runIfZeroCountDown; /** * Extends {@link RunNotifier JUnit notifier}, @@ -172,11 +172,7 @@ public class Notifier */ private void fireStopEvent() { - if ( countDownToZero( skipAfterFailureCount ) ) - { - pleaseStop(); - } - + runIfZeroCountDown( this::pleaseStop, skipAfterFailureCount ); reporter.testExecutionSkippedByUser(); } } diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java index c0799f7..54fea3d 100644 --- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java +++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGExecutor.java @@ -57,7 +57,7 @@ import static org.apache.maven.surefire.api.util.ReflectionUtils.newInstance; import static org.apache.maven.surefire.api.util.ReflectionUtils.tryGetConstructor; import static org.apache.maven.surefire.api.util.ReflectionUtils.tryGetMethod; import static org.apache.maven.surefire.api.util.ReflectionUtils.tryLoadClass; -import static org.apache.maven.surefire.api.util.internal.ConcurrencyUtils.countDownToZero; +import static org.apache.maven.surefire.api.util.internal.ConcurrencyUtils.runIfZeroCountDown; /** * Contains utility methods for executing TestNG. @@ -137,7 +137,7 @@ final class TestNGExecutor xmlTest.setName( metadata.testName ); addSelector( xmlTest, groupMatchingSelector ); addSelector( xmlTest, methodNameFilteringSelector ); - xmlTest.setXmlClasses( new ArrayList<XmlClass>() ); + xmlTest.setXmlClasses( new ArrayList<>() ); suiteAndNamedTests.testNameToTest.put( metadata.testName, xmlTest ); } @@ -366,18 +366,10 @@ final class TestNGExecutor { final AtomicInteger currentFaultCount = new AtomicInteger( skipAfterFailureCount ); - return new Stoppable() + return () -> { - @Override - public void fireStopEvent() - { - if ( countDownToZero( currentFaultCount ) ) - { - FailFastEventsSingleton.getInstance().setSkipOnNextTest(); - } - - reportManager.testExecutionSkippedByUser(); - } + runIfZeroCountDown( () -> FailFastEventsSingleton.getInstance().setSkipOnNextTest(), currentFaultCount ); + reportManager.testExecutionSkippedByUser(); }; }
