View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.surefire.junitcore.pc;
20  
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.Comparator;
26  import java.util.Date;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.concurrent.ConcurrentLinkedQueue;
30  import java.util.concurrent.TimeUnit;
31  
32  import net.jcip.annotations.NotThreadSafe;
33  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
34  import org.junit.After;
35  import org.junit.AfterClass;
36  import org.junit.Before;
37  import org.junit.BeforeClass;
38  import org.junit.Test;
39  import org.junit.runner.Description;
40  import org.junit.runner.JUnitCore;
41  import org.junit.runner.Result;
42  import org.junit.runner.RunWith;
43  import org.junit.runner.notification.RunNotifier;
44  import org.junit.runners.ParentRunner;
45  import org.junit.runners.Suite;
46  import org.junit.runners.model.InitializationError;
47  
48  import static org.apache.maven.surefire.junitcore.pc.RangeMatcher.between;
49  import static org.hamcrest.core.AnyOf.anyOf;
50  import static org.hamcrest.core.Is.is;
51  import static org.hamcrest.core.IsNot.not;
52  import static org.junit.Assert.assertFalse;
53  import static org.junit.Assert.assertNotNull;
54  import static org.junit.Assert.assertNotSame;
55  import static org.junit.Assert.assertSame;
56  import static org.junit.Assert.assertThat;
57  import static org.junit.Assert.assertTrue;
58  import static org.junit.Assert.fail;
59  import static org.mockito.Mockito.mock;
60  
61  /**
62   * @author Tibor Digana (tibor17)
63   * @since 2.16
64   */
65  @SuppressWarnings("checkstyle:magicnumber")
66  public class ParallelComputerBuilderTest {
67      private static final int DELAY_MULTIPLIER = 7;
68  
69      private static final Object CLASS1BLOCK = new Object();
70  
71      private static volatile boolean beforeShutdown;
72  
73      private static volatile Runnable shutdownTask;
74  
75      private static final ConsoleLogger LOGGER = mock(ConsoleLogger.class);
76  
77      private static void testKeepBeforeAfter(ParallelComputerBuilder builder, Class<?>... classes) {
78          JUnitCore core = new JUnitCore();
79          for (int round = 0; round < 5; round++) {
80              NothingDoingTest1.METHODS.clear();
81              Result result = core.run(builder.buildComputer(), classes);
82              assertTrue(result.wasSuccessful());
83              Iterator<String> methods = NothingDoingTest1.METHODS.iterator();
84              for (Class<?> clazz : classes) {
85                  String a = clazz.getName() + "#a()";
86                  String b = clazz.getName() + "#b()";
87                  assertThat(methods.next(), is("init"));
88                  assertThat(methods.next(), anyOf(is(a), is(b)));
89                  assertThat(methods.next(), anyOf(is(a), is(b)));
90                  assertThat(methods.next(), is("deinit"));
91              }
92          }
93      }
94  
95      @BeforeClass
96      public static void cleanup() throws InterruptedException {
97          System.gc();
98          Thread.sleep(500L);
99      }
100 
101     @Before
102     public void beforeTest() {
103         Class1.maxConcurrentMethods = 0;
104         Class1.concurrentMethods = 0;
105         shutdownTask = null;
106         NotThreadSafeTest1.t = null;
107         NotThreadSafeTest2.t = null;
108         NotThreadSafeTest3.t = null;
109         NormalTest1.t = null;
110         NormalTest2.t = null;
111     }
112 
113     @Test
114     public void testsWithoutChildrenShouldAlsoBeRun() {
115         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(LOGGER);
116         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
117         Result result = new JUnitCore().run(computer, TestWithoutPrecalculatedChildren.class);
118         assertThat(result.getRunCount(), is(1));
119     }
120 
121     @Test
122     public void parallelMethodsReuseOneOrTwoThreads() {
123         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(LOGGER);
124         parallelComputerBuilder.useOnePool(4);
125 
126         // One thread because one suite: TestSuite, however the capacity is 5.
127         parallelComputerBuilder.parallelSuites(5);
128 
129         // Two threads because TestSuite has two classes, however the capacity is 5.
130         parallelComputerBuilder.parallelClasses(5);
131 
132         // One or two threads because one threads comes from '#useOnePool(4)'
133         // and next thread may be reused from finished class, however the capacity is 3.
134         parallelComputerBuilder.parallelMethods(3);
135 
136         assertFalse(parallelComputerBuilder.isOptimized());
137 
138         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
139         final JUnitCore core = new JUnitCore();
140         final long t1 = systemMillis();
141         final Result result = core.run(computer, TestSuite.class);
142         final long t2 = systemMillis();
143         final long timeSpent = t2 - t1;
144 
145         assertThat(computer.getSuites().size(), is(1));
146         assertThat(computer.getClasses().size(), is(0));
147         assertThat(computer.getNestedClasses().size(), is(2));
148         assertThat(computer.getNestedSuites().size(), is(0));
149         assertFalse(computer.isSplitPool());
150         assertThat(computer.getPoolCapacity(), is(4));
151         assertTrue(result.wasSuccessful());
152         if (Class1.maxConcurrentMethods == 1) {
153             assertThat(timeSpent, between(2000 * DELAY_MULTIPLIER - 50, 2250 * DELAY_MULTIPLIER));
154         } else if (Class1.maxConcurrentMethods == 2) {
155             assertThat(timeSpent, between(1500 * DELAY_MULTIPLIER - 50, 1750 * DELAY_MULTIPLIER));
156         } else {
157             fail();
158         }
159     }
160 
161     @Test
162     public void suiteAndClassInOnePool() {
163         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(LOGGER);
164         parallelComputerBuilder.useOnePool(5);
165         parallelComputerBuilder.parallelSuites(5);
166         parallelComputerBuilder.parallelClasses(5);
167         parallelComputerBuilder.parallelMethods(3);
168         assertFalse(parallelComputerBuilder.isOptimized());
169 
170         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
171         final JUnitCore core = new JUnitCore();
172         final long t1 = systemMillis();
173         final Result result = core.run(computer, TestSuite.class, Class1.class);
174         final long t2 = systemMillis();
175         final long timeSpent = t2 - t1;
176 
177         assertThat(computer.getSuites().size(), is(1));
178         assertThat(computer.getClasses().size(), is(1));
179         assertThat(computer.getNestedClasses().size(), is(2));
180         assertThat(computer.getNestedSuites().size(), is(0));
181         assertFalse(computer.isSplitPool());
182         assertThat(computer.getPoolCapacity(), is(5));
183         assertTrue(result.wasSuccessful());
184         assertThat(Class1.maxConcurrentMethods, is(2));
185         assertThat(
186                 timeSpent,
187                 anyOf(
188                         between(1500 * DELAY_MULTIPLIER - 50, 1750 * DELAY_MULTIPLIER),
189                         between(2000 * DELAY_MULTIPLIER - 50, 2250 * DELAY_MULTIPLIER),
190                         between(2500 * DELAY_MULTIPLIER - 50, 2750 * DELAY_MULTIPLIER)));
191     }
192 
193     @Test
194     public void onePoolWithUnlimitedParallelMethods() {
195         // see ParallelComputerBuilder Javadoc
196         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(LOGGER);
197         parallelComputerBuilder.useOnePool(8);
198         parallelComputerBuilder.parallelSuites(2);
199         parallelComputerBuilder.parallelClasses(4);
200         parallelComputerBuilder.parallelMethods();
201         assertFalse(parallelComputerBuilder.isOptimized());
202 
203         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
204         final JUnitCore core = new JUnitCore();
205         final long t1 = systemMillis();
206         final Result result = core.run(computer, TestSuite.class, Class1.class);
207         final long t2 = systemMillis();
208         final long timeSpent = t2 - t1;
209 
210         assertThat(computer.getSuites().size(), is(1));
211         assertThat(computer.getClasses().size(), is(1));
212         assertThat(computer.getNestedClasses().size(), is(2));
213         assertThat(computer.getNestedSuites().size(), is(0));
214         assertFalse(computer.isSplitPool());
215         assertThat(computer.getPoolCapacity(), is(8));
216         assertTrue(result.wasSuccessful());
217         assertThat(Class1.maxConcurrentMethods, is(4));
218         assertThat(timeSpent, between(1000 * DELAY_MULTIPLIER - 50, 1250 * DELAY_MULTIPLIER));
219     }
220 
221     @Test
222     public void underflowParallelism() {
223         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(LOGGER);
224         parallelComputerBuilder.useOnePool(3);
225 
226         // One thread because one suite: TestSuite.
227         parallelComputerBuilder.parallelSuites(5);
228 
229         // One thread because of the limitation which is bottleneck.
230         parallelComputerBuilder.parallelClasses(1);
231 
232         // One thread remains from '#useOnePool(3)'.
233         parallelComputerBuilder.parallelMethods(3);
234 
235         assertFalse(parallelComputerBuilder.isOptimized());
236 
237         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
238         final JUnitCore core = new JUnitCore();
239         final long t1 = systemMillis();
240         final Result result = core.run(computer, TestSuite.class);
241         final long t2 = systemMillis();
242         final long timeSpent = t2 - t1;
243 
244         assertThat(computer.getSuites().size(), is(1));
245         assertThat(computer.getClasses().size(), is(0));
246         assertThat(computer.getNestedClasses().size(), is(2));
247         assertThat(computer.getNestedSuites().size(), is(0));
248         assertFalse(computer.isSplitPool());
249         assertThat(computer.getPoolCapacity(), is(3));
250         assertTrue(result.wasSuccessful());
251         assertThat(Class1.maxConcurrentMethods, is(1));
252         assertThat(timeSpent, between(2000 * DELAY_MULTIPLIER - 50, 2250 * DELAY_MULTIPLIER));
253     }
254 
255     @Test
256     public void separatePoolsWithSuite() {
257         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(LOGGER);
258         parallelComputerBuilder.parallelSuites(5);
259         parallelComputerBuilder.parallelClasses(5);
260         parallelComputerBuilder.parallelMethods(3);
261         assertFalse(parallelComputerBuilder.isOptimized());
262 
263         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
264         final JUnitCore core = new JUnitCore();
265         final long t1 = systemMillis();
266         final Result result = core.run(computer, TestSuite.class);
267         final long t2 = systemMillis();
268         final long timeSpent = t2 - t1;
269 
270         assertThat(computer.getSuites().size(), is(1));
271         assertThat(computer.getClasses().size(), is(0));
272         assertThat(computer.getNestedClasses().size(), is(2));
273         assertThat(computer.getNestedSuites().size(), is(0));
274         assertTrue(computer.isSplitPool());
275         assertThat(computer.getPoolCapacity(), is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED));
276         assertTrue(result.wasSuccessful());
277         assertThat(Class1.maxConcurrentMethods, is(3));
278         assertThat(timeSpent, between(1000 * DELAY_MULTIPLIER - 50, 1250 * DELAY_MULTIPLIER));
279     }
280 
281     @Test
282     public void separatePoolsWithSuiteAndClass() {
283         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(LOGGER);
284         parallelComputerBuilder.parallelSuites(5);
285         parallelComputerBuilder.parallelClasses(5);
286         parallelComputerBuilder.parallelMethods(3);
287         assertFalse(parallelComputerBuilder.isOptimized());
288 
289         // 6 methods altogether.
290         // 2 groups with 3 threads.
291         // Each group takes 0.5s.
292         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
293         final JUnitCore core = new JUnitCore();
294         final long t1 = systemMillis();
295         final Result result = core.run(computer, TestSuite.class, Class1.class);
296         final long t2 = systemMillis();
297         final long timeSpent = t2 - t1;
298 
299         assertThat(computer.getSuites().size(), is(1));
300         assertThat(computer.getClasses().size(), is(1));
301         assertThat(computer.getNestedClasses().size(), is(2));
302         assertThat(computer.getNestedSuites().size(), is(0));
303         assertTrue(computer.isSplitPool());
304         assertThat(computer.getPoolCapacity(), is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED));
305         assertTrue(result.wasSuccessful());
306         assertThat(Class1.maxConcurrentMethods, is(3));
307         assertThat(timeSpent, between(1000 * DELAY_MULTIPLIER - 50, 1250 * DELAY_MULTIPLIER));
308     }
309 
310     @Test
311     public void separatePoolsWithSuiteAndSequentialClasses() {
312         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(LOGGER);
313         parallelComputerBuilder.parallelSuites(5);
314         parallelComputerBuilder.parallelClasses(1);
315         parallelComputerBuilder.parallelMethods(3);
316         assertFalse(parallelComputerBuilder.isOptimized());
317 
318         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
319         final JUnitCore core = new JUnitCore();
320         final long t1 = systemMillis();
321         final Result result = core.run(computer, TestSuite.class, Class1.class);
322         final long t2 = systemMillis();
323         final long timeSpent = t2 - t1;
324 
325         assertThat(computer.getSuites().size(), is(1));
326         assertThat(computer.getClasses().size(), is(1));
327         assertThat(computer.getNestedClasses().size(), is(2));
328         assertThat(computer.getNestedSuites().size(), is(0));
329         assertTrue(computer.isSplitPool());
330         assertThat(computer.getPoolCapacity(), is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED));
331         assertTrue(result.wasSuccessful());
332         assertThat(Class1.maxConcurrentMethods, is(2));
333         assertThat(timeSpent, between(1500 * DELAY_MULTIPLIER - 50, 1750 * DELAY_MULTIPLIER));
334     }
335 
336     @Test(timeout = 2000 * DELAY_MULTIPLIER)
337     public void shutdown() {
338         final long t1 = systemMillis();
339         final Result result = new ShutdownTest().run(false);
340         final long t2 = systemMillis();
341         final long timeSpent = t2 - t1;
342         assertTrue(result.wasSuccessful());
343         assertTrue(beforeShutdown);
344         assertThat(timeSpent, between(500 * DELAY_MULTIPLIER - 50, 1250 * DELAY_MULTIPLIER));
345     }
346 
347     @Test(timeout = 2000 * DELAY_MULTIPLIER)
348     public void shutdownWithInterrupt() {
349         final long t1 = systemMillis();
350         new ShutdownTest().run(true);
351         final long t2 = systemMillis();
352         final long timeSpent = t2 - t1;
353         assertTrue(beforeShutdown);
354         assertThat(timeSpent, between(500 * DELAY_MULTIPLIER - 50, 1250 * DELAY_MULTIPLIER));
355     }
356 
357     @Test
358     public void nothingParallel() {
359         JUnitCore core = new JUnitCore();
360         ParallelComputerBuilder builder = new ParallelComputerBuilder(LOGGER);
361         assertFalse(builder.isOptimized());
362 
363         Result result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingTest2.class);
364         assertTrue(result.wasSuccessful());
365 
366         result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class);
367         assertTrue(result.wasSuccessful());
368 
369         builder.useOnePool(1);
370         assertFalse(builder.isOptimized());
371         result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingTest2.class);
372         assertTrue(result.wasSuccessful());
373 
374         builder.useOnePool(1);
375         assertFalse(builder.isOptimized());
376         result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class);
377         assertTrue(result.wasSuccessful());
378 
379         builder.useOnePool(2);
380         assertFalse(builder.isOptimized());
381         result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class);
382         assertTrue(result.wasSuccessful());
383 
384         Class<?>[] classes = {NothingDoingTest1.class, NothingDoingSuite.class};
385 
386         builder.useOnePool(2).parallelSuites(1).parallelClasses(1);
387         assertFalse(builder.isOptimized());
388         result = core.run(builder.buildComputer(), classes);
389         assertTrue(result.wasSuccessful());
390 
391         builder.useOnePool(2).parallelSuites(1).parallelClasses();
392         assertFalse(builder.isOptimized());
393         result = core.run(builder.buildComputer(), classes);
394         assertTrue(result.wasSuccessful());
395 
396         classes = new Class<?>[] {
397             NothingDoingSuite.class,
398             NothingDoingSuite.class,
399             NothingDoingTest1.class,
400             NothingDoingTest2.class,
401             NothingDoingTest3.class
402         };
403 
404         builder.useOnePool(2).parallelSuites(1).parallelClasses(1);
405         assertFalse(builder.isOptimized());
406         result = core.run(builder.buildComputer(), classes);
407         assertTrue(result.wasSuccessful());
408 
409         builder.useOnePool(2).parallelSuites(1).parallelClasses();
410         assertFalse(builder.isOptimized());
411         result = core.run(builder.buildComputer(), classes);
412         assertTrue(result.wasSuccessful());
413     }
414 
415     @Test
416     public void keepBeforeAfterOneClass() {
417         ParallelComputerBuilder builder = new ParallelComputerBuilder(LOGGER);
418         builder.parallelMethods();
419         assertFalse(builder.isOptimized());
420         testKeepBeforeAfter(builder, NothingDoingTest1.class);
421     }
422 
423     @Test
424     public void keepBeforeAfterTwoClasses() {
425         ParallelComputerBuilder builder = new ParallelComputerBuilder(LOGGER);
426         builder.useOnePool(5).parallelClasses(1).parallelMethods(2);
427         assertFalse(builder.isOptimized());
428         testKeepBeforeAfter(builder, NothingDoingTest1.class, NothingDoingTest2.class);
429     }
430 
431     @Test
432     public void keepBeforeAfterTwoParallelClasses() {
433         ParallelComputerBuilder builder = new ParallelComputerBuilder(LOGGER);
434         builder.useOnePool(8).parallelClasses(2).parallelMethods(2);
435         assertFalse(builder.isOptimized());
436         JUnitCore core = new JUnitCore();
437         NothingDoingTest1.METHODS.clear();
438         Class<?>[] classes = {NothingDoingTest1.class, NothingDoingTest2.class, NothingDoingTest3.class};
439         Result result = core.run(builder.buildComputer(), classes);
440         assertTrue(result.wasSuccessful());
441         ArrayList<String> methods = new ArrayList<>(NothingDoingTest1.METHODS);
442         assertThat(methods.size(), is(12));
443         assertThat(methods.subList(9, 12), is(not(Arrays.asList("deinit", "deinit", "deinit"))));
444     }
445 
446     @Test
447     public void notThreadSafeTest() {
448         ParallelComputerBuilder builder = new ParallelComputerBuilder(LOGGER)
449                 .useOnePool(6)
450                 .optimize(true)
451                 .parallelClasses(3)
452                 .parallelMethods(3);
453         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) builder.buildComputer();
454         Result result = new JUnitCore().run(computer, NotThreadSafeTest1.class, NotThreadSafeTest2.class);
455         assertTrue(result.wasSuccessful());
456         assertThat(result.getRunCount(), is(2));
457         assertNotNull(NotThreadSafeTest1.t);
458         assertNotNull(NotThreadSafeTest2.t);
459         assertSame(NotThreadSafeTest1.t, NotThreadSafeTest2.t);
460         assertThat(computer.getNotParallelRunners().size(), is(2));
461         assertTrue(computer.getSuites().isEmpty());
462         assertTrue(computer.getClasses().isEmpty());
463         assertTrue(computer.getNestedClasses().isEmpty());
464         assertTrue(computer.getNestedSuites().isEmpty());
465         assertFalse(computer.isSplitPool());
466         assertThat(computer.getPoolCapacity(), is(6));
467     }
468 
469     @Test
470     public void mixedThreadSafety() {
471         ParallelComputerBuilder builder = new ParallelComputerBuilder(LOGGER)
472                 .useOnePool(6)
473                 .optimize(true)
474                 .parallelClasses(3)
475                 .parallelMethods(3);
476         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) builder.buildComputer();
477         Result result = new JUnitCore().run(computer, NotThreadSafeTest1.class, NormalTest1.class);
478         assertTrue(result.wasSuccessful());
479         assertThat(result.getRunCount(), is(2));
480         assertNotNull(NotThreadSafeTest1.t);
481         assertNotNull(NormalTest1.t);
482         assertThat(NormalTest1.t.getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
483         assertNotSame(NotThreadSafeTest1.t, NormalTest1.t);
484         assertThat(computer.getNotParallelRunners().size(), is(1));
485         assertTrue(computer.getSuites().isEmpty());
486         assertThat(computer.getClasses().size(), is(1));
487         assertTrue(computer.getNestedClasses().isEmpty());
488         assertTrue(computer.getNestedSuites().isEmpty());
489         assertFalse(computer.isSplitPool());
490         assertThat(computer.getPoolCapacity(), is(6));
491     }
492 
493     @Test
494     public void notThreadSafeTestsInSuite() {
495         ParallelComputerBuilder builder =
496                 new ParallelComputerBuilder(LOGGER).useOnePool(5).parallelMethods(3);
497         assertFalse(builder.isOptimized());
498         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) builder.buildComputer();
499         Result result = new JUnitCore().run(computer, NotThreadSafeTestSuite.class);
500         assertTrue(result.wasSuccessful());
501         assertNotNull(NormalTest1.t);
502         assertNotNull(NormalTest2.t);
503         assertSame(NormalTest1.t, NormalTest2.t);
504         assertThat(NormalTest1.t.getName(), is("maven-surefire-plugin@NotThreadSafe"));
505         assertThat(NormalTest2.t.getName(), is("maven-surefire-plugin@NotThreadSafe"));
506         assertThat(computer.getNotParallelRunners().size(), is(1));
507         assertTrue(computer.getSuites().isEmpty());
508         assertTrue(computer.getClasses().isEmpty());
509         assertTrue(computer.getNestedClasses().isEmpty());
510         assertTrue(computer.getNestedSuites().isEmpty());
511         assertFalse(computer.isSplitPool());
512         assertThat(computer.getPoolCapacity(), is(5));
513     }
514 
515     @Test
516     public void mixedThreadSafetyInSuite() {
517         ParallelComputerBuilder builder = new ParallelComputerBuilder(LOGGER)
518                 .useOnePool(10)
519                 .optimize(true)
520                 .parallelSuites(2)
521                 .parallelClasses(3)
522                 .parallelMethods(3);
523         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) builder.buildComputer();
524         Result result = new JUnitCore().run(computer, MixedSuite.class);
525         assertTrue(result.wasSuccessful());
526         assertThat(result.getRunCount(), is(2));
527         assertNotNull(NotThreadSafeTest1.t);
528         assertNotNull(NormalTest1.t);
529         assertThat(NormalTest1.t.getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
530         assertNotSame(NotThreadSafeTest1.t, NormalTest1.t);
531         assertTrue(computer.getNotParallelRunners().isEmpty());
532         assertThat(computer.getSuites().size(), is(1));
533         assertTrue(computer.getClasses().isEmpty());
534         assertThat(computer.getNestedClasses().size(), is(1));
535         assertTrue(computer.getNestedSuites().isEmpty());
536         assertFalse(computer.isSplitPool());
537         assertThat(computer.getPoolCapacity(), is(10));
538     }
539 
540     @Test
541     public void inheritanceWithNotThreadSafe() {
542         ParallelComputerBuilder builder = new ParallelComputerBuilder(LOGGER)
543                 .useOnePool(10)
544                 .optimize(true)
545                 .parallelSuites(2)
546                 .parallelClasses(3)
547                 .parallelMethods(3);
548         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) builder.buildComputer();
549         Result result = new JUnitCore().run(computer, OverMixedSuite.class);
550         assertTrue(result.wasSuccessful());
551         assertThat(result.getRunCount(), is(2));
552         assertNotNull(NotThreadSafeTest3.t);
553         assertNotNull(NormalTest1.t);
554         assertThat(NormalTest1.t.getName(), is("maven-surefire-plugin@NotThreadSafe"));
555         assertSame(NotThreadSafeTest3.t, NormalTest1.t);
556         assertThat(computer.getNotParallelRunners().size(), is(1));
557         assertTrue(computer.getSuites().isEmpty());
558         assertTrue(computer.getClasses().isEmpty());
559         assertTrue(computer.getNestedClasses().isEmpty());
560         assertTrue(computer.getNestedSuites().isEmpty());
561         assertFalse(computer.isSplitPool());
562         assertThat(computer.getPoolCapacity(), is(10));
563     }
564 
565     @Test
566     public void beforeAfterThreadChanges() throws InterruptedException {
567         // try to GC dead Thread objects from previous tests
568         for (int i = 0; i < 5; i++) {
569             System.gc();
570             TimeUnit.MILLISECONDS.sleep(500L);
571         }
572         Collection<Thread> expectedThreads = jvmThreads();
573         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(LOGGER);
574         parallelComputerBuilder.parallelMethods(3);
575         ParallelComputer computer = parallelComputerBuilder.buildComputer();
576         Result result = new JUnitCore().run(computer, TestWithBeforeAfter.class);
577         assertTrue(result.wasSuccessful());
578         // try to GC dead Thread objects
579         for (int i = 0; i < 5 && expectedThreads.size() != jvmThreads().size(); i++) {
580             System.gc();
581             TimeUnit.MILLISECONDS.sleep(500L);
582         }
583         assertThat(jvmThreads(), is(expectedThreads));
584     }
585 
586     private static Collection<Thread> jvmThreads() {
587         Thread[] t = new Thread[1000];
588         Thread.enumerate(t);
589         ArrayList<Thread> appThreads = new ArrayList<>(t.length);
590         Collections.addAll(appThreads, t);
591         appThreads.removeAll(Collections.singleton((Thread) null));
592         Collections.sort(appThreads, new Comparator<Thread>() {
593             @Override
594             public int compare(Thread t1, Thread t2) {
595                 return (int) Math.signum(t1.getId() - t2.getId());
596             }
597         });
598         return appThreads;
599     }
600 
601     private static class ShutdownTest {
602         Result run(final boolean useInterrupt) {
603             ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(LOGGER)
604                     .useOnePool(8)
605                     .parallelSuites(2)
606                     .parallelClasses(3)
607                     .parallelMethods(3);
608 
609             assertFalse(parallelComputerBuilder.isOptimized());
610 
611             final ParallelComputerBuilder.PC computer =
612                     (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
613             shutdownTask = new Runnable() {
614                 @Override
615                 public void run() {
616                     Collection<Description> startedTests =
617                             computer.describeStopped(useInterrupt).getTriggeredTests();
618                     assertThat(startedTests.size(), is(not(0)));
619                 }
620             };
621             return new JUnitCore().run(computer, TestSuite.class, Class2.class, Class3.class);
622         }
623     }
624 
625     /**
626      *
627      */
628     public static class Class1 {
629         static volatile int concurrentMethods = 0;
630 
631         static volatile int maxConcurrentMethods = 0;
632 
633         @Test
634         public void test1() throws InterruptedException {
635             synchronized (CLASS1BLOCK) {
636                 ++concurrentMethods;
637                 CLASS1BLOCK.wait(DELAY_MULTIPLIER * 500L);
638                 maxConcurrentMethods = Math.max(maxConcurrentMethods, concurrentMethods--);
639             }
640         }
641 
642         @Test
643         public void test2() throws InterruptedException {
644             test1();
645             Runnable shutdownTask = ParallelComputerBuilderTest.shutdownTask;
646             if (shutdownTask != null) {
647                 beforeShutdown = true;
648                 shutdownTask.run();
649             }
650         }
651     }
652 
653     /**
654      *
655      */
656     public static class Class2 extends Class1 {}
657 
658     /**
659      *
660      */
661     public static class Class3 extends Class1 {}
662 
663     /**
664      *
665      */
666     public static class NothingDoingTest1 {
667         private static final Collection<String> METHODS = new ConcurrentLinkedQueue<>();
668 
669         @BeforeClass
670         public static void init() {
671             METHODS.add("init");
672         }
673 
674         @AfterClass
675         public static void deinit() {
676             METHODS.add("deinit");
677         }
678 
679         @Test
680         public void a() throws InterruptedException {
681             Thread.sleep(5);
682             METHODS.add(getClass().getName() + "#a()");
683         }
684 
685         @Test
686         public void b() throws InterruptedException {
687             Thread.sleep(5);
688             METHODS.add(getClass().getName() + "#b()");
689         }
690     }
691 
692     /**
693      *
694      */
695     public static class NothingDoingTest2 extends NothingDoingTest1 {}
696 
697     /**
698      *
699      */
700     public static class NothingDoingTest3 extends NothingDoingTest1 {}
701 
702     /**
703      *
704      */
705     @RunWith(Suite.class)
706     @Suite.SuiteClasses({NothingDoingTest1.class, NothingDoingTest2.class})
707     public static class NothingDoingSuite {}
708 
709     /**
710      *
711      */
712     @RunWith(Suite.class)
713     @Suite.SuiteClasses({Class2.class, Class1.class})
714     public static class TestSuite {}
715 
716     /**
717      *
718      */
719     public static class Test2 {
720         @Test
721         public void test() {}
722     }
723 
724     /**
725      *
726      */
727     @RunWith(ReportOneTestAtRuntimeRunner.class)
728     public static class TestWithoutPrecalculatedChildren {}
729 
730     /**
731      *
732      */
733     public static class ReportOneTestAtRuntimeRunner extends ParentRunner {
734         private final Class<?> testClass;
735         private final Description suiteDescription;
736         private final Description myTestMethodDescr;
737 
738         @SuppressWarnings("unchecked")
739         public ReportOneTestAtRuntimeRunner(Class<?> testClass) throws InitializationError {
740             super(Object.class);
741             this.testClass = testClass;
742             suiteDescription = Description.createSuiteDescription(testClass);
743             myTestMethodDescr = Description.createTestDescription(testClass, "my_test");
744             //            suiteDescription.addChild(myTestMethodDescr); // let it be not known at start time
745         }
746 
747         protected List getChildren() {
748             throw new UnsupportedOperationException("workflow from ParentRunner not supported");
749         }
750 
751         protected Description describeChild(Object child) {
752             throw new UnsupportedOperationException("workflow from ParentRunner not supported");
753         }
754 
755         protected void runChild(Object child, RunNotifier notifier) {
756             throw new UnsupportedOperationException("workflow from ParentRunner not supported");
757         }
758 
759         public Description getDescription() {
760             return suiteDescription;
761         }
762 
763         public void run(RunNotifier notifier) {
764             notifier.fireTestStarted(myTestMethodDescr);
765             notifier.fireTestFinished(Description.createTestDescription(testClass, "my_test"));
766         }
767     }
768 
769     /**
770      *
771      */
772     @NotThreadSafe
773     public static class NotThreadSafeTest1 {
774         static volatile Thread t;
775 
776         @BeforeClass
777         public static void beforeSuite() {
778             assertThat(Thread.currentThread().getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
779         }
780 
781         @AfterClass
782         public static void afterSuite() {
783             assertThat(Thread.currentThread().getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
784         }
785 
786         @Test
787         public void test() {
788             t = Thread.currentThread();
789             assertThat(t.getName(), is("maven-surefire-plugin@NotThreadSafe"));
790         }
791     }
792 
793     /**
794      *
795      */
796     @NotThreadSafe
797     public static class NotThreadSafeTest2 {
798         static volatile Thread t;
799 
800         @BeforeClass
801         public static void beforeSuite() {
802             assertThat(Thread.currentThread().getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
803         }
804 
805         @AfterClass
806         public static void afterSuite() {
807             assertThat(Thread.currentThread().getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
808         }
809 
810         @Test
811         public void test() {
812             t = Thread.currentThread();
813             assertThat(t.getName(), is("maven-surefire-plugin@NotThreadSafe"));
814         }
815     }
816 
817     /**
818      *
819      */
820     @NotThreadSafe
821     public static class NotThreadSafeTest3 {
822         static volatile Thread t;
823 
824         @Test
825         public void test() {
826             t = Thread.currentThread();
827             assertThat(t.getName(), is("maven-surefire-plugin@NotThreadSafe"));
828         }
829     }
830 
831     /**
832      *
833      */
834     @RunWith(Suite.class)
835     @Suite.SuiteClasses({NormalTest1.class, NormalTest2.class})
836     @NotThreadSafe
837     public static class NotThreadSafeTestSuite {
838         @BeforeClass
839         public static void beforeSuite() {
840             assertThat(Thread.currentThread().getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
841         }
842 
843         @AfterClass
844         public static void afterSuite() {
845             assertThat(Thread.currentThread().getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
846         }
847     }
848 
849     /**
850      *
851      */
852     public static class NormalTest1 {
853         static volatile Thread t;
854 
855         @Test
856         public void test() {
857             t = Thread.currentThread();
858         }
859     }
860 
861     /**
862      *
863      */
864     public static class NormalTest2 {
865         static volatile Thread t;
866 
867         @Test
868         public void test() {
869             t = Thread.currentThread();
870         }
871     }
872 
873     /**
874      *
875      */
876     @RunWith(Suite.class)
877     @Suite.SuiteClasses({NotThreadSafeTest1.class, NormalTest1.class})
878     public static class MixedSuite {
879         @BeforeClass
880         public static void beforeSuite() {
881             assertThat(Thread.currentThread().getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
882         }
883 
884         @AfterClass
885         public static void afterSuite() {
886             assertThat(Thread.currentThread().getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
887         }
888     }
889 
890     /**
891      *
892      */
893     @RunWith(Suite.class)
894     @Suite.SuiteClasses({NotThreadSafeTest3.class, NormalTest1.class})
895     @NotThreadSafe
896     public static class OverMixedSuite {
897         @BeforeClass
898         public static void beforeSuite() {
899             assertThat(Thread.currentThread().getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
900         }
901 
902         @AfterClass
903         public static void afterSuite() {
904             assertThat(Thread.currentThread().getName(), is(not("maven-surefire-plugin@NotThreadSafe")));
905         }
906     }
907 
908     /**
909      *
910      */
911     public static class TestWithBeforeAfter {
912         @BeforeClass
913         public static void beforeClass() throws InterruptedException {
914             System.out.println(new Date() + " BEG: beforeClass");
915             sleepSeconds(1);
916             System.out.println(new Date() + " END: beforeClass");
917         }
918 
919         @Before
920         public void before() throws InterruptedException {
921             System.out.println(new Date() + " BEG: before");
922             sleepSeconds(1);
923             System.out.println(new Date() + " END: before");
924         }
925 
926         @Test
927         public void test() throws InterruptedException {
928             System.out.println(new Date() + " BEG: test");
929             sleepSeconds(1);
930             System.out.println(new Date() + " END: test");
931         }
932 
933         @After
934         public void after() throws InterruptedException {
935             System.out.println(new Date() + " BEG: after");
936             sleepSeconds(1);
937             System.out.println(new Date() + " END: after");
938         }
939 
940         @AfterClass
941         public static void afterClass() throws InterruptedException {
942             System.out.println(new Date() + " BEG: afterClass");
943             sleepSeconds(1);
944             System.out.println(new Date() + " END: afterClass");
945         }
946     }
947 
948     private static long systemMillis() {
949         return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
950     }
951 
952     private static void sleepSeconds(int seconds) throws InterruptedException {
953         TimeUnit.SECONDS.sleep(seconds);
954     }
955 }