1 package org.apache.maven.surefire.junitcore.pc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.surefire.testset.TestSetFailedException;
23 import org.apache.maven.surefire.util.internal.DaemonThreadFactory;
24 import org.junit.runner.Computer;
25 import org.junit.runner.Description;
26
27 import java.util.Collection;
28 import java.util.TreeSet;
29 import java.util.concurrent.Callable;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.Future;
33 import java.util.concurrent.ScheduledExecutorService;
34 import java.util.concurrent.ThreadFactory;
35
36 import static java.util.concurrent.TimeUnit.NANOSECONDS;
37
38
39
40
41
42
43
44
45 public abstract class ParallelComputer
46 extends Computer
47 {
48 private static final ThreadFactory DAEMON_THREAD_FACTORY = DaemonThreadFactory.newDaemonThreadFactory();
49
50 private static final double NANOS_IN_A_SECOND = 1E9;
51
52 private final ShutdownStatus shutdownStatus = new ShutdownStatus();
53
54 private final ShutdownStatus forcedShutdownStatus = new ShutdownStatus();
55
56 private final long timeoutNanos;
57
58 private final long timeoutForcedNanos;
59
60 private ScheduledExecutorService shutdownScheduler;
61
62 public ParallelComputer( double timeoutInSeconds, double timeoutForcedInSeconds )
63 {
64 this.timeoutNanos = secondsToNanos( timeoutInSeconds );
65 this.timeoutForcedNanos = secondsToNanos( timeoutForcedInSeconds );
66 }
67
68 protected abstract ShutdownResult describeStopped( boolean shutdownNow );
69
70 abstract boolean shutdownThreadPoolsAwaitingKilled();
71
72 protected final void beforeRunQuietly()
73 {
74 shutdownStatus.setDescriptionsBeforeShutdown( hasTimeout() ? scheduleShutdown() : null );
75 forcedShutdownStatus.setDescriptionsBeforeShutdown( hasTimeoutForced() ? scheduleForcedShutdown() : null );
76 }
77
78 protected final boolean afterRunQuietly()
79 {
80 shutdownStatus.tryFinish();
81 forcedShutdownStatus.tryFinish();
82 boolean notInterrupted = true;
83 if ( shutdownScheduler != null )
84 {
85 shutdownScheduler.shutdownNow();
86
87
88
89
90 Thread.interrupted();
91 try
92 {
93 shutdownScheduler.awaitTermination( Long.MAX_VALUE, NANOSECONDS );
94 }
95 catch ( InterruptedException e )
96 {
97 notInterrupted = false;
98 }
99 }
100 notInterrupted &= shutdownThreadPoolsAwaitingKilled();
101 return notInterrupted;
102 }
103
104 public String describeElapsedTimeout()
105 throws TestSetFailedException
106 {
107 final StringBuilder msg = new StringBuilder();
108 final boolean isShutdownTimeout = shutdownStatus.isTimeoutElapsed();
109 final boolean isForcedShutdownTimeout = forcedShutdownStatus.isTimeoutElapsed();
110 if ( isShutdownTimeout || isForcedShutdownTimeout )
111 {
112 msg.append( "The test run has finished abruptly after timeout of " );
113 msg.append( nanosToSeconds( minTimeout( timeoutNanos, timeoutForcedNanos ) ) );
114 msg.append( " seconds.\n" );
115
116 try
117 {
118 final TreeSet<String> executedTests = new TreeSet<String>();
119 final TreeSet<String> incompleteTests = new TreeSet<String>();
120
121 if ( isShutdownTimeout )
122 {
123 printShutdownHook( executedTests, incompleteTests, shutdownStatus.getDescriptionsBeforeShutdown() );
124 }
125
126 if ( isForcedShutdownTimeout )
127 {
128 printShutdownHook( executedTests, incompleteTests,
129 forcedShutdownStatus.getDescriptionsBeforeShutdown() );
130 }
131
132 if ( !executedTests.isEmpty() )
133 {
134 msg.append( "These tests were executed in prior to the shutdown operation:\n" );
135 for ( String executedTest : executedTests )
136 {
137 msg.append( executedTest ).append( '\n' );
138 }
139 }
140
141 if ( !incompleteTests.isEmpty() )
142 {
143 msg.append( "These tests are incomplete:\n" );
144 for ( String incompleteTest : incompleteTests )
145 {
146 msg.append( incompleteTest ).append( '\n' );
147 }
148 }
149 }
150 catch ( InterruptedException e )
151 {
152 throw new TestSetFailedException( "Timed termination was interrupted.", e );
153 }
154 catch ( ExecutionException e )
155 {
156 throw new TestSetFailedException( e.getLocalizedMessage(), e.getCause() );
157 }
158 }
159 return msg.toString();
160 }
161
162 private Future<ShutdownResult> scheduleShutdown()
163 {
164 return getShutdownScheduler().schedule( createShutdownTask(), timeoutNanos, NANOSECONDS );
165 }
166
167 private Future<ShutdownResult> scheduleForcedShutdown()
168 {
169 return getShutdownScheduler().schedule( createForcedShutdownTask(), timeoutForcedNanos, NANOSECONDS );
170 }
171
172 private ScheduledExecutorService getShutdownScheduler()
173 {
174 if ( shutdownScheduler == null )
175 {
176 shutdownScheduler = Executors.newScheduledThreadPool( 2, DAEMON_THREAD_FACTORY );
177 }
178 return shutdownScheduler;
179 }
180
181 private Callable<ShutdownResult> createShutdownTask()
182 {
183 return new Callable<ShutdownResult>()
184 {
185 @Override
186 public ShutdownResult call()
187 throws Exception
188 {
189 boolean stampedStatusWithTimeout = ParallelComputer.this.shutdownStatus.tryTimeout();
190 return stampedStatusWithTimeout ? ParallelComputer.this.describeStopped( false ) : null;
191 }
192 };
193 }
194
195 private Callable<ShutdownResult> createForcedShutdownTask()
196 {
197 return new Callable<ShutdownResult>()
198 {
199 @Override
200 public ShutdownResult call()
201 throws Exception
202 {
203 boolean stampedStatusWithTimeout = ParallelComputer.this.forcedShutdownStatus.tryTimeout();
204 return stampedStatusWithTimeout ? ParallelComputer.this.describeStopped( true ) : null;
205 }
206 };
207 }
208
209 private double nanosToSeconds( long nanos )
210 {
211 return (double) nanos / NANOS_IN_A_SECOND;
212 }
213
214 private boolean hasTimeout()
215 {
216 return timeoutNanos > 0;
217 }
218
219 private boolean hasTimeoutForced()
220 {
221 return timeoutForcedNanos > 0;
222 }
223
224 private static long secondsToNanos( double seconds )
225 {
226 double nanos = seconds > 0 ? seconds * NANOS_IN_A_SECOND : 0;
227 return Double.isInfinite( nanos ) || nanos >= Long.MAX_VALUE ? 0 : (long) nanos;
228 }
229
230 private static long minTimeout( long timeout1, long timeout2 )
231 {
232 if ( timeout1 == 0 )
233 {
234 return timeout2;
235 }
236 else if ( timeout2 == 0 )
237 {
238 return timeout1;
239 }
240 else
241 {
242 return Math.min( timeout1, timeout2 );
243 }
244 }
245
246 private static void printShutdownHook( Collection<String> executedTests, Collection<String> incompleteTests,
247 Future<ShutdownResult> testsBeforeShutdown )
248 throws ExecutionException, InterruptedException
249 {
250 if ( testsBeforeShutdown != null )
251 {
252 for ( final Description test : testsBeforeShutdown.get().getTriggeredTests() )
253 {
254 if ( test != null && test.getDisplayName() != null )
255 {
256 executedTests.add( test.getDisplayName() );
257 }
258 }
259
260 for ( final Description test : testsBeforeShutdown.get().getIncompleteTests() )
261 {
262 if ( test != null && test.getDisplayName() != null )
263 {
264 incompleteTests.add( test.getDisplayName() );
265 }
266 }
267 }
268 }
269 }