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.reflect.Constructor;
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.util.List;
27  import java.util.Map;
28  import org.apache.maven.artifact.versioning.ArtifactVersion;
29  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
30  import org.apache.maven.artifact.versioning.VersionRange;
31  import org.apache.maven.surefire.booter.ProviderParameterNames;
32  import org.apache.maven.surefire.report.RunListener;
33  import org.apache.maven.surefire.testng.conf.Configurator;
34  import org.apache.maven.surefire.testng.conf.TestNG4751Configurator;
35  import org.apache.maven.surefire.testng.conf.TestNG52Configurator;
36  import org.apache.maven.surefire.testng.conf.TestNG652Configurator;
37  import org.apache.maven.surefire.testng.conf.TestNGMapConfigurator;
38  import org.apache.maven.surefire.testset.TestSetFailedException;
39  import org.apache.maven.surefire.util.NestedRuntimeException;
40  import org.apache.maven.surefire.util.internal.StringUtils;
41  
42  import org.testng.TestNG;
43  
44  /**
45   * Contains utility methods for executing TestNG.
46   *
47   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
48   * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>
49   */
50  public class TestNGExecutor
51  {
52  
53  
54      private TestNGExecutor()
55      {
56          // noop
57      }
58  
59      public static void run( Class[] testClasses, String testSourceDirectory, Map options, ArtifactVersion version,
60                              RunListener reportManager, TestNgTestSuite suite, File reportsDirectory,
61                              final String methodNamePattern )
62          throws TestSetFailedException
63      {
64          TestNG testng = new TestNG( true );
65  
66          applyGroupMatching( testng, options );
67          if ( !StringUtils.isBlank( methodNamePattern ) )
68          {
69              applyMethodNameFiltering( testng, methodNamePattern );
70          }
71  
72          Configurator configurator = getConfigurator( version );
73          System.out.println( "Configuring TestNG with: " + configurator.getClass().getSimpleName() );
74          configurator.configure( testng, options );
75          postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory );
76          testng.setTestClasses( testClasses );
77          testng.run();
78      }
79  
80      private static void applyMethodNameFiltering( TestNG testng, String methodNamePattern )
81          throws TestSetFailedException
82      {
83          // the class is available in the testClassPath
84          String clazzName = "org.apache.maven.surefire.testng.utils.MethodSelector";
85          // looks to need a high value 
86          testng.addMethodSelector( clazzName, 10000 );
87          try
88          {
89              Class clazz = Class.forName( clazzName );
90  
91              Method method = clazz.getMethod( "setMethodName", new Class[]{ String.class } );
92              method.invoke( null, new Object[]{ methodNamePattern } );
93          }
94          catch ( ClassNotFoundException e )
95          {
96              throw new TestSetFailedException( e.getMessage(), e );
97          }
98          catch ( SecurityException e )
99          {
100             throw new TestSetFailedException( e.getMessage(), e );
101         }
102         catch ( NoSuchMethodException e )
103         {
104             throw new TestSetFailedException( e.getMessage(), e );
105         }
106         catch ( IllegalArgumentException e )
107         {
108             throw new TestSetFailedException( e.getMessage(), e );
109         }
110         catch ( IllegalAccessException e )
111         {
112             throw new TestSetFailedException( e.getMessage(), e );
113         }
114         catch ( InvocationTargetException e )
115         {
116             throw new TestSetFailedException( e.getMessage(), e );
117         }
118     }
119 
120     private static void applyGroupMatching( TestNG testng, Map options )
121         throws TestSetFailedException
122     {
123         String groups = (String) options.get( ProviderParameterNames.TESTNG_GROUPS_PROP );
124         String excludedGroups = (String) options.get( ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP );
125 
126         if ( groups == null && excludedGroups == null )
127         {
128             return;
129         }
130 
131         // the class is available in the testClassPath
132         String clazzName = "org.apache.maven.surefire.testng.utils.GroupMatcherMethodSelector";
133         // looks to need a high value
134         testng.addMethodSelector( clazzName, 9999 );
135         try
136         {
137             Class clazz = Class.forName( clazzName );
138 
139             // HORRIBLE hack, but TNG doesn't allow us to setup a method selector instance directly.
140             Method method = clazz.getMethod( "setGroups", new Class[]{ String.class, String.class } );
141             method.invoke( null, new Object[]{ groups, excludedGroups } );
142         }
143         catch ( ClassNotFoundException e )
144         {
145             throw new TestSetFailedException( e.getMessage(), e );
146         }
147         catch ( SecurityException e )
148         {
149             throw new TestSetFailedException( e.getMessage(), e );
150         }
151         catch ( NoSuchMethodException e )
152         {
153             throw new TestSetFailedException( e.getMessage(), e );
154         }
155         catch ( IllegalArgumentException e )
156         {
157             throw new TestSetFailedException( e.getMessage(), e );
158         }
159         catch ( IllegalAccessException e )
160         {
161             throw new TestSetFailedException( e.getMessage(), e );
162         }
163         catch ( InvocationTargetException e )
164         {
165             throw new TestSetFailedException( e.getMessage(), e );
166         }
167     }
168 
169     public static void run( List suiteFiles, String testSourceDirectory, Map options, ArtifactVersion version,
170                             RunListener reportManager, TestNgTestSuite suite, File reportsDirectory )
171         throws TestSetFailedException
172     {
173         TestNG testng = new TestNG( true );
174         Configurator configurator = getConfigurator( version );
175         configurator.configure( testng, options );
176         postConfigure( testng, testSourceDirectory, reportManager, suite, reportsDirectory );
177         testng.setTestSuites( suiteFiles );
178         testng.run();
179     }
180 
181     private static Configurator getConfigurator( ArtifactVersion version )
182         throws TestSetFailedException
183     {
184         try
185         {
186             VersionRange range = VersionRange.createFromVersionSpec( "[4.7,5.1]" );
187             if ( range.containsVersion( version ) )
188             {
189                 return new TestNG4751Configurator();
190             }
191             range = VersionRange.createFromVersionSpec( "[5.2]" );
192             if ( range.containsVersion( version ) )
193             {
194                 return new TestNG52Configurator();
195             }
196             range = VersionRange.createFromVersionSpec( "[5.3,6.4]" );
197             if ( range.containsVersion( version ) )
198             {
199                 return new TestNGMapConfigurator();
200             }
201             range = VersionRange.createFromVersionSpec( "[6.5,)" );
202             if ( range.containsVersion( version ) )
203             {
204                 return new TestNG652Configurator();
205             }
206 
207             throw new TestSetFailedException( "Unknown TestNG version " + version );
208         }
209         catch ( InvalidVersionSpecificationException invsex )
210         {
211             throw new TestSetFailedException( "Bug in plugin. Please report it with the attached stacktrace", invsex );
212         }
213     }
214 
215 
216     private static void postConfigure( TestNG testNG, String sourcePath, RunListener reportManager,
217                                        TestNgTestSuite suite, File reportsDirectory )
218         throws TestSetFailedException
219     {
220         // turn off all TestNG output
221         testNG.setVerbose( 0 );
222 
223         TestNGReporter reporter = createTestNGReporter( reportManager, suite );
224         testNG.addListener( (Object) reporter );
225 
226         // FIXME: use classifier to decide if we need to pass along the source dir (onyl for JDK14)
227         if ( sourcePath != null )
228         {
229             testNG.setSourcePath( sourcePath );
230         }
231 
232         testNG.setOutputDirectory( reportsDirectory.getAbsolutePath() );
233     }
234 
235     // If we have access to IResultListener, return a ConfigurationAwareTestNGReporter
236     // But don't cause NoClassDefFoundErrors if it isn't available; just return a regular TestNGReporter instead
237     private static TestNGReporter createTestNGReporter( RunListener reportManager, TestNgTestSuite suite )
238     {
239         try
240         {
241             Class.forName( "org.testng.internal.IResultListener" );
242             Class c = Class.forName( "org.apache.maven.surefire.testng.ConfigurationAwareTestNGReporter" );
243             try
244             {
245                 Constructor ctor = c.getConstructor( new Class[]{ RunListener.class, TestNgTestSuite.class } );
246                 return (TestNGReporter) ctor.newInstance( new Object[]{ reportManager, suite } );
247             }
248             catch ( Exception e )
249             {
250                 throw new NestedRuntimeException( "Bug in ConfigurationAwareTestNGReporter", e );
251             }
252         }
253         catch ( ClassNotFoundException e )
254         {
255             return new TestNGReporter( reportManager );
256         }
257     }
258 
259 }