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