View Javadoc
1   package org.apache.maven.surefire.testng;
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.io.File;
23  import java.lang.annotation.Annotation;
24  import java.lang.reflect.Method;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Properties;
31  import java.util.SortedMap;
32  import java.util.TreeMap;
33  
34  import org.apache.maven.surefire.NonAbstractClassFilter;
35  import org.apache.maven.surefire.report.ConsoleOutputCapture;
36  import org.apache.maven.surefire.report.ConsoleOutputReceiver;
37  import org.apache.maven.surefire.report.ReportEntry;
38  import org.apache.maven.surefire.report.ReporterException;
39  import org.apache.maven.surefire.report.ReporterFactory;
40  import org.apache.maven.surefire.report.RunListener;
41  import org.apache.maven.surefire.report.SimpleReportEntry;
42  import org.apache.maven.surefire.testset.TestSetFailedException;
43  import org.apache.maven.surefire.util.RunOrderCalculator;
44  import org.apache.maven.surefire.util.ScanResult;
45  import org.apache.maven.surefire.util.TestsToRun;
46  
47  /**
48   * Test suite for TestNG based on a directory of Java test classes. Can also execute JUnit tests.
49   *
50   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
51   * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>
52   */
53  public class TestNGDirectoryTestSuite
54      implements TestNgTestSuite
55  {
56  
57      private final Map options;
58  
59      private final Map junitOptions;
60  
61      private final String testSourceDirectory;
62  
63      private final File reportsDirectory;
64  
65      private SortedMap<String, TestNGTestSet> testSets;
66  
67      private final ScanResult scanResult;
68  
69      private final String testMethodPattern;
70  
71      private final RunOrderCalculator runOrderCalculator;
72  
73      private final Class junitTestClass;
74  
75      private Class<? extends Annotation> junitRunWithAnnotation;
76  
77      private Class<? extends Annotation> junitTestAnnotation;
78  
79      public TestNGDirectoryTestSuite( String testSourceDirectory, Properties confOptions, File reportsDirectory,
80                                       String testMethodPattern, RunOrderCalculator runOrderCalculator,
81                                       ScanResult scanResult )
82      {
83  
84          this.runOrderCalculator = runOrderCalculator;
85  
86          this.options = confOptions;
87  
88          this.testSourceDirectory = testSourceDirectory;
89          this.reportsDirectory = reportsDirectory;
90          this.scanResult = scanResult;
91          this.testMethodPattern = testMethodPattern;
92          this.junitTestClass = findJUnitTestClass();
93          this.junitRunWithAnnotation = findJUnitRunWithAnnotation();
94          this.junitTestAnnotation = findJUnitTestAnnotation();
95          this.junitOptions = createJUnitOptions();
96      }
97  
98      public void execute( TestsToRun testsToRun, ReporterFactory reporterManagerFactory )
99          throws TestSetFailedException
100     {
101 
102         if ( !testsToRun.allowEagerReading() )
103         {
104             executeLazy( testsToRun, reporterManagerFactory );
105         }
106         else if ( testsToRun.containsAtLeast( 2 ) )
107         {
108             executeMulti( testsToRun, reporterManagerFactory );
109         }
110         else if ( testsToRun.containsAtLeast( 1 ) )
111         {
112             Class testClass = testsToRun.iterator().next();
113             executeSingleClass( reporterManagerFactory, testClass );
114         }
115     }
116 
117     private void executeSingleClass( ReporterFactory reporterManagerFactory, Class testClass )
118         throws TestSetFailedException
119     {
120         this.options.put( "suitename", testClass.getName() );
121 
122         RunListener reporter = reporterManagerFactory.createReporter();
123         ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) reporter );
124 
125         startTestSuite( reporter, this );
126 
127         final Map optionsToUse = isJUnitTest( testClass ) ? junitOptions : options;
128 
129         TestNGExecutor.run( new Class[]{ testClass }, testSourceDirectory, optionsToUse, reporter, this,
130                             reportsDirectory, testMethodPattern );
131 
132         finishTestSuite( reporter, this );
133     }
134 
135     public void executeLazy( TestsToRun testsToRun, ReporterFactory reporterFactory )
136         throws TestSetFailedException
137     {
138 
139         for ( Class c : testsToRun )
140         {
141             executeSingleClass( reporterFactory, c );
142         }
143     }
144 
145     private Class findJUnitTestClass()
146     {
147         return lookupClass( "junit.framework.Test" );
148     }
149 
150     private Class findJUnitRunWithAnnotation()
151     {
152         return lookupClass( "org.junit.runner.RunWith" );
153     }
154 
155     private Class findJUnitTestAnnotation()
156     {
157         return lookupClass( "org.junit.Test" );
158     }
159 
160     private Class lookupClass( String className )
161     {
162         Class junitClass;
163         try
164         {
165             junitClass = Class.forName( className );
166         }
167         catch ( ClassNotFoundException e )
168         {
169             junitClass = null;
170         }
171         return junitClass;
172     }
173 
174     public void executeMulti( TestsToRun testsToRun, ReporterFactory reporterFactory )
175         throws TestSetFailedException
176     {
177         List<Class> testNgTestClasses = new ArrayList<Class>();
178         List<Class> junitTestClasses = new ArrayList<Class>();
179         for ( Class c : testsToRun )
180         {
181             if ( isJUnitTest( c ) )
182             {
183                 junitTestClasses.add( c );
184             }
185             else
186             {
187                 testNgTestClasses.add( c );
188             }
189         }
190 
191         File testNgReportsDirectory = reportsDirectory, junitReportsDirectory = reportsDirectory;
192 
193         if ( junitTestClasses.size() > 0 && testNgTestClasses.size() > 0 )
194         {
195             testNgReportsDirectory = new File( reportsDirectory, "testng-native-results" );
196             junitReportsDirectory = new File( reportsDirectory, "testng-junit-results" );
197         }
198 
199         RunListener reporterManager = reporterFactory.createReporter();
200         ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) reporterManager );
201         startTestSuite( reporterManager, this );
202 
203         Class[] testClasses = testNgTestClasses.toArray( new Class[testNgTestClasses.size()] );
204 
205         TestNGExecutor.run( testClasses, this.testSourceDirectory, options, reporterManager, this,
206                             testNgReportsDirectory, testMethodPattern );
207 
208         if ( junitTestClasses.size() > 0 )
209         {
210             testClasses = junitTestClasses.toArray( new Class[junitTestClasses.size()] );
211 
212             TestNGExecutor.run( testClasses, testSourceDirectory, junitOptions, reporterManager, this,
213                                 junitReportsDirectory, testMethodPattern );
214         }
215 
216         finishTestSuite( reporterManager, this );
217     }
218 
219     private boolean isJUnitTest( Class c )
220     {
221         return isJunit3Test( c ) || isJunit4Test( c );
222     }
223 
224     private boolean isJunit4Test( Class c )
225     {
226         return hasJunit4RunWithAnnotation( c ) || hasJunit4TestAnnotation( c );
227     }
228 
229     private boolean hasJunit4RunWithAnnotation( Class c )
230     {
231         return junitRunWithAnnotation != null && c.getAnnotation( junitRunWithAnnotation ) != null;
232     }
233 
234     private boolean hasJunit4TestAnnotation( Class c )
235     {
236         if ( junitTestAnnotation != null )
237         {
238             for ( Method m : c.getMethods() )
239             {
240                 if ( m.getAnnotation( junitTestAnnotation ) != null )
241                 {
242                     return true;
243                 }
244             }
245         }
246 
247         return false;
248     }
249 
250     private boolean isJunit3Test( Class c )
251     {
252         return junitTestClass != null && junitTestClass.isAssignableFrom( c );
253     }
254 
255     private Map createJUnitOptions()
256     {
257         Map junitOptions = new HashMap( this.options );
258         junitOptions.put( "junit", Boolean.TRUE );
259         return junitOptions;
260     }
261 
262     // single class test
263     public void execute( String testSetName, ReporterFactory reporterManagerFactory )
264         throws TestSetFailedException
265     {
266         if ( testSets == null )
267         {
268             throw new IllegalStateException( "You must call locateTestSets before calling execute" );
269         }
270         TestNGTestSet testSet = testSets.get( testSetName );
271 
272         if ( testSet == null )
273         {
274             throw new TestSetFailedException( "Unable to find test set '" + testSetName + "' in suite" );
275         }
276 
277         RunListener reporter = reporterManagerFactory.createReporter();
278         ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) reporter );
279 
280         startTestSuite( reporter, this );
281 
282         TestNGExecutor.run( new Class[] { testSet.getTestClass() }, this.testSourceDirectory, this.options, reporter,
283                             this, reportsDirectory, testMethodPattern );
284 
285         finishTestSuite( reporter, this );
286     }
287 
288     public static void startTestSuite( RunListener reporter, Object suite )
289     {
290         ReportEntry report = new SimpleReportEntry( suite.getClass().getName(), getSuiteName( suite ) );
291 
292         try
293         {
294             reporter.testSetStarting( report );
295         }
296         catch ( ReporterException e )
297         {
298             // TODO: remove this exception from the report manager
299         }
300     }
301 
302     public static void finishTestSuite( RunListener reporterManager, Object suite )
303     {
304         ReportEntry report = new SimpleReportEntry( suite.getClass().getName(), getSuiteName( suite ) );
305 
306         reporterManager.testSetCompleted( report );
307     }
308 
309     public String getSuiteName()
310     {
311         String result = (String) options.get( "suitename" );
312         if ( result == null )
313         {
314             result = "TestSuite";
315         }
316         return result;
317     }
318 
319     private static String getSuiteName( Object suite )
320     {
321         String result;
322         if ( suite instanceof TestNGDirectoryTestSuite )
323         {
324             return ( (TestNGDirectoryTestSuite) suite ).getSuiteName();
325         }
326         else if ( suite instanceof TestNGXmlTestSuite )
327         {
328             return ( (TestNGXmlTestSuite) suite ).getSuiteName();
329         }
330         else
331         {
332             result = "TestSuite";
333         }
334 
335         return result;
336     }
337 
338     public Map locateTestSets( ClassLoader classLoader )
339         throws TestSetFailedException
340     {
341         if ( testSets != null )
342         {
343             throw new IllegalStateException( "You can't call locateTestSets twice" );
344         }
345         testSets = new TreeMap<String, TestNGTestSet>();
346 
347         final TestsToRun scanned = scanResult.applyFilter( new NonAbstractClassFilter(), classLoader );
348 
349         final TestsToRun testsToRun = runOrderCalculator.orderTestClasses( scanned );
350 
351         for ( Class testClass : testsToRun )
352         {
353             TestNGTestSet testSet = new TestNGTestSet( testClass );
354 
355             if ( testSets.containsKey( testSet.getName() ) )
356             {
357                 throw new TestSetFailedException( "Duplicate test set '" + testSet.getName() + "'" );
358             }
359             testSets.put( testSet.getName(), testSet );
360 
361         }
362 
363         return Collections.unmodifiableSortedMap( testSets );
364     }
365 
366 }