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