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.junitcore.JUnitCoreParameters;
23 import org.apache.maven.surefire.testset.TestSetFailedException;
24 import org.junit.runner.Description;
25
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Iterator;
29
30
31
32
33
34
35
36
37
38
39
40 final class ParallelComputerUtil
41 {
42 private static final Collection<Description> UNUSED_DESCRIPTIONS =
43 Arrays.asList( null, Description.createSuiteDescription( "null" ), Description.TEST_MECHANISM,
44 Description.EMPTY );
45
46 private static int availableProcessors = Runtime.getRuntime().availableProcessors();
47
48 private ParallelComputerUtil()
49 {
50 throw new IllegalStateException( "Suppresses calling constructor, ensuring non-instantiability." );
51 }
52
53
54
55
56 static void overrideAvailableProcessors( int availableProcessors )
57 {
58 ParallelComputerUtil.availableProcessors = availableProcessors;
59 }
60
61
62
63
64 static void setDefaultAvailableProcessors()
65 {
66 ParallelComputerUtil.availableProcessors = Runtime.getRuntime().availableProcessors();
67 }
68
69 static Concurrency resolveConcurrency( JUnitCoreParameters params, RunnerCounter counts )
70 throws TestSetFailedException
71 {
72 if ( !params.isParallelismSelected() )
73 {
74 throw new TestSetFailedException( "Unspecified parameter '" + JUnitCoreParameters.PARALLEL_KEY + "'." );
75 }
76
77 if ( !params.isUseUnlimitedThreads() && !hasThreadCount( params ) && !hasThreadCounts( params ) )
78 {
79 throw new TestSetFailedException( "Unspecified thread-count(s). "
80 + "See the parameters "
81 + JUnitCoreParameters.USEUNLIMITEDTHREADS_KEY + ", "
82 + JUnitCoreParameters.THREADCOUNT_KEY + ", "
83 + JUnitCoreParameters.THREADCOUNTSUITES_KEY + ", "
84 + JUnitCoreParameters.THREADCOUNTCLASSES_KEY + ", "
85 + JUnitCoreParameters.THREADCOUNTMETHODS_KEY + "." );
86 }
87
88 if ( params.isUseUnlimitedThreads() )
89 {
90 return concurrencyForUnlimitedThreads( params );
91 }
92 else if ( hasThreadCount( params ) )
93 {
94 if ( hasThreadCounts( params ) )
95 {
96 return isLeafUnspecified( params )
97 ? concurrencyFromAllThreadCountsButUnspecifiedLeafCount( params, counts )
98 : concurrencyFromAllThreadCounts( params );
99 }
100 else
101 {
102 return estimateConcurrency( params, counts );
103 }
104 }
105 else
106 {
107 return concurrencyFromThreadCounts( params );
108 }
109 }
110
111 static boolean isUnusedDescription( Description examined )
112 {
113 if ( UNUSED_DESCRIPTIONS.contains( examined ) )
114 {
115 return true;
116 }
117 else
118 {
119
120 for ( Description unused : UNUSED_DESCRIPTIONS )
121 {
122 if ( unused != null && unused.getDisplayName().equals( examined.getDisplayName() ) )
123 {
124 return true;
125 }
126 }
127 return false;
128 }
129 }
130
131 static void removeUnusedDescriptions( Collection<Description> examined )
132 {
133 for ( Iterator<Description> it = examined.iterator(); it.hasNext(); )
134 {
135 if ( isUnusedDescription( it.next() ) )
136 {
137 it.remove();
138 }
139 }
140 }
141
142 private static Concurrency concurrencyForUnlimitedThreads( JUnitCoreParameters params )
143 {
144 Concurrency concurrency = new Concurrency();
145 concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0;
146 concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0;
147 concurrency.methods = params.isParallelMethods() ? threadCountMethods( params ) : 0;
148 concurrency.capacity = Integer.MAX_VALUE;
149 return concurrency;
150 }
151
152 private static Concurrency estimateConcurrency( JUnitCoreParameters params, RunnerCounter counts )
153 {
154 final Concurrency concurrency = new Concurrency();
155 final int parallelEntities = countParallelEntities( params );
156 concurrency.capacity = multiplyByCoreCount( params, params.getThreadCount() );
157 if ( parallelEntities == 1 || counts == null || counts.classes == 0 )
158 {
159
160 double ratio = 1d / parallelEntities;
161 int threads = multiplyByCoreCount( params, ratio * params.getThreadCount() );
162 concurrency.suites = params.isParallelSuites() ? minSuites( threads, counts ) : 0;
163 concurrency.classes = params.isParallelClasses() ? minClasses( threads, counts ) : 0;
164 concurrency.methods = params.isParallelMethods() ? minMethods( threads, counts ) : 0;
165 if ( parallelEntities == 1 )
166 {
167 concurrency.capacity = 0;
168 }
169 else
170 {
171 adjustLeaf( params, concurrency );
172 }
173 }
174 else
175 {
176
177 concurrency.suites = params.isParallelSuites() ? toNonNegative( counts.suites ) : 0;
178 concurrency.classes = params.isParallelClasses() ? toNonNegative( counts.classes ) : 0;
179 concurrency.methods =
180 params.isParallelMethods() ? toNonNegative( Math.ceil( counts.methods / (double) counts.classes ) ) : 0;
181 double sum = toNonNegative( concurrency.suites + concurrency.classes + concurrency.methods );
182 if ( concurrency.capacity < sum && sum != 0 )
183 {
184
185 double weight = concurrency.capacity / sum;
186 concurrency.suites *= weight;
187 concurrency.classes *= weight;
188 concurrency.methods *= weight;
189 }
190 adjustLeaf( params, concurrency );
191 }
192 return concurrency;
193 }
194
195 private static Concurrency concurrencyFromAllThreadCountsButUnspecifiedLeafCount( JUnitCoreParameters params,
196 RunnerCounter counts )
197 {
198 Concurrency concurrency = new Concurrency();
199 concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0;
200 concurrency.suites = params.isParallelSuites() ? multiplyByCoreCount( params, concurrency.suites ) : 0;
201 concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0;
202 concurrency.classes = params.isParallelClasses() ? multiplyByCoreCount( params, concurrency.classes ) : 0;
203 concurrency.methods = params.isParallelMethods() ? params.getThreadCountMethods() : 0;
204 concurrency.methods = params.isParallelMethods() ? multiplyByCoreCount( params, concurrency.methods ) : 0;
205 concurrency.capacity = multiplyByCoreCount( params, params.getThreadCount() );
206
207 if ( counts != null )
208 {
209 concurrency.suites = toNonNegative( Math.min( concurrency.suites, counts.suites ) );
210 concurrency.classes = toNonNegative( Math.min( concurrency.classes, counts.classes ) );
211 }
212
213 setLeafInfinite( params, concurrency );
214
215 return concurrency;
216 }
217
218 private static Concurrency concurrencyFromAllThreadCounts( JUnitCoreParameters params )
219 {
220 Concurrency concurrency = new Concurrency();
221 concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0;
222 concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0;
223 concurrency.methods = params.isParallelMethods() ? params.getThreadCountMethods() : 0;
224 concurrency.capacity = params.getThreadCount();
225 double all = sumThreadCounts( concurrency );
226
227 concurrency.suites = params.isParallelSuites() ? multiplyByCoreCount( params, concurrency.capacity * (
228 concurrency.suites / all ) ) : 0;
229
230 concurrency.classes = params.isParallelClasses() ? multiplyByCoreCount( params, concurrency.capacity * (
231 concurrency.classes / all ) ) : 0;
232
233 concurrency.methods = params.isParallelMethods() ? multiplyByCoreCount( params, concurrency.capacity * (
234 concurrency.methods / all ) ) : 0;
235
236 concurrency.capacity = multiplyByCoreCount( params, concurrency.capacity );
237 adjustPrecisionInLeaf( params, concurrency );
238 return concurrency;
239 }
240
241 private static Concurrency concurrencyFromThreadCounts( JUnitCoreParameters params )
242 {
243 Concurrency concurrency = new Concurrency();
244 concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0;
245 concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0;
246 concurrency.methods = params.isParallelMethods() ? threadCountMethods( params ) : 0;
247 concurrency.capacity = toNonNegative( sumThreadCounts( concurrency ) );
248 return concurrency;
249 }
250
251 private static int countParallelEntities( JUnitCoreParameters params )
252 {
253 int count = 0;
254 if ( params.isParallelSuites() )
255 {
256 count++;
257 }
258
259 if ( params.isParallelClasses() )
260 {
261 count++;
262 }
263
264 if ( params.isParallelMethods() )
265 {
266 count++;
267 }
268 return count;
269 }
270
271 private static void adjustPrecisionInLeaf( JUnitCoreParameters params, Concurrency concurrency )
272 {
273 if ( params.isParallelMethods() )
274 {
275 concurrency.methods = concurrency.capacity - concurrency.suites - concurrency.classes;
276 }
277 else if ( params.isParallelClasses() )
278 {
279 concurrency.classes = concurrency.capacity - concurrency.suites;
280 }
281 }
282
283 private static void adjustLeaf( JUnitCoreParameters params, Concurrency concurrency )
284 {
285 if ( params.isParallelMethods() )
286 {
287 concurrency.methods = Integer.MAX_VALUE;
288 }
289 else if ( params.isParallelClasses() )
290 {
291 concurrency.classes = Integer.MAX_VALUE;
292 }
293 }
294
295 private static void setLeafInfinite( JUnitCoreParameters params, Concurrency concurrency )
296 {
297 if ( params.isParallelMethods() )
298 {
299 concurrency.methods = Integer.MAX_VALUE;
300 }
301 else if ( params.isParallelClasses() )
302 {
303 concurrency.classes = Integer.MAX_VALUE;
304 }
305 else if ( params.isParallelSuites() )
306 {
307 concurrency.suites = Integer.MAX_VALUE;
308 }
309 }
310
311 private static boolean isLeafUnspecified( JUnitCoreParameters params )
312 {
313 int maskOfParallel = params.isParallelSuites() ? 4 : 0;
314 maskOfParallel |= params.isParallelClasses() ? 2 : 0;
315 maskOfParallel |= params.isParallelMethods() ? 1 : 0;
316
317 int maskOfConcurrency = params.getThreadCountSuites() > 0 ? 4 : 0;
318 maskOfConcurrency |= params.getThreadCountClasses() > 0 ? 2 : 0;
319 maskOfConcurrency |= params.getThreadCountMethods() > 0 ? 1 : 0;
320
321 maskOfConcurrency &= maskOfParallel;
322
323 int leaf = Integer.lowestOneBit( maskOfParallel );
324 return maskOfConcurrency == maskOfParallel - leaf;
325 }
326
327 private static double sumThreadCounts( Concurrency concurrency )
328 {
329 double sum = concurrency.suites;
330 sum += concurrency.classes;
331 sum += concurrency.methods;
332 return sum;
333 }
334
335 private static boolean hasThreadCounts( JUnitCoreParameters jUnitCoreParameters )
336 {
337 return ( jUnitCoreParameters.isParallelSuites() && jUnitCoreParameters.getThreadCountSuites() > 0 )
338 || ( jUnitCoreParameters.isParallelClasses() && jUnitCoreParameters.getThreadCountClasses() > 0 )
339 || ( jUnitCoreParameters.isParallelMethods() && jUnitCoreParameters.getThreadCountMethods() > 0 );
340 }
341
342 private static boolean hasThreadCount( JUnitCoreParameters jUnitCoreParameters )
343 {
344 return jUnitCoreParameters.getThreadCount() > 0;
345 }
346
347 private static int threadCountMethods( JUnitCoreParameters jUnitCoreParameters )
348 {
349 return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountMethods() );
350 }
351
352 private static int threadCountClasses( JUnitCoreParameters jUnitCoreParameters )
353 {
354 return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountClasses() );
355 }
356
357 private static int threadCountSuites( JUnitCoreParameters jUnitCoreParameters )
358 {
359 return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountSuites() );
360 }
361
362 private static int multiplyByCoreCount( JUnitCoreParameters jUnitCoreParameters, double threadsPerCore )
363 {
364 double numberOfThreads =
365 jUnitCoreParameters.isPerCoreThreadCount() ? threadsPerCore * (double) availableProcessors : threadsPerCore;
366
367 return numberOfThreads > 0 ? toNonNegative( numberOfThreads ) : Integer.MAX_VALUE;
368 }
369
370 private static int minSuites( int threads, RunnerCounter counts )
371 {
372 long count = counts == null ? Integer.MAX_VALUE : counts.suites;
373 return Math.min( threads, toNonNegative( count ) );
374 }
375
376 private static int minClasses( int threads, RunnerCounter counts )
377 {
378 long count = counts == null ? Integer.MAX_VALUE : counts.classes;
379 return Math.min( threads, toNonNegative( count ) );
380 }
381
382 private static int minMethods( int threads, RunnerCounter counts )
383 {
384 long count = counts == null ? Integer.MAX_VALUE : counts.methods;
385 return Math.min( threads, toNonNegative( count ) );
386 }
387
388 private static int toNonNegative( long num )
389 {
390 return (int) Math.min( num > 0 ? num : 0, Integer.MAX_VALUE );
391 }
392
393 private static int toNonNegative( double num )
394 {
395 return (int) Math.min( num > 0 ? num : 0, Integer.MAX_VALUE );
396 }
397 }