View Javadoc

1   package org.apache.maven.surefire.junit4;
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.lang.reflect.Method;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import org.apache.maven.shared.utils.io.SelectorUtils;
27  import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
28  import org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory;
29  import org.apache.maven.surefire.common.junit4.JUnit4TestChecker;
30  import org.apache.maven.surefire.providerapi.AbstractProvider;
31  import org.apache.maven.surefire.providerapi.ProviderParameters;
32  import org.apache.maven.surefire.report.ConsoleOutputCapture;
33  import org.apache.maven.surefire.report.ConsoleOutputReceiver;
34  import org.apache.maven.surefire.report.PojoStackTraceWriter;
35  import org.apache.maven.surefire.report.ReportEntry;
36  import org.apache.maven.surefire.report.ReporterException;
37  import org.apache.maven.surefire.report.ReporterFactory;
38  import org.apache.maven.surefire.report.RunListener;
39  import org.apache.maven.surefire.report.SimpleReportEntry;
40  import org.apache.maven.surefire.suite.RunResult;
41  import org.apache.maven.surefire.testset.TestSetFailedException;
42  import org.apache.maven.surefire.util.RunOrderCalculator;
43  import org.apache.maven.surefire.util.ScanResult;
44  import org.apache.maven.surefire.util.TestsToRun;
45  import org.apache.maven.surefire.util.internal.StringUtils;
46  
47  import org.junit.runner.Request;
48  import org.junit.runner.Result;
49  import org.junit.runner.Runner;
50  import org.junit.runner.notification.RunNotifier;
51  
52  /**
53   * @author Kristian Rosenvold
54   */
55  public class JUnit4Provider
56      extends AbstractProvider
57  {
58      private final ClassLoader testClassLoader;
59  
60      private final List<org.junit.runner.notification.RunListener> customRunListeners;
61  
62      private final JUnit4TestChecker jUnit4TestChecker;
63  
64      private final String requestedTestMethod;
65  
66      private TestsToRun testsToRun;
67  
68      private final ProviderParameters providerParameters;
69  
70      private final RunOrderCalculator runOrderCalculator;
71  
72      private final ScanResult scanResult;
73  
74  
75      public JUnit4Provider( ProviderParameters booterParameters )
76      {
77          this.providerParameters = booterParameters;
78          this.testClassLoader = booterParameters.getTestClassLoader();
79          this.scanResult = booterParameters.getScanResult();
80          this.runOrderCalculator = booterParameters.getRunOrderCalculator();
81          customRunListeners = JUnit4RunListenerFactory.
82              createCustomListeners( booterParameters.getProviderProperties().getProperty( "listener" ) );
83          jUnit4TestChecker = new JUnit4TestChecker( testClassLoader );
84          requestedTestMethod = booterParameters.getTestRequest().getRequestedTestMethod();
85  
86      }
87  
88      public RunResult invoke( Object forkTestSet )
89          throws TestSetFailedException, ReporterException
90      {
91          if ( testsToRun == null )
92          {
93              if ( forkTestSet instanceof TestsToRun )
94              {
95                  testsToRun = (TestsToRun) forkTestSet;
96              }
97              else if ( forkTestSet instanceof Class )
98              {
99                  testsToRun = TestsToRun.fromClass( (Class) forkTestSet );
100             }
101             else
102             {
103                 testsToRun = scanClassPath();
104             }
105         }
106 
107         upgradeCheck();
108 
109         final ReporterFactory reporterFactory = providerParameters.getReporterFactory();
110 
111         final RunListener reporter = reporterFactory.createReporter();
112 
113         ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) reporter );
114 
115         JUnit4RunListener jUnit4TestSetReporter = new JUnit4RunListener( reporter );
116 
117         Result result = new Result();
118         RunNotifier runNotifer = getRunNotifer( jUnit4TestSetReporter, result, customRunListeners );
119 
120         runNotifer.fireTestRunStarted( null );
121 
122         for ( Class aTestsToRun : testsToRun )
123         {
124             executeTestSet( aTestsToRun, reporter, runNotifer );
125         }
126 
127         runNotifer.fireTestRunFinished( result );
128 
129         JUnit4RunListener.rethrowAnyTestMechanismFailures( result );
130 
131         closeRunNotifer( jUnit4TestSetReporter, customRunListeners );
132 
133         return reporterFactory.close();
134     }
135 
136     private void executeTestSet( Class<?> clazz, RunListener reporter, RunNotifier listeners )
137         throws ReporterException, TestSetFailedException
138     {
139         final ReportEntry report = new SimpleReportEntry( this.getClass().getName(), clazz.getName() );
140 
141         reporter.testSetStarting( report );
142 
143         try
144         {
145             if ( !StringUtils.isBlank( this.requestedTestMethod ) )
146             {
147                 String actualTestMethod = getMethod( clazz, this.requestedTestMethod );//add by rainLee
148                 String[] testMethods = StringUtils.split( actualTestMethod, "+" );
149                 execute( clazz, listeners, testMethods );
150             }
151             else
152             {//the original way
153                 execute( clazz, listeners, null );
154             }
155         }
156         catch ( TestSetFailedException e )
157         {
158             throw e;
159         }
160         catch ( Throwable e )
161         {
162             reporter.testError( SimpleReportEntry.withException( report.getSourceName(), report.getName(),
163                                                                  new PojoStackTraceWriter( report.getSourceName(),
164                                                                                            report.getName(), e ) ) );
165         }
166         finally
167         {
168             reporter.testSetCompleted( report );
169         }
170     }
171 
172     private RunNotifier getRunNotifer( org.junit.runner.notification.RunListener main, Result result,
173                                        List<org.junit.runner.notification.RunListener> others )
174     {
175         RunNotifier fNotifier = new RunNotifier();
176         fNotifier.addListener( main );
177         fNotifier.addListener( result.createListener() );
178         for ( org.junit.runner.notification.RunListener listener : others )
179         {
180             fNotifier.addListener( listener );
181         }
182         return fNotifier;
183     }
184 
185     // I am not entierly sure as to why we do this explicit freeing, it's one of those
186     // pieces of code that just seem to linger on in here ;)
187     private void closeRunNotifer( org.junit.runner.notification.RunListener main,
188                                   List<org.junit.runner.notification.RunListener> others )
189     {
190         RunNotifier fNotifier = new RunNotifier();
191         fNotifier.removeListener( main );
192         for ( org.junit.runner.notification.RunListener listener : others )
193         {
194             fNotifier.removeListener( listener );
195         }
196     }
197 
198     public Iterator<?> getSuites()
199     {
200         testsToRun = scanClassPath();
201         return testsToRun.iterator();
202     }
203 
204     private TestsToRun scanClassPath()
205     {
206         final TestsToRun scannedClasses = scanResult.applyFilter( jUnit4TestChecker, testClassLoader );
207         return runOrderCalculator.orderTestClasses( scannedClasses );
208     }
209 
210     @SuppressWarnings("unchecked")
211     private void upgradeCheck()
212         throws TestSetFailedException
213     {
214         if ( isJunit4UpgradeCheck() )
215         {
216             List<Class> classesSkippedByValidation =
217                 scanResult.getClassesSkippedByValidation( jUnit4TestChecker, testClassLoader );
218             if ( !classesSkippedByValidation.isEmpty() )
219             {
220                 StringBuilder reason = new StringBuilder();
221                 reason.append( "Updated check failed\n" );
222                 reason.append( "There are tests that would be run with junit4 / surefire 2.6 but not with [2.7,):\n" );
223                 for ( Class testClass : classesSkippedByValidation )
224                 {
225                     reason.append( "   " );
226                     reason.append( testClass.getName() );
227                     reason.append( "\n" );
228                 }
229                 throw new TestSetFailedException( reason.toString() );
230             }
231         }
232     }
233 
234     private boolean isJunit4UpgradeCheck()
235     {
236         final String property = System.getProperty( "surefire.junit4.upgradecheck" );
237         return property != null;
238     }
239 
240 
241     private static void execute( Class<?> testClass, RunNotifier fNotifier, String[] testMethods )
242         throws TestSetFailedException
243     {
244         if ( null != testMethods )
245         {
246             Method[] methods = testClass.getMethods();
247             for ( Method method : methods )
248             {
249                 for ( String testMethod : testMethods )
250                 {
251                     if ( SelectorUtils.match( testMethod, method.getName() ) )
252                     {
253                         Runner junitTestRunner = Request.method( testClass, method.getName() ).getRunner();
254                         junitTestRunner.run( fNotifier );
255                     }
256 
257                 }
258             }
259             return;
260         }
261 
262         Runner junitTestRunner = Request.aClass( testClass ).getRunner();
263 
264         junitTestRunner.run( fNotifier );
265     }
266 
267     /**
268      * this method retrive  testMethods from String like "com.xx.ImmutablePairTest#testBasic,com.xx.StopWatchTest#testLang315+testStopWatchSimpleGet"
269      * <br>
270      * and we need to think about cases that 2 or more method in 1 class. we should choose the correct method
271      *
272      * @param testClass     the testclass
273      * @param testMethodStr the test method string
274      * @return a string ;)
275      */
276     private static String getMethod( Class testClass, String testMethodStr )
277     {
278         String className = testClass.getName();
279 
280         if ( !testMethodStr.contains( "#" ) && !testMethodStr.contains( "," ) )
281         {//the original way
282             return testMethodStr;
283         }
284         testMethodStr += ",";//for the bellow  split code
285         int beginIndex = testMethodStr.indexOf( className );
286         int endIndex = testMethodStr.indexOf( ",", beginIndex );
287         String classMethodStr =
288             testMethodStr.substring( beginIndex, endIndex );//String like "StopWatchTest#testLang315"
289 
290         int index = classMethodStr.indexOf( '#' );
291         if ( index >= 0 )
292         {
293             return classMethodStr.substring( index + 1, classMethodStr.length() );
294         }
295         return null;
296     }
297 }