View Javadoc

1   package org.apache.maven.surefire.junitcore;
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 java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.concurrent.ExecutionException;
26  import java.util.concurrent.ExecutorService;
27  import java.util.concurrent.Executors;
28  import org.apache.maven.surefire.util.NestedRuntimeException;
29  
30  import org.junit.runner.Computer;
31  import org.junit.runner.Runner;
32  import org.junit.runners.ParentRunner;
33  import org.junit.runners.Suite;
34  import org.junit.runners.model.InitializationError;
35  import org.junit.runners.model.RunnerBuilder;
36  import org.junit.runners.model.RunnerScheduler;
37  
38  /*
39   * @author Kristian Rosenvold
40   */
41  public class ConfigurableParallelComputer
42      extends Computer
43  {
44      private final boolean fClasses;
45  
46      private final boolean fMethods;
47  
48      private final boolean fixedPool;
49  
50      private final ExecutorService fService;
51  
52      private final List<AsynchronousRunner> nonBlockers =
53          Collections.synchronizedList( new ArrayList<AsynchronousRunner>() );
54  
55  
56      public ConfigurableParallelComputer()
57      {
58          this( true, true, Executors.newCachedThreadPool(), false );
59      }
60  
61      public ConfigurableParallelComputer( boolean fClasses, boolean fMethods )
62      {
63          this( fClasses, fMethods, Executors.newCachedThreadPool(), false );
64      }
65  
66      public ConfigurableParallelComputer( boolean fClasses, boolean fMethods, Integer numberOfThreads, boolean perCore )
67      {
68          this( fClasses, fMethods, Executors.newFixedThreadPool(
69              numberOfThreads * ( perCore ? Runtime.getRuntime().availableProcessors() : 1 ) ), true );
70      }
71  
72      private ConfigurableParallelComputer( boolean fClasses, boolean fMethods, ExecutorService executorService,
73                                            boolean fixedPool )
74      {
75          this.fClasses = fClasses;
76          this.fMethods = fMethods;
77          fService = executorService;
78          this.fixedPool = fixedPool;
79      }
80  
81      @SuppressWarnings( { "UnusedDeclaration" } )
82      public void close()
83          throws ExecutionException
84      {
85          for ( AsynchronousRunner nonBlocker : nonBlockers )
86          {
87              nonBlocker.waitForCompletion();
88          }
89  
90          fService.shutdown();
91          try
92          {
93              fService.awaitTermination( 10, java.util.concurrent.TimeUnit.SECONDS );
94          }
95          catch ( InterruptedException e )
96          {
97              throw new NestedRuntimeException( e );
98          }
99      }
100 
101     private Runner parallelize( Runner runner, RunnerScheduler runnerInterceptor )
102     {
103         if ( runner instanceof ParentRunner<?> )
104         {
105             ( (ParentRunner<?>) runner ).setScheduler( runnerInterceptor );
106         }
107         return runner;
108     }
109 
110     private RunnerScheduler getMethodInterceptor()
111     {
112         if ( fClasses && fMethods )
113         {
114             final AsynchronousRunner blockingAsynchronousRunner = new AsynchronousRunner( fService );
115             nonBlockers.add( blockingAsynchronousRunner );
116             return blockingAsynchronousRunner;
117         }
118         return fMethods ? new AsynchronousRunner( fService ) : new SynchronousRunner();
119     }
120 
121     private RunnerScheduler getClassInterceptor()
122     {
123         if ( fClasses )
124         {
125             return fMethods ? new SynchronousRunner() : new AsynchronousRunner( fService );
126         }
127         return new SynchronousRunner();
128     }
129 
130     @Override
131     public Runner getSuite( RunnerBuilder builder, java.lang.Class<?>[] classes )
132         throws InitializationError
133     {
134         Runner suite = super.getSuite( builder, classes );
135         return fClasses ? parallelize( suite, getClassInterceptor() ) : suite;
136     }
137 
138     @Override
139     protected Runner getRunner( RunnerBuilder builder, Class<?> testClass )
140         throws Throwable
141     {
142         Runner runner = super.getRunner( builder, testClass );
143         return fMethods && !isTestSuite( testClass ) ? parallelize( runner, getMethodInterceptor() ) : runner;
144     }
145 
146     private boolean isTestSuite( Class<?> testClass )
147     {
148         // Todo: Find out how/if this is enough
149         final Suite.SuiteClasses annotation = testClass.getAnnotation( Suite.SuiteClasses.class );
150         return ( annotation != null );
151     }
152 
153     @Override
154     public String toString()
155     {
156         return "ConfigurableParallelComputer{" + "classes=" + fClasses + ", methods=" + fMethods + ", fixedPool="
157             + fixedPool + '}';
158     }
159 
160 }