1 package org.apache.maven.surefire.testng;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.surefire.booter.ProviderParameterNames;
23 import org.apache.maven.surefire.report.RunListener;
24 import org.apache.maven.surefire.testng.conf.Configurator;
25 import org.apache.maven.surefire.testset.TestSetFailedException;
26 import org.apache.maven.surefire.util.ReflectionUtils;
27 import org.apache.maven.surefire.util.internal.StringUtils;
28 import org.testng.TestNG;
29 import org.testng.annotations.Test;
30 import org.testng.xml.XmlClass;
31 import org.testng.xml.XmlMethodSelector;
32 import org.testng.xml.XmlSuite;
33 import org.testng.xml.XmlTest;
34
35 import java.io.File;
36 import java.lang.annotation.Annotation;
37 import java.lang.reflect.Constructor;
38 import java.lang.reflect.InvocationTargetException;
39 import java.lang.reflect.Method;
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44
45
46
47
48
49
50
51 public class TestNGExecutor
52 {
53
54 public static final String DEFAULT_SUREFIRE_SUITE_NAME = "Surefire suite";
55
56
57 public static final String DEFAULT_SUREFIRE_TEST_NAME = "Surefire test";
58
59 private static final boolean HAS_TEST_ANNOTATION_ON_CLASSPATH =
60 null != ReflectionUtils.tryLoadClass( TestNGExecutor.class.getClassLoader(), "org.testng.annotations.Test" );
61
62 private TestNGExecutor()
63 {
64
65 }
66
67 public static void run( Class[] testClasses, String testSourceDirectory, Map options, RunListener reportManager,
68 TestNgTestSuite suite, File reportsDirectory, final String methodNamePattern )
69 throws TestSetFailedException
70 {
71 TestNG testng = new TestNG( true );
72
73 Configurator configurator = getConfigurator( (String) options.get( "testng.configurator" ) );
74 System.out.println( "Configuring TestNG with: " + configurator.getClass().getSimpleName() );
75
76 XmlMethodSelector groupMatchingSelector = getGroupMatchingSelector( options );
77 XmlMethodSelector methodNameFilteringSelector = getMethodNameFilteringSelector( methodNamePattern );
78
79 Map<String, SuiteAndNamedTests> suitesNames = new HashMap<String, SuiteAndNamedTests>();
80
81 List<XmlSuite> xmlSuites = new ArrayList<XmlSuite>();
82 for ( Class testClass : testClasses )
83 {
84 TestMetadata metadata = findTestMetadata( testClass );
85
86 SuiteAndNamedTests suiteAndNamedTests = suitesNames.get( metadata.suiteName );
87 if ( suiteAndNamedTests == null )
88 {
89 suiteAndNamedTests = new SuiteAndNamedTests();
90 suiteAndNamedTests.xmlSuite.setName( metadata.suiteName );
91 configurator.configure( suiteAndNamedTests.xmlSuite, options );
92 xmlSuites.add( suiteAndNamedTests.xmlSuite );
93
94 suitesNames.put( metadata.suiteName, suiteAndNamedTests );
95 }
96
97 XmlTest xmlTest = suiteAndNamedTests.testNameToTest.get( metadata.testName );
98 if ( xmlTest == null )
99 {
100 xmlTest = new XmlTest( suiteAndNamedTests.xmlSuite );
101 xmlTest.setName( metadata.testName );
102 addSelector( xmlTest, groupMatchingSelector );
103 addSelector( xmlTest, methodNameFilteringSelector );
104 xmlTest.setXmlClasses( new ArrayList<XmlClass>() );
105
106 suiteAndNamedTests.testNameToTest.put( metadata.testName, xmlTest );
107 }
108
109 xmlTest.getXmlClasses().add( new XmlClass( testClass.getName() ) );
110 }
111
112 testng.setXmlSuites( xmlSuites );
113 configurator.configure( testng, options );
114 postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory );
115 testng.run();
116 }
117
118 private static TestMetadata findTestMetadata( Class testClass )
119 {
120 TestMetadata result = new TestMetadata();
121 if ( HAS_TEST_ANNOTATION_ON_CLASSPATH )
122 {
123 Test testAnnotation = findAnnotation( testClass, Test.class );
124 if ( null != testAnnotation )
125 {
126 if ( !StringUtils.isBlank( testAnnotation.suiteName() ) )
127 {
128 result.suiteName = testAnnotation.suiteName();
129 }
130
131 if ( !StringUtils.isBlank( testAnnotation.testName() ) )
132 {
133 result.testName = testAnnotation.testName();
134 }
135 }
136 }
137 return result;
138 }
139
140 private static <T extends Annotation> T findAnnotation( Class<?> clazz, Class<T> annotationType )
141 {
142 if ( clazz == null )
143 {
144 return null;
145 }
146
147 T result = clazz.getAnnotation( annotationType );
148 if ( result != null )
149 {
150 return result;
151 }
152
153 return findAnnotation( clazz.getSuperclass(), annotationType );
154 }
155
156 private static class TestMetadata
157 {
158 private String testName = DEFAULT_SUREFIRE_TEST_NAME;
159
160 private String suiteName = DEFAULT_SUREFIRE_SUITE_NAME;
161 }
162
163 private static class SuiteAndNamedTests
164 {
165 private XmlSuite xmlSuite = new XmlSuite();
166
167 private Map<String, XmlTest> testNameToTest = new HashMap<String, XmlTest>();
168 }
169
170 private static void addSelector( XmlTest xmlTest, XmlMethodSelector selector )
171 {
172 if ( selector != null )
173 {
174 xmlTest.getMethodSelectors().add( selector );
175 }
176 }
177
178 private static XmlMethodSelector getMethodNameFilteringSelector( String methodNamePattern )
179 throws TestSetFailedException
180 {
181 if ( StringUtils.isBlank( methodNamePattern ) )
182 {
183 return null;
184 }
185
186
187 String clazzName = "org.apache.maven.surefire.testng.utils.MethodSelector";
188 try
189 {
190 Class clazz = Class.forName( clazzName );
191
192 Method method = clazz.getMethod( "setMethodName", new Class[] { String.class } );
193 method.invoke( null, methodNamePattern );
194 }
195 catch ( ClassNotFoundException e )
196 {
197 throw new TestSetFailedException( e.getMessage(), e );
198 }
199 catch ( SecurityException e )
200 {
201 throw new TestSetFailedException( e.getMessage(), e );
202 }
203 catch ( NoSuchMethodException e )
204 {
205 throw new TestSetFailedException( e.getMessage(), e );
206 }
207 catch ( IllegalArgumentException e )
208 {
209 throw new TestSetFailedException( e.getMessage(), e );
210 }
211 catch ( IllegalAccessException e )
212 {
213 throw new TestSetFailedException( e.getMessage(), e );
214 }
215 catch ( InvocationTargetException e )
216 {
217 throw new TestSetFailedException( e.getMessage(), e );
218 }
219
220 XmlMethodSelector xms = new XmlMethodSelector();
221
222 xms.setName( clazzName );
223
224 xms.setPriority( 10000 );
225
226 return xms;
227 }
228
229 private static XmlMethodSelector getGroupMatchingSelector( Map options )
230 throws TestSetFailedException
231 {
232 String groups = (String) options.get( ProviderParameterNames.TESTNG_GROUPS_PROP );
233 String excludedGroups = (String) options.get( ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP );
234
235 if ( groups == null && excludedGroups == null )
236 {
237 return null;
238 }
239
240
241 String clazzName = "org.apache.maven.surefire.testng.utils.GroupMatcherMethodSelector";
242 try
243 {
244 Class clazz = Class.forName( clazzName );
245
246
247 Method method = clazz.getMethod( "setGroups", new Class[] { String.class, String.class } );
248 method.invoke( null, groups, excludedGroups );
249 }
250 catch ( ClassNotFoundException e )
251 {
252 throw new TestSetFailedException( e.getMessage(), e );
253 }
254 catch ( SecurityException e )
255 {
256 throw new TestSetFailedException( e.getMessage(), e );
257 }
258 catch ( NoSuchMethodException e )
259 {
260 throw new TestSetFailedException( e.getMessage(), e );
261 }
262 catch ( IllegalArgumentException e )
263 {
264 throw new TestSetFailedException( e.getMessage(), e );
265 }
266 catch ( IllegalAccessException e )
267 {
268 throw new TestSetFailedException( e.getMessage(), e );
269 }
270 catch ( InvocationTargetException e )
271 {
272 throw new TestSetFailedException( e.getMessage(), e );
273 }
274
275 XmlMethodSelector xms = new XmlMethodSelector();
276
277 xms.setName( clazzName );
278
279 xms.setPriority( 9999 );
280
281 return xms;
282 }
283
284 public static void run( List<String> suiteFiles, String testSourceDirectory, Map options,
285 RunListener reportManager, TestNgTestSuite suite, File reportsDirectory )
286 throws TestSetFailedException
287 {
288 TestNG testng = new TestNG( true );
289 Configurator configurator = getConfigurator( (String) options.get( "testng.configurator" ) );
290 configurator.configure( testng, options );
291 postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory );
292 testng.setTestSuites( suiteFiles );
293 testng.run();
294 }
295
296 private static Configurator getConfigurator( String className )
297 {
298 try
299 {
300 return (Configurator) Class.forName( className ).newInstance();
301 }
302 catch ( InstantiationException e )
303 {
304 throw new RuntimeException( e );
305 }
306 catch ( IllegalAccessException e )
307 {
308 throw new RuntimeException( e );
309 }
310 catch ( ClassNotFoundException e )
311 {
312 throw new RuntimeException( e );
313 }
314 }
315
316 private static void postConfigure( TestNG testNG, String sourcePath, RunListener reportManager,
317 TestNgTestSuite suite, File reportsDirectory )
318 throws TestSetFailedException
319 {
320
321 testNG.setVerbose( 0 );
322
323 TestNGReporter reporter = createTestNGReporter( reportManager, suite );
324 testNG.addListener( (Object) reporter );
325
326
327 if ( sourcePath != null )
328 {
329 testNG.setSourcePath( sourcePath );
330 }
331
332 testNG.setOutputDirectory( reportsDirectory.getAbsolutePath() );
333 }
334
335
336
337 private static TestNGReporter createTestNGReporter( RunListener reportManager, TestNgTestSuite suite )
338 {
339 try
340 {
341 Class.forName( "org.testng.internal.IResultListener" );
342 Class c = Class.forName( "org.apache.maven.surefire.testng.ConfigurationAwareTestNGReporter" );
343 try
344 {
345 Constructor ctor = c.getConstructor( new Class[] { RunListener.class, TestNgTestSuite.class } );
346 return (TestNGReporter) ctor.newInstance( reportManager, suite );
347 }
348 catch ( Exception e )
349 {
350 throw new RuntimeException( "Bug in ConfigurationAwareTestNGReporter", e );
351 }
352 }
353 catch ( ClassNotFoundException e )
354 {
355 return new TestNGReporter( reportManager );
356 }
357 }
358
359 }