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.Collection;
22  import java.util.Collections;
23  import java.util.Iterator;
24  import java.util.Queue;
25  
26  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
27  import org.apache.maven.surefire.api.testset.TestSetFailedException;
28  import org.apache.maven.surefire.api.util.TestsToRun;
29  import org.apache.maven.surefire.common.junit4.Notifier;
30  import org.apache.maven.surefire.junitcore.pc.ParallelComputer;
31  import org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder;
32  import org.junit.Ignore;
33  import org.junit.runner.Computer;
34  import org.junit.runner.Description;
35  import org.junit.runner.Request;
36  import org.junit.runner.Result;
37  import org.junit.runner.manipulation.Filter;
38  import org.junit.runner.notification.RunListener;
39  import org.junit.runner.notification.StoppedByUserException;
40  
41  import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createDescription;
42  import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createIgnored;
43  import static org.apache.maven.surefire.common.junit4.JUnit4RunListener.rethrowAnyTestMechanismFailures;
44  import static org.junit.runner.Computer.serial;
45  import static org.junit.runner.Request.classes;
46  
47  /**
48   * Encapsulates access to JUnitCore
49   *
50   * @author Kristian Rosenvold
51   */
52  final class JUnitCoreWrapper {
53      private final Notifier notifier;
54      private final JUnitCoreParameters jUnitCoreParameters;
55      private final ConsoleLogger consoleStream;
56  
57      JUnitCoreWrapper(Notifier notifier, JUnitCoreParameters jUnitCoreParameters, ConsoleLogger consoleStream) {
58          this.notifier = notifier;
59          this.jUnitCoreParameters = jUnitCoreParameters;
60          this.consoleStream = consoleStream;
61      }
62  
63      void execute(TestsToRun testsToRun, Filter filter) throws TestSetFailedException {
64          execute(testsToRun, true, Collections.<RunListener>emptyList(), filter);
65      }
66  
67      void execute(TestsToRun testsToRun, Collection<RunListener> listeners, Filter filter)
68              throws TestSetFailedException {
69          execute(testsToRun, false, listeners, filter);
70      }
71  
72      private void execute(TestsToRun testsToRun, boolean useIterated, Collection<RunListener> listeners, Filter filter)
73              throws TestSetFailedException {
74          if (testsToRun.allowEagerReading()) {
75              executeEager(testsToRun, filter, listeners);
76          } else {
77              executeLazy(testsToRun, useIterated, filter, listeners);
78          }
79      }
80  
81      private JUnitCore createJUnitCore(Notifier notifier, Collection<RunListener> listeners) {
82          JUnitCore junitCore = new JUnitCore();
83  
84          // custom listeners added last
85          notifier.addListeners(listeners);
86  
87          return junitCore;
88      }
89  
90      private void executeEager(TestsToRun testsToRun, Filter filter, Collection<RunListener> listeners)
91              throws TestSetFailedException {
92          JUnitCore junitCore = createJUnitCore(notifier, listeners);
93          Class<?>[] tests = testsToRun.getLocatedClasses();
94          Computer computer = createComputer();
95          createRequestAndRun(filter, computer, junitCore.withReportedTests(tests), tests);
96      }
97  
98      private void executeLazy(
99              TestsToRun testsToRun, boolean useIterated, Filter filter, Collection<RunListener> listeners)
100             throws TestSetFailedException {
101         JUnitCore junitCore = createJUnitCore(notifier, listeners);
102         for (Iterator<Class<?>> it = useIterated ? testsToRun.iterated() : testsToRun.iterator(); it.hasNext(); ) {
103             Class<?> clazz = it.next();
104             Computer computer = createComputer();
105             createRequestAndRun(filter, computer, junitCore.withReportedTests(clazz), clazz);
106         }
107     }
108 
109     private void createRequestAndRun(Filter filter, Computer computer, JUnitCore junitCore, Class<?>... classesToRun)
110             throws TestSetFailedException {
111         Request req = classes(computer, classesToRun);
112         if (filter != null) {
113             req = new FilteringRequest(req, filter);
114             if (req.getRunner() == null) {
115                 // nothing to run
116                 return;
117             }
118         }
119 
120         Result run = junitCore.run(req.getRunner());
121         rethrowAnyTestMechanismFailures(run);
122 
123         if (computer instanceof ParallelComputer) {
124             String timeoutMessage = ((ParallelComputer) computer).describeElapsedTimeout();
125             if (!timeoutMessage.isEmpty()) {
126                 throw new TestSetFailedException(timeoutMessage);
127             }
128         }
129     }
130 
131     private Computer createComputer() {
132         return jUnitCoreParameters.isNoThreading()
133                 ? serial()
134                 : new ParallelComputerBuilder(consoleStream, jUnitCoreParameters).buildComputer();
135     }
136 
137     private final class JUnitCore extends org.apache.maven.surefire.junitcore.JUnitCore {
138         JUnitCore() {
139             super(JUnitCoreWrapper.this.notifier);
140         }
141 
142         JUnitCore withReportedTests(Class<?>... tests) {
143             Queue<String> stoppedTests = JUnitCoreWrapper.this.notifier.getRemainingTestClasses();
144             if (stoppedTests != null) {
145                 for (Class<?> test : tests) {
146                     stoppedTests.add(test.getName());
147                 }
148             }
149             return this;
150         }
151 
152         @Override
153         @SuppressWarnings("checkstyle:innerassignment")
154         protected void afterException(Throwable e) throws TestSetFailedException {
155             if (JUnitCoreWrapper.this.notifier.isFailFast() && e instanceof StoppedByUserException) {
156                 Queue<String> stoppedTests = JUnitCoreWrapper.this.notifier.getRemainingTestClasses();
157                 if (stoppedTests != null) {
158                     String reason = e.getClass().getName();
159                     Ignore reasonForSkippedTest = createIgnored(reason);
160                     for (String clazz; (clazz = stoppedTests.poll()) != null; ) {
161                         Description skippedTest = createDescription(clazz, reasonForSkippedTest);
162                         JUnitCoreWrapper.this.notifier.fireTestIgnored(skippedTest);
163                     }
164                 }
165             } else {
166                 super.afterException(e);
167             }
168         }
169 
170         @Override
171         protected void afterFinished() {
172             Queue<String> stoppedTests = JUnitCoreWrapper.this.notifier.getRemainingTestClasses();
173             if (stoppedTests != null) {
174                 stoppedTests.clear();
175             }
176         }
177     }
178 }