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