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