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  
31  import org.apache.maven.surefire.cli.CommandLineOption;
32  import org.apache.maven.surefire.report.RunListener;
33  import org.apache.maven.surefire.testset.TestListResolver;
34  import org.apache.maven.surefire.testset.TestSetFailedException;
35  import org.apache.maven.surefire.util.TestsToRun;
36  
37  import static org.apache.maven.surefire.testng.TestNGExecutor.run;
38  import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
39  
40  /**
41   * Test suite for TestNG based on a directory of Java test classes. Can also execute JUnit tests.
42   *
43   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
44   * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>
45   */
46  final class TestNGDirectoryTestSuite
47      extends TestSuite
48  {
49      private final Map<String, String> options;
50  
51      private final Map<String, String> junitOptions;
52  
53      private final String testSourceDirectory;
54  
55      private final File reportsDirectory;
56  
57      private final TestListResolver methodFilter;
58  
59      private final Class<?> junitTestClass;
60  
61      private final Class<? extends Annotation> junitRunWithAnnotation;
62  
63      private final Class<? extends Annotation> junitTestAnnotation;
64  
65      private final List<CommandLineOption> mainCliOptions;
66  
67      private final int skipAfterFailureCount;
68  
69      TestNGDirectoryTestSuite( String testSourceDirectory, Map<String, String> confOptions, File reportsDirectory,
70                                TestListResolver methodFilter, List<CommandLineOption> mainCliOptions,
71                                int skipAfterFailureCount )
72      {
73          this.options = confOptions;
74          this.testSourceDirectory = testSourceDirectory;
75          this.reportsDirectory = reportsDirectory;
76          this.methodFilter = methodFilter;
77          this.junitTestClass = findJUnitTestClass();
78          this.junitRunWithAnnotation = findJUnitRunWithAnnotation();
79          this.junitTestAnnotation = findJUnitTestAnnotation();
80          this.junitOptions = createJUnitOptions();
81          this.mainCliOptions = mainCliOptions;
82          this.skipAfterFailureCount = skipAfterFailureCount;
83      }
84  
85      void execute( TestsToRun testsToRun, RunListener reporterManager )
86          throws TestSetFailedException
87      {
88          if ( !testsToRun.allowEagerReading() )
89          {
90              executeLazy( testsToRun, reporterManager );
91          }
92          else if ( testsToRun.containsAtLeast( 2 ) )
93          {
94              executeMulti( testsToRun, reporterManager );
95          }
96          else if ( testsToRun.containsAtLeast( 1 ) )
97          {
98              Class<?> testClass = testsToRun.iterator().next();
99              executeSingleClass( reporterManager, testClass );
100         }
101     }
102 
103     private void executeSingleClass( RunListener reporter, Class<?> testClass )
104         throws TestSetFailedException
105     {
106         options.put( "suitename", testClass.getName() );
107 
108         startTestSuite( reporter );
109 
110         Map<String, String> optionsToUse = isJUnitTest( testClass ) ? junitOptions : options;
111 
112         run( Collections.<Class<?>>singleton( testClass ), testSourceDirectory, optionsToUse, reporter,
113                 reportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
114 
115         finishTestSuite( reporter );
116     }
117 
118     private void executeLazy( TestsToRun testsToRun, RunListener reporterManager )
119         throws TestSetFailedException
120     {
121         for ( Class<?> testToRun : testsToRun )
122         {
123             executeSingleClass( reporterManager, testToRun );
124         }
125     }
126 
127     private static Class<?> findJUnitTestClass()
128     {
129         return lookupClass( "junit.framework.Test" );
130     }
131 
132     private static Class<Annotation> findJUnitRunWithAnnotation()
133     {
134         return lookupAnnotation( "org.junit.runner.RunWith" );
135     }
136 
137     private static Class<Annotation> findJUnitTestAnnotation()
138     {
139         return lookupAnnotation( "org.junit.Test" );
140     }
141 
142     @SuppressWarnings( "unchecked" )
143     private static Class<Annotation> lookupAnnotation( String className )
144     {
145         try
146         {
147             return (Class<Annotation>) Class.forName( className );
148         }
149         catch ( ClassNotFoundException e )
150         {
151             return null;
152         }
153     }
154 
155     private static Class<?> lookupClass( String className )
156     {
157         try
158         {
159             return Class.forName( className );
160         }
161         catch ( ClassNotFoundException e )
162         {
163             return null;
164         }
165     }
166 
167     private void executeMulti( TestsToRun testsToRun, RunListener reporterManager )
168         throws TestSetFailedException
169     {
170         List<Class<?>> testNgTestClasses = new ArrayList<Class<?>>();
171         List<Class<?>> junitTestClasses = new ArrayList<Class<?>>();
172         for ( Class<?> testToRun : testsToRun )
173         {
174             if ( isJUnitTest( testToRun ) )
175             {
176                 junitTestClasses.add( testToRun );
177             }
178             else
179             {
180                 testNgTestClasses.add( testToRun );
181             }
182         }
183 
184         File testNgReportsDirectory = reportsDirectory, junitReportsDirectory = reportsDirectory;
185 
186         if ( !junitTestClasses.isEmpty() && !testNgTestClasses.isEmpty() )
187         {
188             testNgReportsDirectory = new File( reportsDirectory, "testng-native-results" );
189             junitReportsDirectory = new File( reportsDirectory, "testng-junit-results" );
190         }
191         startTestSuite( reporterManager );
192 
193         run( testNgTestClasses, testSourceDirectory, options, reporterManager,
194                 testNgReportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
195 
196         if ( !junitTestClasses.isEmpty() )
197         {
198             run( junitTestClasses, testSourceDirectory, junitOptions, reporterManager,
199                     junitReportsDirectory, methodFilter, mainCliOptions, skipAfterFailureCount );
200         }
201 
202         finishTestSuite( reporterManager );
203     }
204 
205     private boolean isJUnitTest( Class<?> c )
206     {
207         return isJunit3Test( c ) || isJunit4Test( c );
208     }
209 
210     private boolean isJunit4Test( Class<?> c )
211     {
212         return hasJunit4RunWithAnnotation( c ) || hasJunit4TestAnnotation( c );
213     }
214 
215     private boolean hasJunit4RunWithAnnotation( Class<?> c )
216     {
217         return junitRunWithAnnotation != null && c.getAnnotation( junitRunWithAnnotation ) != null;
218     }
219 
220     private boolean hasJunit4TestAnnotation( Class<?> c )
221     {
222         if ( junitTestAnnotation != null )
223         {
224             for ( Method m : c.getMethods() )
225             {
226                 if ( m.getAnnotation( junitTestAnnotation ) != null )
227                 {
228                     return true;
229                 }
230             }
231         }
232 
233         return false;
234     }
235 
236     private boolean isJunit3Test( Class<?> c )
237     {
238         return junitTestClass != null && junitTestClass.isAssignableFrom( c );
239     }
240 
241     private Map<String, String> createJUnitOptions()
242     {
243         Map<String, String> junitOptions = new HashMap<String, String>( options );
244         String onlyJUnit = options.get( "junit" );
245         if ( isBlank( onlyJUnit ) )
246         {
247             onlyJUnit = "true";
248         }
249         junitOptions.put( "junit", onlyJUnit );
250         return junitOptions;
251     }
252 
253     @Override
254     Map<String, String> getOptions()
255     {
256         return options;
257     }
258 }