...29import org.apache.maven.surefire.util.internal.StringUtils;30import org.testng.TestNG;31import org.testng.annotations.Test;32import org.testng.xml.XmlClass;33import org.testng.xml.XmlMethodSelector;34import org.testng.xml.XmlSuite;35import org.testng.xml.XmlTest;36import java.io.File;37import java.lang.annotation.Annotation;38import java.lang.reflect.Constructor;39import java.lang.reflect.InvocationTargetException;40import java.lang.reflect.Method;41import java.util.ArrayList;42import java.util.HashMap;43import java.util.List;44import java.util.Map;45import java.util.concurrent.atomic.AtomicInteger;46import static org.apache.maven.surefire.cli.CommandLineOption.LOGGING_LEVEL_DEBUG;47import static org.apache.maven.surefire.cli.CommandLineOption.SHOW_ERRORS;48import static org.apache.maven.surefire.util.ReflectionUtils.instantiate;49import static org.apache.maven.surefire.util.ReflectionUtils.tryLoadClass;50import static org.apache.maven.surefire.util.internal.ConcurrencyUtils.countDownToZero;51/**52 * Contains utility methods for executing TestNG.53 *54 * @author <a href="mailto:brett@apache.org">Brett Porter</a>55 * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>56 */57final class TestNGExecutor58{59 /** The default name for a suite launched from the maven surefire plugin */60 private static final String DEFAULT_SUREFIRE_SUITE_NAME = "Surefire suite";61 /** The default name for a test launched from the maven surefire plugin */62 private static final String DEFAULT_SUREFIRE_TEST_NAME = "Surefire test";63 private static final boolean HAS_TEST_ANNOTATION_ON_CLASSPATH =64 tryLoadClass( TestNGExecutor.class.getClassLoader(), "org.testng.annotations.Test" ) != null;65 private TestNGExecutor()66 {67 throw new IllegalStateException( "not instantiable constructor" );68 }69 @SuppressWarnings( "checkstyle:parameternumbercheck" )70 static void run( Iterable<Class<?>> testClasses, String testSourceDirectory,71 Map<String, String> options, // string,string because TestNGMapConfigurator#configure()72 RunListener reportManager, File reportsDirectory,73 TestListResolver methodFilter, List<CommandLineOption> mainCliOptions,74 int skipAfterFailureCount )75 throws TestSetFailedException76 {77 TestNG testng = new TestNG( true );78 Configurator configurator = getConfigurator( options.get( "testng.configurator" ) );79 if ( isCliDebugOrShowErrors( mainCliOptions ) )80 {81 System.out.println( "Configuring TestNG with: " + configurator.getClass().getSimpleName() );82 }83 XmlMethodSelector groupMatchingSelector = createGroupMatchingSelector( options );84 XmlMethodSelector methodNameFilteringSelector = createMethodNameFilteringSelector( methodFilter );85 Map<String, SuiteAndNamedTests> suitesNames = new HashMap<>();86 List<XmlSuite> xmlSuites = new ArrayList<>();87 for ( Class<?> testClass : testClasses )88 {89 TestMetadata metadata = findTestMetadata( testClass );90 SuiteAndNamedTests suiteAndNamedTests = suitesNames.get( metadata.suiteName );91 if ( suiteAndNamedTests == null )92 {93 suiteAndNamedTests = new SuiteAndNamedTests();94 suiteAndNamedTests.xmlSuite.setName( metadata.suiteName );95 configurator.configure( suiteAndNamedTests.xmlSuite, options );96 xmlSuites.add( suiteAndNamedTests.xmlSuite );97 suitesNames.put( metadata.suiteName, suiteAndNamedTests );98 }99 XmlTest xmlTest = suiteAndNamedTests.testNameToTest.get( metadata.testName );100 if ( xmlTest == null )101 {102 xmlTest = new XmlTest( suiteAndNamedTests.xmlSuite );103 xmlTest.setName( metadata.testName );104 addSelector( xmlTest, groupMatchingSelector );105 addSelector( xmlTest, methodNameFilteringSelector );106 xmlTest.setXmlClasses( new ArrayList<XmlClass>() );107 suiteAndNamedTests.testNameToTest.put( metadata.testName, xmlTest );108 }109 xmlTest.getXmlClasses().add( new XmlClass( testClass.getName() ) );110 }111 testng.setXmlSuites( xmlSuites );112 configurator.configure( testng, options );113 postConfigure( testng, testSourceDirectory, reportManager, reportsDirectory, skipAfterFailureCount,114 extractVerboseLevel( options ) );115 testng.run();116 }117 private static boolean isCliDebugOrShowErrors( List<CommandLineOption> mainCliOptions )118 {119 return mainCliOptions.contains( LOGGING_LEVEL_DEBUG ) || mainCliOptions.contains( SHOW_ERRORS );120 }121 private static TestMetadata findTestMetadata( Class<?> testClass )122 {123 TestMetadata result = new TestMetadata();124 if ( HAS_TEST_ANNOTATION_ON_CLASSPATH )125 {126 Test testAnnotation = findAnnotation( testClass, Test.class );127 if ( null != testAnnotation )128 {129 if ( !StringUtils.isBlank( testAnnotation.suiteName() ) )130 {131 result.suiteName = testAnnotation.suiteName();132 }133 if ( !StringUtils.isBlank( testAnnotation.testName() ) )134 {135 result.testName = testAnnotation.testName();136 }137 }138 }139 return result;140 }141 private static <T extends Annotation> T findAnnotation( Class<?> clazz, Class<T> annotationType )142 {143 if ( clazz == null )144 {145 return null;146 }147 T result = clazz.getAnnotation( annotationType );148 if ( result != null )149 {150 return result;151 }152 return findAnnotation( clazz.getSuperclass(), annotationType );153 }154 private static class TestMetadata155 {156 private String testName = DEFAULT_SUREFIRE_TEST_NAME;157 private String suiteName = DEFAULT_SUREFIRE_SUITE_NAME;158 }159 private static class SuiteAndNamedTests160 {161 private XmlSuite xmlSuite = new XmlSuite();162 private Map<String, XmlTest> testNameToTest = new HashMap<>();163 }164 private static void addSelector( XmlTest xmlTest, XmlMethodSelector selector )165 {166 if ( selector != null )167 {168 xmlTest.getMethodSelectors().add( selector );169 }170 }171 @SuppressWarnings( "checkstyle:magicnumber" )172 private static XmlMethodSelector createMethodNameFilteringSelector( TestListResolver methodFilter )173 throws TestSetFailedException174 {175 if ( methodFilter != null && !methodFilter.isEmpty() )176 {177 // the class is available in the testClassPath178 String clazzName = "org.apache.maven.surefire.testng.utils.MethodSelector";179 try180 {181 Class<?> clazz = Class.forName( clazzName );182 Method method = clazz.getMethod( "setTestListResolver", TestListResolver.class );183 method.invoke( null, methodFilter );184 }185 catch ( Exception e )186 {187 throw new TestSetFailedException( e.getMessage(), e );188 }189 XmlMethodSelector xms = new XmlMethodSelector();190 xms.setName( clazzName );191 // looks to need a high value192 xms.setPriority( 10000 );193 return xms;194 }195 else196 {197 return null;198 }199 }200 @SuppressWarnings( "checkstyle:magicnumber" )201 private static XmlMethodSelector createGroupMatchingSelector( Map<String, String> options )202 throws TestSetFailedException203 {204 final String groups = options.get( ProviderParameterNames.TESTNG_GROUPS_PROP );205 final String excludedGroups = options.get( ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP );206 if ( groups == null && excludedGroups == null )207 {208 return null;209 }210 // the class is available in the testClassPath211 final String clazzName = "org.apache.maven.surefire.testng.utils.GroupMatcherMethodSelector";212 try213 {214 Class<?> clazz = Class.forName( clazzName );215 // HORRIBLE hack, but TNG doesn't allow us to setup a method selector instance directly.216 Method method = clazz.getMethod( "setGroups", String.class, String.class );217 method.invoke( null, groups, excludedGroups );218 }219 catch ( Exception e )220 {221 throw new TestSetFailedException( e.getMessage(), e );222 }223 XmlMethodSelector xms = new XmlMethodSelector();224 xms.setName( clazzName );225 // looks to need a high value226 xms.setPriority( 9999 );227 return xms;228 }229 static void run( List<String> suiteFiles, String testSourceDirectory,230 Map<String, String> options, // string,string because TestNGMapConfigurator#configure()231 RunListener reportManager, File reportsDirectory, int skipAfterFailureCount )232 throws TestSetFailedException233 {234 TestNG testng = new TestNG( true );235 Configurator configurator = getConfigurator( options.get( "testng.configurator" ) );236 configurator.configure( testng, options );237 postConfigure( testng, testSourceDirectory, reportManager, reportsDirectory, skipAfterFailureCount,...