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