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 org.junit.AfterClass;
23  import org.junit.Before;
24  import org.junit.BeforeClass;
25  import org.junit.Rule;
26  import org.junit.Test;
27  import org.junit.runner.JUnitCore;
28  import org.junit.runner.Result;
29  import org.junit.runner.RunWith;
30  import org.junit.runners.Suite;
31  
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.Collection;
35  import java.util.Iterator;
36  import java.util.concurrent.ConcurrentLinkedQueue;
37  
38  import static org.junit.Assert.assertFalse;
39  import static org.junit.Assert.assertThat;
40  import static org.junit.Assert.assertTrue;
41  import static org.junit.Assert.fail;
42  import static org.hamcrest.core.AnyOf.anyOf;
43  import static org.hamcrest.core.Is.is;
44  import static org.hamcrest.core.IsNot.not;
45  import static org.apache.maven.surefire.junitcore.pc.RangeMatcher.between;
46  
47  /**
48   * @author Tibor Digana (tibor17)
49   * @since 2.16
50   */
51  public class ParallelComputerBuilderTest {
52      @Rule
53      public final Stopwatch runtime = new Stopwatch();
54  
55      @Before
56      public void beforeTest() {
57          Class1.maxConcurrentMethods = 0;
58          Class1.concurrentMethods = 0;
59          shutdownTask = null;
60      }
61  
62      @Test
63      public void parallelMethodsReuseOneOrTwoThreads() {
64          ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder();
65          parallelComputerBuilder.useOnePool(4);
66  
67          // One thread because one suite: TestSuite, however the capacity is 5.
68          parallelComputerBuilder.parallelSuites(5);
69  
70          // Two threads because TestSuite has two classes, however the capacity is 5.
71          parallelComputerBuilder.parallelClasses(5);
72  
73          // One or two threads because one threads comes from '#useOnePool(4)'
74          // and next thread may be reused from finished class, however the capacity is 3.
75          parallelComputerBuilder.parallelMethods(3);
76  
77          ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
78          Result result = new JUnitCore().run(computer, TestSuite.class);
79          long timeSpent = runtime.stop();
80  
81          assertThat(computer.suites.size(), is(1));
82          assertThat(computer.classes.size(), is(0));
83          assertThat(computer.nestedClasses.size(), is(2));
84          assertThat(computer.nestedSuites.size(), is(0));
85          assertFalse(computer.splitPool);
86          assertThat(computer.poolCapacity, is(4));
87          assertTrue(result.wasSuccessful());
88          if (Class1.maxConcurrentMethods == 1) {
89              assertThat(timeSpent, between(1950, 2250));
90          } else if (Class1.maxConcurrentMethods == 2) {
91              assertThat(timeSpent, between(1450, 1750));
92          } else {
93              fail();
94          }
95      }
96  
97      @Test
98      public void suiteAndClassInOnePool() {
99          ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder();
100         parallelComputerBuilder.useOnePool(5);
101         parallelComputerBuilder.parallelSuites(5);
102         parallelComputerBuilder.parallelClasses(5);
103         parallelComputerBuilder.parallelMethods(3);
104 
105         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
106         Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class);
107         long timeSpent = runtime.stop();
108 
109         assertThat(computer.suites.size(), is(1));
110         assertThat(computer.classes.size(), is(1));
111         assertThat(computer.nestedClasses.size(), is(2));
112         assertThat(computer.nestedSuites.size(), is(0));
113         assertFalse(computer.splitPool);
114         assertThat(computer.poolCapacity, is(5));
115         assertTrue(result.wasSuccessful());
116         assertThat(Class1.maxConcurrentMethods, is(2));
117         assertThat(timeSpent, anyOf(between(1450, 1750), between(1950, 2250), between(2450, 2750)));
118     }
119 
120     @Test
121     public void onePoolWithUnlimitedParallelMethods() {
122         // see ParallelComputerBuilder Javadoc
123         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder();
124         parallelComputerBuilder.useOnePool(8);
125         parallelComputerBuilder.parallelSuites(2);
126         parallelComputerBuilder.parallelClasses(4);
127         parallelComputerBuilder.parallelMethods();
128 
129         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
130         Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class);
131         long timeSpent = runtime.stop();
132 
133         assertThat(computer.suites.size(), is(1));
134         assertThat(computer.classes.size(), is(1));
135         assertThat(computer.nestedClasses.size(), is(2));
136         assertThat(computer.nestedSuites.size(), is(0));
137         assertFalse(computer.splitPool);
138         assertThat(computer.poolCapacity, is(8));
139         assertTrue(result.wasSuccessful());
140         assertThat(Class1.maxConcurrentMethods, is(4));
141         assertThat(timeSpent, between(950, 1250));
142     }
143 
144     @Test
145     public void underflowParallelism()
146     {
147         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder();
148         parallelComputerBuilder.useOnePool( 3 );
149 
150         // One thread because one suite: TestSuite.
151         parallelComputerBuilder.parallelSuites( 5 );
152 
153         // One thread because of the limitation which is bottleneck.
154         parallelComputerBuilder.parallelClasses( 1 );
155 
156         // One thread remains from '#useOnePool(3)'.
157         parallelComputerBuilder.parallelMethods( 3 );
158 
159         ParallelComputerBuilder.PC computer = ( ParallelComputerBuilder.PC ) parallelComputerBuilder.buildComputer();
160         Result result = new JUnitCore().run( computer, TestSuite.class );
161         long timeSpent = runtime.stop();
162 
163         assertThat( computer.suites.size(), is( 1 ) );
164         assertThat( computer.classes.size(), is( 0 ) );
165         assertThat( computer.nestedClasses.size(), is( 2 ) );
166         assertThat( computer.nestedSuites.size(), is( 0 ) );
167         assertFalse( computer.splitPool );
168         assertThat( computer.poolCapacity, is( 3 ) );
169         assertTrue( result.wasSuccessful() );
170         assertThat( Class1.maxConcurrentMethods, is( 1 ) );
171         assertThat( timeSpent, between( 1950, 2250 ) );
172     }
173 
174     @Test
175     public void separatePoolsWithSuite() {
176         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder();
177         parallelComputerBuilder.parallelSuites(5);
178         parallelComputerBuilder.parallelClasses(5);
179         parallelComputerBuilder.parallelMethods(3);
180 
181         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
182         Result result = new JUnitCore().run(computer, TestSuite.class);
183         long timeSpent = runtime.stop();
184 
185         assertThat(computer.suites.size(), is(1));
186         assertThat(computer.classes.size(), is(0));
187         assertThat(computer.nestedClasses.size(), is(2));
188         assertThat(computer.nestedSuites.size(), is(0));
189         assertTrue(computer.splitPool);
190         assertThat(computer.poolCapacity, is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED));
191         assertTrue(result.wasSuccessful());
192         assertThat(Class1.maxConcurrentMethods, is(3));
193         assertThat(timeSpent, between(950, 1250));
194     }
195 
196     @Test
197     public void separatePoolsWithSuiteAndClass() {
198         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder();
199         parallelComputerBuilder.parallelSuites(5);
200         parallelComputerBuilder.parallelClasses(5);
201         parallelComputerBuilder.parallelMethods(3);
202 
203         // 6 methods altogether.
204         // 2 groups with 3 threads.
205         // Each group takes 0.5s.
206         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
207         Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class);
208         long timeSpent = runtime.stop();
209 
210         assertThat(computer.suites.size(), is(1));
211         assertThat(computer.classes.size(), is(1));
212         assertThat(computer.nestedClasses.size(), is(2));
213         assertThat(computer.nestedSuites.size(), is(0));
214         assertTrue(computer.splitPool);
215         assertThat(computer.poolCapacity, is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED));
216         assertTrue(result.wasSuccessful());
217         assertThat(Class1.maxConcurrentMethods, is(3));
218         assertThat(timeSpent, between(950, 1250));
219     }
220 
221     @Test
222     public void separatePoolsWithSuiteAndSequentialClasses() {
223         ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder();
224         parallelComputerBuilder.parallelSuites(5);
225         parallelComputerBuilder.parallelClasses(1);
226         parallelComputerBuilder.parallelMethods(3);
227 
228         ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
229         Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class);
230         long timeSpent = runtime.stop();
231 
232         assertThat(computer.suites.size(), is(1));
233         assertThat(computer.classes.size(), is(1));
234         assertThat(computer.nestedClasses.size(), is(2));
235         assertThat(computer.nestedSuites.size(), is(0));
236         assertTrue(computer.splitPool);
237         assertThat(computer.poolCapacity, is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED));
238         assertTrue(result.wasSuccessful());
239         assertThat(Class1.maxConcurrentMethods, is(2));
240         assertThat(timeSpent, between(1450, 1750));
241     }
242 
243     private static class ShutdownTest {
244         Result run(final boolean useInterrupt) {
245             ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder().useOnePool(8);
246             parallelComputerBuilder.parallelSuites(2);
247             parallelComputerBuilder.parallelClasses(3);
248             parallelComputerBuilder.parallelMethods(3);
249 
250             final ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer();
251             shutdownTask = new Runnable() {
252                 public void run() {
253                     Collection<org.junit.runner.Description> startedTests = computer.shutdown(useInterrupt);
254                     assertThat(startedTests.size(), is(not(0)));
255                 }
256             };
257             return new JUnitCore().run(computer, TestSuite.class, Class2.class, Class3.class);
258         }
259     }
260 
261     @Test(timeout = 2000)
262     public void shutdown() {
263         Result result = new ShutdownTest().run(false);
264         long timeSpent = runtime.stop();
265         assertTrue(result.wasSuccessful());
266         assertTrue(beforeShutdown);
267         assertThat(timeSpent, between(450, 1250));
268     }
269 
270     @Test(timeout = 2000)
271     public void shutdownWithInterrupt() {
272         new ShutdownTest().run(true);
273         long timeSpent = runtime.stop();
274         assertTrue(beforeShutdown);
275         assertThat(timeSpent, between(450, 1250));
276     }
277 
278     @Test
279     public void nothingParallel() {
280         JUnitCore core = new JUnitCore();
281         ParallelComputerBuilder builder = new ParallelComputerBuilder();
282 
283         Result result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingTest2.class);
284         assertTrue(result.wasSuccessful());
285 
286         result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class);
287         assertTrue(result.wasSuccessful());
288 
289         result = core.run(builder.useOnePool(1).buildComputer(), NothingDoingTest1.class, NothingDoingTest2.class);
290         assertTrue(result.wasSuccessful());
291 
292         result = core.run(builder.useOnePool(1).buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class);
293         assertTrue(result.wasSuccessful());
294 
295         result = core.run(builder.useOnePool(2).buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class);
296         assertTrue(result.wasSuccessful());
297 
298         Class<?>[] classes = {NothingDoingTest1.class, NothingDoingSuite.class};
299 
300         result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses(1).buildComputer(), classes);
301         assertTrue(result.wasSuccessful());
302 
303         result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses().buildComputer(), classes);
304         assertTrue(result.wasSuccessful());
305 
306         classes = new Class<?>[]{NothingDoingSuite.class, NothingDoingSuite.class,
307                 NothingDoingTest1.class, NothingDoingTest2.class, NothingDoingTest3.class};
308 
309         result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses(1).buildComputer(), classes);
310         assertTrue(result.wasSuccessful());
311 
312         result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses().buildComputer(), classes);
313         assertTrue(result.wasSuccessful());
314     }
315 
316     private static void testKeepBeforeAfter(ParallelComputerBuilder builder, Class<?>... classes) {
317         JUnitCore core = new JUnitCore();
318         for (int round = 0; round < 5; round++) {
319             NothingDoingTest1.methods.clear();
320             Result result = core.run(builder.buildComputer(), classes);
321             assertTrue(result.wasSuccessful());
322             Iterator<String> methods = NothingDoingTest1.methods.iterator();
323             for (Class<?> clazz : classes) {
324                 String a = clazz.getName() + "#a()";
325                 String b = clazz.getName() + "#b()";
326                 assertThat(methods.next(), is("init"));
327                 assertThat(methods.next(), anyOf(is(a), is(b)));
328                 assertThat(methods.next(), anyOf(is(a), is(b)));
329                 assertThat(methods.next(), is("deinit"));
330             }
331         }
332     }
333 
334     @Test
335     public void keepBeforeAfterOneClass() {
336         ParallelComputerBuilder builder = new ParallelComputerBuilder();
337         builder.parallelMethods();
338         testKeepBeforeAfter(builder, NothingDoingTest1.class);
339     }
340 
341     @Test
342     public void keepBeforeAfterTwoClasses() {
343         ParallelComputerBuilder builder = new ParallelComputerBuilder();
344         builder.useOnePool(5).parallelClasses(1).parallelMethods(2);
345         testKeepBeforeAfter(builder, NothingDoingTest1.class, NothingDoingTest2.class);
346     }
347 
348     @Test
349     public void keepBeforeAfterTwoParallelClasses() {
350         ParallelComputerBuilder builder = new ParallelComputerBuilder();
351         builder.useOnePool(8).parallelClasses(2).parallelMethods(2);
352         JUnitCore core = new JUnitCore();
353         NothingDoingTest1.methods.clear();
354         Class<?>[] classes = {NothingDoingTest1.class, NothingDoingTest2.class, NothingDoingTest3.class};
355         Result result = core.run(builder.buildComputer(), classes);
356         assertTrue(result.wasSuccessful());
357         ArrayList<String> methods = new ArrayList<String>(NothingDoingTest1.methods);
358         assertThat(methods.size(), is(12));
359         assertThat(methods.subList(9, 12), is(not(Arrays.asList("deinit", "deinit", "deinit"))));
360     }
361 
362     private static volatile boolean beforeShutdown;
363     private static volatile Runnable shutdownTask;
364 
365     public static class Class1 {
366         static volatile int concurrentMethods = 0;
367         static volatile int maxConcurrentMethods = 0;
368 
369         @Test
370         public void test1() throws InterruptedException {
371             synchronized (Class1.class) {
372                 ++concurrentMethods;
373                 Class1.class.wait(500);
374                 maxConcurrentMethods = Math.max(maxConcurrentMethods, concurrentMethods--);
375             }
376         }
377 
378         @Test
379         public void test2() throws InterruptedException {
380             test1();
381             Runnable shutdownTask = ParallelComputerBuilderTest.shutdownTask;
382             if (shutdownTask != null) {
383                 beforeShutdown = true;
384                 shutdownTask.run();
385             }
386         }
387     }
388 
389     public static class Class2 extends Class1 {
390     }
391 
392     public static class Class3 extends Class1 {
393     }
394 
395     @RunWith(Suite.class)
396     @Suite.SuiteClasses({Class2.class, Class1.class})
397     public class TestSuite {
398     }
399 
400     public static class NothingDoingTest1 {
401         static final Collection<String> methods = new ConcurrentLinkedQueue<String>();
402 
403         @BeforeClass
404         public static void init() {
405             methods.add("init");
406         }
407 
408         @Test
409         public void a() throws InterruptedException {
410             Thread.sleep(5);
411             methods.add(getClass().getName() + "#a()");
412         }
413 
414         @Test
415         public void b() throws InterruptedException {
416             Thread.sleep(5);
417             methods.add(getClass().getName() + "#b()");
418         }
419 
420         @AfterClass
421         public static void deinit() {
422             methods.add("deinit");
423         }
424     }
425 
426     public static class NothingDoingTest2 extends NothingDoingTest1 {
427     }
428 
429     public static class NothingDoingTest3 extends NothingDoingTest1 {
430     }
431 
432     @RunWith(Suite.class)
433     @Suite.SuiteClasses({NothingDoingTest1.class, NothingDoingTest2.class})
434     public static class NothingDoingSuite {
435     }
436 }