From 06200e077db1ebb0ac99c937532c77af8e3778aa Mon Sep 17 00:00:00 2001 From: tibordigana Date: Wed, 29 Oct 2014 22:56:44 +0100 Subject: [PATCH] [SUREFIRE-1108] Surefire should print parallel tests in progress been stopped after elapsed timeout --- .../maven/surefire/its/JUnit47ParallelIT.java | 11 ++++ .../junitcore/AsynchronousRunner.java | 4 ++ .../ConfigurableParallelComputer.java | 4 ++ .../surefire/junitcore/SynchronousRunner.java | 4 ++ .../junitcore/pc/ParallelComputer.java | 54 ++++++++++++------ .../junitcore/pc/ParallelComputerBuilder.java | 10 ++-- .../surefire/junitcore/pc/Scheduler.java | 42 ++++++++++---- .../surefire/junitcore/pc/ShutdownResult.java | 57 +++++++++++++++++++ .../surefire/junitcore/pc/ShutdownStatus.java | 23 +++----- .../junitcore/pc/SingleThreadScheduler.java | 16 ++++-- .../pc/ParallelComputerBuilderTest.java | 2 +- 11 files changed, 174 insertions(+), 53 deletions(-) create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ShutdownResult.java diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/JUnit47ParallelIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/JUnit47ParallelIT.java index 921ba73adf..d2d00982fa 100644 --- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/JUnit47ParallelIT.java +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/JUnit47ParallelIT.java @@ -521,6 +521,17 @@ public void timeoutAndForcedShutdown() "The test run has finished abruptly after timeout of 1.0 seconds." ); } + @Test + public void forcedShutdownVerifyingLogs() + { + // executes for 2.5 sec until timeout has elapsed + unpack().parallelMethods().threadCountMethods( 3 ).disablePerCoreThreadCount() + .parallelTestsTimeoutForcedInSeconds( 1.05d ).setTestToRun( "Waiting*Test" ).failNever().executeTest() + .verifyTextInLog( "The test run has finished abruptly after timeout of 1.05 seconds." ) + .verifyTextInLog( "These tests were executed in prior to the shutdown operation:" ) + .verifyTextInLog( "These tests are incomplete:" ); + } + private SurefireLauncher unpack() { return unpack( "junit47-parallel" ); diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/AsynchronousRunner.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/AsynchronousRunner.java index dcadeff39b..8faa12e13d 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/AsynchronousRunner.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/AsynchronousRunner.java @@ -30,8 +30,12 @@ import org.junit.runners.model.RunnerScheduler; /** + * Since SUREFIRE 2.18 this class is deprecated. + * Please use {@link org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder} instead. + * * @author Kristian Rosenvold */ +@Deprecated public class AsynchronousRunner implements RunnerScheduler { diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConfigurableParallelComputer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConfigurableParallelComputer.java index a4a5414f47..d664edcc76 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConfigurableParallelComputer.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ConfigurableParallelComputer.java @@ -35,8 +35,12 @@ import org.junit.runners.model.RunnerScheduler; /** + * Since SUREFIRE 2.18 this class is deprecated. + * Please use {@link org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder} instead. + * * @author Kristian Rosenvold */ +@Deprecated public class ConfigurableParallelComputer extends Computer { diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/SynchronousRunner.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/SynchronousRunner.java index 665bcb72d5..efbca98853 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/SynchronousRunner.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/SynchronousRunner.java @@ -22,8 +22,12 @@ import org.junit.runners.model.RunnerScheduler; /** + * Since SUREFIRE 2.18 this class is deprecated. + * Please use {@link org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder} instead. + * * @author Kristian Rosenvold */ +@Deprecated class SynchronousRunner implements RunnerScheduler { diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java index 86c958f9a7..5491a68a44 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java @@ -61,7 +61,8 @@ public ParallelComputer( double timeoutInSeconds, double timeoutForcedInSeconds this.timeoutForcedNanos = secondsToNanos( timeoutForcedInSeconds ); } - protected abstract Collection describeStopped( boolean shutdownNow ); + protected abstract ShutdownResult describeStopped( boolean shutdownNow ); + abstract boolean shutdownThreadPoolsAwaitingKilled(); protected final void beforeRunQuietly() @@ -111,15 +112,17 @@ public String describeElapsedTimeout() try { final TreeSet executedTests = new TreeSet(); + final TreeSet incompleteTests = new TreeSet(); if ( isShutdownTimeout ) { - printShutdownHook( executedTests, shutdownStatus.getDescriptionsBeforeShutdown() ); + printShutdownHook( executedTests, incompleteTests, shutdownStatus.getDescriptionsBeforeShutdown() ); } if ( isForcedShutdownTimeout ) { - printShutdownHook( executedTests, forcedShutdownStatus.getDescriptionsBeforeShutdown() ); + printShutdownHook( executedTests, incompleteTests, + forcedShutdownStatus.getDescriptionsBeforeShutdown() ); } if ( !executedTests.isEmpty() ) @@ -130,6 +133,15 @@ public String describeElapsedTimeout() msg.append( executedTest ).append( '\n' ); } } + + if ( !incompleteTests.isEmpty() ) + { + msg.append( "These tests are incomplete:\n" ); + for ( String incompleteTest : incompleteTests ) + { + msg.append( incompleteTest ).append( '\n' ); + } + } } catch ( InterruptedException e ) { @@ -143,12 +155,12 @@ public String describeElapsedTimeout() return msg.toString(); } - private Future> scheduleShutdown() + private Future scheduleShutdown() { return getShutdownScheduler().schedule( createShutdownTask(), timeoutNanos, NANOSECONDS ); } - private Future> scheduleForcedShutdown() + private Future scheduleForcedShutdown() { return getShutdownScheduler().schedule( createForcedShutdownTask(), timeoutForcedNanos, NANOSECONDS ); } @@ -162,11 +174,11 @@ private ScheduledExecutorService getShutdownScheduler() return shutdownScheduler; } - private Callable> createShutdownTask() + private Callable createShutdownTask() { - return new Callable>() + return new Callable() { - public Collection call() + public ShutdownResult call() throws Exception { boolean stampedStatusWithTimeout = ParallelComputer.this.shutdownStatus.tryTimeout(); @@ -175,11 +187,11 @@ public Collection call() }; } - private Callable> createForcedShutdownTask() + private Callable createForcedShutdownTask() { - return new Callable>() + return new Callable() { - public Collection call() + public ShutdownResult call() throws Exception { boolean stampedStatusWithTimeout = ParallelComputer.this.forcedShutdownStatus.tryTimeout(); @@ -225,19 +237,27 @@ else if ( timeout2 == 0 ) } } - private static void printShutdownHook( Collection executedTests, - Future> testsBeforeShutdown ) + private static void printShutdownHook( Collection executedTests, Collection incompleteTests, + Future testsBeforeShutdown ) throws ExecutionException, InterruptedException { if ( testsBeforeShutdown != null ) { - for ( final Description executedTest : testsBeforeShutdown.get() ) + for ( final Description test : testsBeforeShutdown.get().getTriggeredTests() ) + { + if ( test != null && test.getDisplayName() != null ) + { + executedTests.add( test.getDisplayName() ); + } + } + + for ( final Description test : testsBeforeShutdown.get().getIncompleteTests() ) { - if ( executedTest != null && executedTest.getDisplayName() != null ) + if ( test != null && test.getDisplayName() != null ) { - executedTests.add( executedTest.getDisplayName() ); + incompleteTests.add( test.getDisplayName() ); } } } } -} +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java index d18edd7b1e..dd120f9daa 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java @@ -257,15 +257,17 @@ private PC() } @Override - protected Collection describeStopped( boolean shutdownNow ) + protected ShutdownResult describeStopped( boolean shutdownNow ) { - Collection startedTests = notThreadSafeTests.describeStopped( shutdownNow ); + ShutdownResult shutdownResult = notThreadSafeTests.describeStopped( shutdownNow ); final Scheduler m = master; if ( m != null ) { - startedTests.addAll( m.describeStopped( shutdownNow ) ); + ShutdownResult shutdownResultOfMaster = m.describeStopped( shutdownNow ); + shutdownResult.getTriggeredTests().addAll( shutdownResultOfMaster.getTriggeredTests() ); + shutdownResult.getIncompleteTests().addAll( shutdownResultOfMaster.getIncompleteTests() ); } - return startedTests; + return shutdownResult; } @Override diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java index 8feeb0211a..79f6bd9183 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java @@ -61,6 +61,8 @@ public class Scheduler private volatile boolean started = false; + private volatile boolean finished = false; + private volatile Controller masterController; /** @@ -212,17 +214,19 @@ protected void logQuietly( String msg ) * @param stopNow if true interrupts waiting test methods * @return collection of descriptions started before shutting down */ - protected Collection describeStopped( boolean stopNow ) + protected ShutdownResult describeStopped( boolean stopNow ) { Collection executedTests = new ConcurrentLinkedQueue(); - stop( executedTests, false, stopNow ); - return executedTests; + Collection incompleteTests = new ConcurrentLinkedQueue(); + stop( executedTests, incompleteTests, false, stopNow ); + return new ShutdownResult( executedTests, incompleteTests ); } /** * Stop/Shutdown/Interrupt scheduler and its children (if any). * * @param executedTests Started tests which have finished normally or abruptly till called this method. + * @param incompleteTests Started tests which have finished incomplete due to shutdown. * @param tryCancelFutures Useful to set to {@code false} if a timeout is specified in plugin config. * When the runner of * {@link ParallelComputer#getSuite(org.junit.runners.model.RunnerBuilder, Class[])} @@ -234,19 +238,28 @@ protected Collection describeStopped( boolean stopNow ) * {@link java.util.concurrent.Future#cancel(boolean) Future#cancel(true)} or * {@link Thread#interrupt()}. */ - private void stop( Collection executedTests, boolean tryCancelFutures, boolean stopNow ) + private void stop( Collection executedTests, Collection incompleteTests, + boolean tryCancelFutures, boolean stopNow ) { shutdown = true; try { - if ( executedTests != null && started && !UNUSED_DESCRIPTIONS.contains( description ) ) + if ( started && !UNUSED_DESCRIPTIONS.contains( description ) ) { - executedTests.add( description ); + if ( executedTests != null ) + { + executedTests.add( description ); + } + + if ( incompleteTests != null && !finished ) + { + incompleteTests.add( description ); + } } for ( Controller slave : slaves ) { - slave.stop( executedTests, tryCancelFutures, stopNow ); + slave.stop( executedTests, incompleteTests, tryCancelFutures, stopNow ); } } finally @@ -277,7 +290,7 @@ protected boolean shutdownThreadPoolsAwaitingKilled() { if ( masterController == null ) { - stop( null, true, false ); + stop( null, null, true, false ); boolean isNotInterrupted = true; if ( strategy != null ) { @@ -323,7 +336,7 @@ else if ( canSchedule() && strategy.canSchedule() ) } catch ( RejectedExecutionException e ) { - stop( null, true, false ); + stop( null, null, true, false ); } catch ( Throwable t ) { @@ -343,6 +356,10 @@ public void finished() { logQuietly( e ); } + finally + { + finished = true; + } } private Runnable wrapTask( final Runnable task ) @@ -396,9 +413,10 @@ boolean canSchedule() return Scheduler.this.canSchedule(); } - void stop( Collection executedTests, boolean tryCancelFutures, boolean shutdownNow ) + void stop( Collection executedTests, Collection incompleteTests, + boolean tryCancelFutures, boolean shutdownNow ) { - slave.stop( executedTests, tryCancelFutures, shutdownNow ); + slave.stop( executedTests, incompleteTests, tryCancelFutures, shutdownNow ); } /** @@ -449,7 +467,7 @@ public void rejectedExecution( Runnable r, ThreadPoolExecutor executor ) { if ( executor.isShutdown() ) { - Scheduler.this.stop( null, true, false ); + Scheduler.this.stop( null, null, true, false ); } final RejectedExecutionHandler poolHandler = this.poolHandler; if ( poolHandler != null ) diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ShutdownResult.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ShutdownResult.java new file mode 100644 index 0000000000..ef95be8e2c --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ShutdownResult.java @@ -0,0 +1,57 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * 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 java.util.Collection; +import java.util.Collections; + +/** + * Populates collection {@code triggeredTests} of descriptions started before shutting down. + * Populates collection {@code incompleteTests} which describes started tests but unfinished due to abrupt shutdown. + * The collection {@code triggeredTests} contains all elements from {@code incompleteTests}. + * + * @author Tibor Digana (tibor17) + * @see Scheduler + * @since 2.18 + */ +public final class ShutdownResult +{ + private final Collection triggeredTests; + + private final Collection incompleteTests; + + public ShutdownResult( Collection triggeredTests, Collection incompleteTests ) + { + this.triggeredTests = triggeredTests == null ? Collections.emptySet() : triggeredTests; + this.incompleteTests = incompleteTests == null ? Collections.emptySet() : incompleteTests; + } + + public Collection getTriggeredTests() + { + return triggeredTests; + } + + public Collection getIncompleteTests() + { + return incompleteTests; + } +} diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ShutdownStatus.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ShutdownStatus.java index 670808933f..b1a519f3a2 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ShutdownStatus.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ShutdownStatus.java @@ -19,15 +19,9 @@ * under the License. */ -import org.junit.runner.Description; - -import java.util.Collection; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; -// CHECKSTYLE_OFF: imports -import static org.apache.maven.surefire.junitcore.pc.ExecutionStatus.*; - /** * Wrapper of {@link ParallelComputer ParallelComputer status information} and tests been populated before * a shutdown hook has been triggered. @@ -38,32 +32,33 @@ */ final class ShutdownStatus { - private final AtomicReference status = new AtomicReference( STARTED ); + private final AtomicReference status = + new AtomicReference( ExecutionStatus.STARTED ); - private Future> descriptionsBeforeShutdown; + private Future descriptionsBeforeShutdown; boolean tryFinish() { - return status.compareAndSet( STARTED, FINISHED ); + return status.compareAndSet( ExecutionStatus.STARTED, ExecutionStatus.FINISHED ); } boolean tryTimeout() { - return status.compareAndSet( STARTED, TIMEOUT ); + return status.compareAndSet( ExecutionStatus.STARTED, ExecutionStatus.TIMEOUT ); } boolean isTimeoutElapsed() { - return status.get() == TIMEOUT; + return status.get() == ExecutionStatus.TIMEOUT; } - Future> getDescriptionsBeforeShutdown() + Future getDescriptionsBeforeShutdown() { return descriptionsBeforeShutdown; } - void setDescriptionsBeforeShutdown( Future> descriptionsBeforeShutdown ) + void setDescriptionsBeforeShutdown( Future descriptionsBeforeShutdown ) { this.descriptionsBeforeShutdown = descriptionsBeforeShutdown; } -} +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SingleThreadScheduler.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SingleThreadScheduler.java index 52dba73774..87c11141f6 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SingleThreadScheduler.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SingleThreadScheduler.java @@ -68,12 +68,11 @@ RunnerScheduler newRunnerScheduler() /** * @see Scheduler#describeStopped(boolean) */ - Collection describeStopped( boolean shutdownNow ) + ShutdownResult describeStopped( boolean shutdownNow ) { - Collection activeChildren = - new ConcurrentLinkedQueue( master.describeStopped( shutdownNow ) ); - activeChildren.removeAll( UNUSED_DESCRIPTIONS ); - return activeChildren; + ShutdownResult shutdownResult = master.describeStopped( shutdownNow ); + return new ShutdownResult( copyExisting( shutdownResult.getTriggeredTests() ), + copyExisting( shutdownResult.getIncompleteTests() ) ); } /** @@ -83,4 +82,11 @@ boolean shutdownThreadPoolsAwaitingKilled() { return master.shutdownThreadPoolsAwaitingKilled(); } + + private Collection copyExisting( Collection descriptions ) + { + Collection activeChildren = new ConcurrentLinkedQueue( descriptions ); + activeChildren.removeAll( UNUSED_DESCRIPTIONS ); + return activeChildren; + } } \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java index ce6ef44b43..63d25f8dfb 100644 --- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java @@ -561,7 +561,7 @@ Result run( final boolean useInterrupt ) { public void run() { - Collection startedTests = computer.describeStopped( useInterrupt ); + Collection startedTests = computer.describeStopped( useInterrupt ).getTriggeredTests(); assertThat( startedTests.size(), is( not( 0 ) ) ); } };