这里是基于surefire 2.19.1版本分析的。
还是根据surefire 拉起单元测试执行报错的日志展示的执行过程分析
java.lang.instrument.IllegalClassFormatException: Error while instrumenting class com/suning/imp/scheduler/ActivityDoneMonitorSchedule. at org.jacoco.agent.rt.internal_6da5971.CoverageTransformer.transform(CoverageTransformer.java:93) at sun.instrument.TransformerManager.transform(TransformerManager.java:188) at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:760) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) at java.net.URLClassLoader.access$100(URLClassLoader.java:73) at java.net.URLClassLoader$1.run(URLClassLoader.java:368) at java.net.URLClassLoader$1.run(URLClassLoader.java:362) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:361) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at org.springframework.util.ClassUtils.forName(ClassUtils.java:258) at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:417) at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1283) at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1254) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:576) at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1331) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:341) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:312) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:420) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:617) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:451) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:106) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:57) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:248) at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124) at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:313) at org.springframework.test.context.testng.AbstractTestNGSpringContextTests.springTestContextPrepareTestInstance(AbstractTestNGSpringContextTests.java:130) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84) at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:564) at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:213) at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:138) at org.testng.internal.TestMethodWorker.invokeBeforeClassMethods(TestMethodWorker.java:175) at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:107) at org.testng.TestRunner.privateRun(TestRunner.java:767) at org.testng.TestRunner.run(TestRunner.java:617) at org.testng.SuiteRunner.runTest(SuiteRunner.java:348) at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:343) at org.testng.SuiteRunner.privateRun(SuiteRunner.java:305) at org.testng.SuiteRunner.run(SuiteRunner.java:254) at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52) at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86) at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224) at org.testng.TestNG.runSuitesLocally(TestNG.java:1149) at org.testng.TestNG.run(TestNG.java:1057) at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:281) at org.apache.maven.surefire.testng.TestNGXmlTestSuite.execute(TestNGXmlTestSuite.java:75) at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:121) at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:290) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:242) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:121) Caused by: java.io.IOException: Error while instrumenting class com/suning/imp/scheduler/ActivityDoneMonitorSchedule. at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrumentError(Instrumenter.java:160) at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrument(Instrumenter.java:111) at org.jacoco.agent.rt.internal_6da5971.CoverageTransformer.transform(CoverageTransformer.java:91) ... 62 more Caused by: java.lang.IllegalStateException: Class com/suning/imp/scheduler/ActivityDoneMonitorSchedule is already instrumented. at org.jacoco.agent.rt.internal_6da5971.core.internal.instr.InstrSupport.assertNotInstrumented(InstrSupport.java:89) at org.jacoco.agent.rt.internal_6da5971.core.internal.instr.ClassInstrumenter.visitField(ClassInstrumenter.java:55) at org.jacoco.agent.rt.internal_6da5971.asm.ClassVisitor.visitField(ClassVisitor.java:272) at org.jacoco.agent.rt.internal_6da5971.asm.ClassReader.readField(ClassReader.java:768) at org.jacoco.agent.rt.internal_6da5971.asm.ClassReader.accept(ClassReader.java:689) at org.jacoco.agent.rt.internal_6da5971.asm.ClassReader.accept(ClassReader.java:506) at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrument(Instrumenter.java:84) at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrument(Instrumenter.java:108) ... 63 more
-> SuiteRunnerWorker.run -> runSuites
-> SuiteRunner.run -> privateRun -> invokeTestMethods
->testng.internal.TestMethodWorker.run -> invokeTestMethods
->testng.internal.Invoker.invokeTestMethods -> invokeMethod
->testng.internal.MethodInvocationHelper.invokeMethod
然后surefire 通过调用 booter.SurefireBooter.main
(surefire-booter.2.19.1.jar) -> booter.ForkedBooter.runSuitesInProcess
-> booter.ForkedBooter.invokeProviderInSameClassLoader
-> surefire.testng.TestNGProvider.invoke (surefire-testng-2.19.1)
-> surefire.testng.TestNGXmlTestSuite.execute
-> surefire.testng.TestNGExecutor.run
-> TestNG.run(testng-6.9.12)
surefireBooter类代码:public final class ForkedBooter
{
private static final long SYSTEM_EXIT_TIMEOUT_IN_SECONDS = 30;
private static final long PING_TIMEOUT_IN_SECONDS = 20;
private static final ScheduledExecutorService JVM_TERMINATOR = createJvmTerminator();
/**
* This method is invoked when Surefire is forked - this method parses and organizes the arguments passed to it and
* then calls the Surefire class' run method. <p/> The system exit code will be 1 if an exception is thrown.
*
* @param args Commandline arguments
*/
public static void main( String... args )
{
final CommandReader reader = startupMasterProcessReader();
final ScheduledFuture<?> pingScheduler = listenToShutdownCommands( reader );
final PrintStream originalOut = System.out;
try
{
if ( args.length > 1 )
{
SystemPropertyManager.setSystemProperties( new File( args[1] ) );
}
File surefirePropertiesFile = new File( args[0] );
InputStream stream = surefirePropertiesFile.exists() ? new FileInputStream( surefirePropertiesFile ) : null;
BooterDeserializer booterDeserializer = new BooterDeserializer( stream );
ProviderConfiguration providerConfiguration = booterDeserializer.deserialize();
final StartupConfiguration startupConfiguration = booterDeserializer.getProviderConfiguration();
TypeEncodedValue forkedTestSet = providerConfiguration.getTestForFork();
boolean readTestsFromInputStream = providerConfiguration.isReadTestsFromInStream();
final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
if ( startupConfiguration.isManifestOnlyJarRequestedAndUsable() )
{
classpathConfiguration.trickClassPathWhenManifestOnlyClasspath();
}
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
classLoader.setDefaultAssertionStatus( classpathConfiguration.isEnableAssertions() );
startupConfiguration.writeSurefireTestClasspathProperty();
final Object testSet;
if ( forkedTestSet != null )
{
testSet = forkedTestSet.getDecodedValue( classLoader );
}
else if ( readTestsFromInputStream )
{
testSet = new LazyTestsToRun( originalOut );
}
else
{
testSet = null;
}
try
{
runSuitesInProcess( testSet, startupConfiguration, providerConfiguration, originalOut );
}
catch ( InvocationTargetException t )
{
LegacyPojoStackTraceWriter stackTraceWriter =
new LegacyPojoStackTraceWriter( "test subystem", "no method", t.getTargetException() );
StringBuilder stringBuilder = new StringBuilder();
encode( stringBuilder, stackTraceWriter, false );
encodeAndWriteToOutput( ( (char) BOOTERCODE_ERROR ) + ",0," + stringBuilder + "\n" , originalOut );
}
catch ( Throwable t )
{
StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( "test subystem", "no method", t );
StringBuilder stringBuilder = new StringBuilder();
encode( stringBuilder, stackTraceWriter, false );
encodeAndWriteToOutput( ( (char) BOOTERCODE_ERROR ) + ",0," + stringBuilder + "\n", originalOut );
}
// Say bye.
encodeAndWriteToOutput( ( (char) BOOTERCODE_BYE ) + ",0,BYE!\n", originalOut );
originalOut.flush();
// noinspection CallToSystemExit
exit( 0, EXIT, reader, false );
}
catch ( Throwable t )
{
// Just throwing does getMessage() and a local trace - we want to call printStackTrace for a full trace
// noinspection UseOfSystemOutOrSystemErr
t.printStackTrace( System.err );
// noinspection ProhibitedExceptionThrown,CallToSystemExit
exit( 1, EXIT, reader, false );
}
finally
{
pingScheduler.cancel( true );
}
}
private static CommandReader startupMasterProcessReader()
{
return CommandReader.getReader();
}
private static ScheduledFuture<?> listenToShutdownCommands( CommandReader reader )
{
reader.addShutdownListener( createExitHandler( reader ) );
AtomicBoolean pingDone = new AtomicBoolean( true );
reader.addNoopListener( createPingHandler( pingDone ) );
return JVM_TERMINATOR.scheduleAtFixedRate( createPingJob( pingDone, reader ),
0, PING_TIMEOUT_IN_SECONDS, SECONDS );
}
private static CommandListener createPingHandler( final AtomicBoolean pingDone )
{
return new CommandListener()
{
public void update( Command command )
{
pingDone.set( true );
}
};
}
private static CommandListener createExitHandler( final CommandReader reader )
{
return new CommandListener()
{
public void update( Command command )
{
exit( 1, command.toShutdownData(), reader, true );
}
};
}
private static Runnable createPingJob( final AtomicBoolean pingDone, final CommandReader reader )
{
return new Runnable()
{
public void run()
{
boolean hasPing = pingDone.getAndSet( false );
if ( !hasPing )
{
exit( 1, KILL, reader, true );
}
}
};
}
private static void encodeAndWriteToOutput( String string, PrintStream out )
{
byte[] encodeBytes = encodeStringForForkCommunication( string );
out.write( encodeBytes, 0, encodeBytes.length );
}
private static void exit( int returnCode, Shutdown shutdownType, CommandReader reader, boolean stopReaderOnExit )
{
switch ( shutdownType )
{
case KILL:
Runtime.getRuntime().halt( returnCode );
case EXIT:
if ( stopReaderOnExit )
{
reader.stop();
}
launchLastDitchDaemonShutdownThread( returnCode );
System.exit( returnCode );
case DEFAULT:
// refers to shutdown=testset, but not used now, keeping reader open
default:
break;
}
}
private static RunResult runSuitesInProcess( Object testSet, StartupConfiguration startupConfiguration,
ProviderConfiguration providerConfiguration,
PrintStream originalSystemOut )
throws SurefireExecutionException, TestSetFailedException, InvocationTargetException
{
final ReporterFactory factory = createForkingReporterFactory( providerConfiguration, originalSystemOut );
return invokeProviderInSameClassLoader( testSet, factory, providerConfiguration, true, startupConfiguration,
false );
}
private static ReporterFactory createForkingReporterFactory( ProviderConfiguration providerConfiguration,
PrintStream originalSystemOut )
{
final boolean trimStackTrace = providerConfiguration.getReporterConfiguration().isTrimStackTrace();
return SurefireReflector.createForkingReporterFactoryInCurrentClassLoader( trimStackTrace, originalSystemOut );
}
private static ScheduledExecutorService createJvmTerminator()
{
ThreadFactory threadFactory = newDaemonThreadFactory( "last-ditch-daemon-shutdown-thread-"
+ SYSTEM_EXIT_TIMEOUT_IN_SECONDS
+ "sec" );
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( 1, threadFactory );
executor.setMaximumPoolSize( 1 );
executor.prestartCoreThread();
return executor;
}
@SuppressWarnings( "checkstyle:emptyblock" )
private static void launchLastDitchDaemonShutdownThread( final int returnCode )
{
JVM_TERMINATOR.schedule( new Runnable()
{
public void run()
{
Runtime.getRuntime().halt( returnCode );
}
}, SYSTEM_EXIT_TIMEOUT_IN_SECONDS, SECONDS );
}
private static RunResult invokeProviderInSameClassLoader( Object testSet, Object factory,
ProviderConfiguration providerConfiguration,
boolean insideFork,
StartupConfiguration startupConfig,
boolean restoreStreams )
throws TestSetFailedException, InvocationTargetException
{
final PrintStream orgSystemOut = System.out;
final PrintStream orgSystemErr = System.err;
// Note that System.out/System.err are also read in the "ReporterConfiguration" instatiation
// in createProvider below. These are the same values as here.
try
{
return createProviderInCurrentClassloader( startupConfig, insideFork, providerConfiguration, factory )
.invoke( testSet );
}
finally
{
if ( restoreStreams && System.getSecurityManager() == null )
{
System.setOut( orgSystemOut );
System.setErr( orgSystemErr );
}
}
}
private static SurefireProvider createProviderInCurrentClassloader( StartupConfiguration startupConfiguration1,
boolean isInsideFork,
ProviderConfiguration providerConfiguration,
Object reporterManagerFactory1 )
{
BaseProviderFactory bpf = new BaseProviderFactory( (ReporterFactory) reporterManagerFactory1, isInsideFork );
bpf.setTestRequest( providerConfiguration.getTestSuiteDefinition() );
bpf.setReporterConfiguration( providerConfiguration.getReporterConfiguration() );
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
bpf.setClassLoaders( classLoader );
bpf.setTestArtifactInfo( providerConfiguration.getTestArtifact() );
bpf.setProviderProperties( providerConfiguration.getProviderProperties() );
bpf.setRunOrderParameters( providerConfiguration.getRunOrderParameters() );
bpf.setDirectoryScannerParameters( providerConfiguration.getDirScannerParams() );
bpf.setMainCliOptions( providerConfiguration.getMainCliOptions() );
bpf.setSkipAfterFailureCount( providerConfiguration.getSkipAfterFailureCount() );
bpf.setShutdown( providerConfiguration.getShutdown() );
String providerClass = startupConfiguration1.getActualClassName();
return (SurefireProvider) ReflectionUtils.instantiateOneArg( classLoader, providerClass, ProviderParameters.class, bpf );
}
}
最关键的方法是上述代码中: private static SurefireProvider createProviderInCurrentClassloader(StartupConfiguration startupConfiguration1, boolean isInsideFork, ProviderConfiguration providerConfiguration, Object reporterManagerFactory1)
{
BaseProviderFactory bpf = new BaseProviderFactory((ReporterFactory)reporterManagerFactory1, isInsideFork);
bpf.setTestRequest(providerConfiguration.getTestSuiteDefinition());
bpf.setReporterConfiguration(providerConfiguration.getReporterConfiguration());
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
bpf.setClassLoaders(classLoader);
bpf.setTestArtifactInfo(providerConfiguration.getTestArtifact());
bpf.setProviderProperties(providerConfiguration.getProviderProperties());
bpf.setRunOrderParameters(providerConfiguration.getRunOrderParameters());
bpf.setDirectoryScannerParameters(providerConfiguration.getDirScannerParams());
bpf.setMainCliOptions(providerConfiguration.getMainCliOptions());
bpf.setSkipAfterFailureCount(providerConfiguration.getSkipAfterFailureCount());
bpf.setShutdown(providerConfiguration.getShutdown());
String providerClass = startupConfiguration1.getActualClassName();
return (SurefireProvider)ReflectionUtils.instantiateOneArg(classLoader, providerClass, ProviderParameters.class, bpf);
}
ReflectionUtils 来自 surefire-api.jar(2.19.1) 代码:
public static Object instantiateOneArg(ClassLoader classLoader, String className, Class<?> param1Class, Object param1)
{
try
{
Class<?> aClass = loadClass(classLoader, className);
Constructor constructor = getConstructor(aClass, new Class[] { param1Class });
return constructor.newInstance(new Object[] { param1 });
}
catch (InvocationTargetException e)
{
throw new SurefireReflectionException(e.getTargetException());
}
catch (InstantiationException e)
{
throw new SurefireReflectionException(e);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
public static Object instantiateTwoArgs(ClassLoader classLoader, String className, Class<?> param1Class, Object param1, Class param2Class, Object param2)
{
try
{
Class<?> aClass = loadClass(classLoader, className);
Constructor constructor = getConstructor(aClass, new Class[] { param1Class, param2Class });
return constructor.newInstance(new Object[] { param1, param2 });
}
catch (InvocationTargetException e)
{
throw new SurefireReflectionException(e.getTargetException());
}
catch (InstantiationException e)
{
throw new SurefireReflectionException(e);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
public static void invokeSetter(Object o, String name, Class<?> value1clazz, Object value)
{
Method setter = getMethod(o, name, new Class[] { value1clazz });
invokeSetter(o, setter, value);
}
public static Object invokeSetter(Object target, Method method, Object value)
{
return invokeMethodWithArray(target, method, new Object[] { value });
}
public static Object invokeMethodWithArray(Object target, Method method, Object... args)
{
try
{
return method.invoke(target, args);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
catch (InvocationTargetException e)
{
throw new SurefireReflectionException(e.getTargetException());
}
}
public static Object invokeMethodWithArray2(Object target, Method method, Object... args)
throws InvocationTargetException
{
try
{
return method.invoke(target, args);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
public static Object instantiateObject(String className, Class[] types, Object[] params, ClassLoader classLoader)
{
Class<?> clazz = loadClass(classLoader, className);
Constructor constructor = getConstructor(clazz, types);
return newInstance(constructor, params);
}
public static Class<?> tryLoadClass(ClassLoader classLoader, String className)
{
try
{
return classLoader.loadClass(className);
}
catch (NoClassDefFoundError localNoClassDefFoundError) {}catch (ClassNotFoundException localClassNotFoundException) {}
return null;
}
public static Class<?> loadClass(ClassLoader classLoader, String className)
{
try
{
return classLoader.loadClass(className);
}
catch (NoClassDefFoundError e)
{
throw new SurefireReflectionException(e);
}
catch (ClassNotFoundException e)
{
throw new SurefireReflectionException(e);
}
}
public static Object newInstance(Constructor constructor, Object... params)
{
try
{
return constructor.newInstance(params);
}
catch (InvocationTargetException e)
{
throw new SurefireReflectionException(e);
}
catch (InstantiationException e)
{
throw new SurefireReflectionException(e);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
public static <T> T instantiate(ClassLoader classLoader, String classname, Class<T> returnType)
{
try
{
Class<?> clazz = loadClass(classLoader, classname);
return returnType.cast(clazz.newInstance());
}
catch (InstantiationException e)
{
throw new SurefireReflectionException(e);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
public static Object instantiateOneArg(ClassLoader classLoader, String className, Class<?> param1Class, Object param1)
{
try
{
Class<?> aClass = loadClass(classLoader, className);
Constructor constructor = getConstructor(aClass, new Class[] { param1Class });
return constructor.newInstance(new Object[] { param1 });
}
catch (InvocationTargetException e)
{
throw new SurefireReflectionException(e.getTargetException());
}
catch (InstantiationException e)
{
throw new SurefireReflectionException(e);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
传入的是SurefireProvider 类型,其实它是接口,代码如下:
public abstract interface SurefireProvider
{
public abstract Iterable<Class<?>> getSuites();
public abstract RunResult invoke(Object paramObject)
throws TestSetFailedException, ReporterException, InvocationTargetException;
public abstract void cancel();
}
还有一个很关键的接口扩展 了SurefireProvider ,它就是AbstractProvider 接口:
public abstract class AbstractProvider
implements SurefireProvider
{
private final Thread creatingThread = Thread.currentThread();
public void cancel()
{
synchronized (this.creatingThread)
{
if (this.creatingThread.isAlive()) {
this.creatingThread.interrupt();
}
}
}
}
然后 surefire-testng.jar 中TestNGProvider代码:
public class TestNGProvider
extends AbstractProvider
{
private final Map<String, String> providerProperties;
private final ReporterConfiguration reporterConfiguration;
private final ClassLoader testClassLoader;
private final ScanResult scanResult;
private final TestRequest testRequest;
private final ProviderParameters providerParameters;
private final RunOrderCalculator runOrderCalculator;
private final List<CommandLineOption> mainCliOptions;
private final CommandReader commandsReader;
private TestsToRun testsToRun;
public TestNGProvider( ProviderParameters bootParams )
{
// don't start a thread in CommandReader while we are in in-plugin process
commandsReader = bootParams.isInsideFork() ? getReader().setShutdown( bootParams.getShutdown() ) : null;
providerParameters = bootParams;
testClassLoader = bootParams.getTestClassLoader();
runOrderCalculator = bootParams.getRunOrderCalculator();
providerProperties = bootParams.getProviderProperties();
testRequest = bootParams.getTestRequest();
reporterConfiguration = bootParams.getReporterConfiguration();
scanResult = bootParams.getScanResult();
mainCliOptions = bootParams.getMainCliOptions();
}
public RunResult invoke( Object forkTestSet )
throws TestSetFailedException
{
if ( isFailFast() && commandsReader != null )
{
registerPleaseStopListener();
}
final ReporterFactory reporterFactory = providerParameters.getReporterFactory();
final RunListener reporter = reporterFactory.createReporter();
/**
* {@link org.apache.maven.surefire.report.ConsoleOutputCapture#startCapture(ConsoleOutputReceiver)}
* called in prior to initializing variable {@link #testsToRun}
*/
startCapture( (ConsoleOutputReceiver) reporter );
RunResult runResult;
try
{
if ( isTestNGXmlTestSuite( testRequest ) )
{
if ( commandsReader != null )
{
commandsReader.awaitStarted();
}
TestNGXmlTestSuite testNGXmlTestSuite = newXmlSuite();
testNGXmlTestSuite.locateTestSets();
testNGXmlTestSuite.execute( reporter );
}
else
{
if ( testsToRun == null )
{
if ( forkTestSet instanceof TestsToRun )
{
testsToRun = (TestsToRun) forkTestSet;
}
else if ( forkTestSet instanceof Class )
{
testsToRun = fromClass( (Class<?>) forkTestSet );
}
else
{
testsToRun = scanClassPath();
}
}
if ( commandsReader != null )
{
registerShutdownListener( testsToRun );
commandsReader.awaitStarted();
}
TestNGDirectoryTestSuite suite = newDirectorySuite();
suite.execute( testsToRun, reporter );
}
}
finally
{
runResult = reporterFactory.close();
}
return runResult;
}
boolean isTestNGXmlTestSuite( TestRequest testSuiteDefinition )
{
Collection<File> suiteXmlFiles = testSuiteDefinition.getSuiteXmlFiles();
return !suiteXmlFiles.isEmpty() && !hasSpecificTests();
}
private boolean isFailFast()
{
return providerParameters.getSkipAfterFailureCount() > 0;
}
private int getSkipAfterFailureCount()
{
return isFailFast() ? providerParameters.getSkipAfterFailureCount() : 0;
}
private void registerShutdownListener( final TestsToRun testsToRun )
{
commandsReader.addShutdownListener( new CommandListener()
{
public void update( Command command )
{
testsToRun.markTestSetFinished();
}
} );
}
private void registerPleaseStopListener()
{
commandsReader.addSkipNextTestsListener( new CommandListener()
{
public void update( Command command )
{
FailFastEventsSingleton.getInstance().setSkipOnNextTest();
}
} );
}
private TestNGDirectoryTestSuite newDirectorySuite()
{
return new TestNGDirectoryTestSuite( testRequest.getTestSourceDirectory().toString(), providerProperties,
reporterConfiguration.getReportsDirectory(), getTestFilter(),
mainCliOptions, getSkipAfterFailureCount() );
}
private TestNGXmlTestSuite newXmlSuite()
{
return new TestNGXmlTestSuite( testRequest.getSuiteXmlFiles(),
testRequest.getTestSourceDirectory().toString(),
providerProperties,
reporterConfiguration.getReportsDirectory(), getSkipAfterFailureCount() );
}
public Iterable<Class<?>> getSuites()
{
if ( isTestNGXmlTestSuite( testRequest ) )
{
return Collections.emptySet();
}
else
{
testsToRun = scanClassPath();
return testsToRun;
}
}
private TestsToRun scanClassPath()
{
final TestsToRun scanned = scanResult.applyFilter( null, testClassLoader );
return runOrderCalculator.orderTestClasses( scanned );
}
private boolean hasSpecificTests()
{
TestListResolver specificTestPatterns = testRequest.getTestListResolver();
return !specificTestPatterns.isEmpty() && !specificTestPatterns.isWildcard();
}
private TestListResolver getTestFilter()
{
TestListResolver filter = optionallyWildcardFilter( testRequest.getTestListResolver() );
return filter.isWildcard() ? getEmptyTestListResolver() : filter;
}
}
ProviderParameters接口代码:public abstract interface ProviderParameters
{
/**
* @deprecated
*/
public abstract DirectoryScanner getDirectoryScanner();
public abstract ScanResult getScanResult();
public abstract RunOrderCalculator getRunOrderCalculator();
public abstract ReporterFactory getReporterFactory();
public abstract ConsoleLogger getConsoleLogger();
@Deprecated
public abstract DirectoryScannerParameters getDirectoryScannerParameters();
public abstract ReporterConfiguration getReporterConfiguration();
public abstract TestRequest getTestRequest();
public abstract ClassLoader getTestClassLoader();
public abstract Map<String, String> getProviderProperties();
public abstract TestArtifactInfo getTestArtifactInfo();
public abstract List<CommandLineOption> getMainCliOptions();
public abstract int getSkipAfterFailureCount();
public abstract boolean isInsideFork();
public abstract Shutdown getShutdown();
ScanResult接口代码:public abstract interface ScanResult
{
public abstract int size();
public abstract String getClassName(int paramInt);
public abstract TestsToRun applyFilter(ScannerFilter paramScannerFilter, ClassLoader paramClassLoader);
public abstract List<Class<?>> getClassesSkippedByValidation(ScannerFilter paramScannerFilter, ClassLoader paramClassLoader);
public abstract void writeTo(Map<String, String> paramMap);
}
TestsToRun代码:
public class TestsToRun
implements Iterable<Class<?>>
{
private final List<Class<?>> locatedClasses;
private volatile boolean finished;
private int iteratedCount;
public TestsToRun(Set<Class<?>> locatedClasses)
{
this.locatedClasses = new ArrayList(locatedClasses);
}
public static TestsToRun fromClass(Class<?> clazz)
throws TestSetFailedException
{
return new TestsToRun(Collections.singleton(clazz));
}
public Iterator<Class<?>> iterated()
{
return newWeakIterator();
}
public Iterator<Class<?>> iterator()
{
return new ClassesIterator(null);
}
private final class ClassesIterator
extends CloseableIterator<Class<?>>
{
private final Iterator<Class<?>> it = TestsToRun.this.locatedClasses.iterator();
private int iteratedCount;
private ClassesIterator() {}
protected boolean isClosed()
{
return TestsToRun.this.isFinished();
}
protected boolean doHasNext()
{
return this.it.hasNext();
}
protected Class<?> doNext()
{
Class<?> nextTest = (Class)this.it.next();
TestsToRun.this.iteratedCount = Math.max(++this.iteratedCount, TestsToRun.this.iteratedCount);
return nextTest;
}
protected void doRemove() {}
public void remove()
{
throw new UnsupportedOperationException("unsupported remove");
}
}
public final void markTestSetFinished()
{
this.finished = true;
}
public final boolean isFinished()
{
return this.finished;
}
public String toString()
{
StringBuilder sb = new StringBuilder("TestsToRun: [");
for (Class<?> clazz : this) {
sb.append(' ').append(clazz.getName());
}
sb.append(']');
return sb.toString();
}
public boolean containsAtLeast(int atLeast)
{
return containsAtLeast(iterator(), atLeast);
}
private boolean containsAtLeast(Iterator<Class<?>> it, int atLeast)
{
for (int i = 0; i < atLeast; i++)
{
if (!it.hasNext()) {
return false;
}
it.next();
}
return true;
}
public boolean containsExactly(int items)
{
Iterator<Class<?>> it = iterator();
return (containsAtLeast(it, items)) && (!it.hasNext());
}
public boolean allowEagerReading()
{
return true;
}
public Class<?>[] getLocatedClasses()
{
if (!allowEagerReading()) {
throw new IllegalStateException("Cannot eagerly read");
}
Collection<Class<?>> result = new ArrayList();
for (Class<?> clazz : this) {
result.add(clazz);
}
return (Class[])result.toArray(new Class[result.size()]);
}
public Class<?> getClassByName(String className)
{
for (Class<?> clazz : this) {
if (clazz.getName().equals(className)) {
return clazz;
}
}
return null;
}
private Iterator<Class<?>> newWeakIterator()
{
final Iterator<Class<?>> it = this.locatedClasses.subList(0, this.iteratedCount).iterator();
new CloseableIterator()
{
protected boolean isClosed()
{
return TestsToRun.this.isFinished();
}
protected boolean doHasNext()
{
return it.hasNext();
}
protected Class<?> doNext()
{
return (Class)it.next();
}
protected void doRemove() {}
public void remove()
{
throw new UnsupportedOperationException("unsupported remove");
}
};
}
}
TestNGXmlTestSuite类代码:
import static org.apache.maven.surefire.testng.TestNGExecutor.run;
/**
* Handles suite xml file definitions for TestNG.
*
* @author jkuhnert
* @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>
*/
final class TestNGXmlTestSuite
extends TestSuite
{
private final List<File> suiteFiles;
private List<String> suiteFilePaths;
private final String testSourceDirectory;
private final Map<String, String> options;
private final File reportsDirectory;
private final int skipAfterFailureCount;
/**
* Creates a testng testset to be configured by the specified
* xml file(s). The XML files are suite definitions files according to TestNG DTD.
*/
TestNGXmlTestSuite( List<File> suiteFiles, String testSourceDirectory, Map<String, String> confOptions,
File reportsDirectory, int skipAfterFailureCount )
{
this.suiteFiles = suiteFiles;
this.options = confOptions;
this.testSourceDirectory = testSourceDirectory;
this.reportsDirectory = reportsDirectory;
this.skipAfterFailureCount = skipAfterFailureCount;
}
void execute( RunListener reporter )
throws TestSetFailedException
{
if ( suiteFilePaths == null )
{
throw new IllegalStateException( "You must call locateTestSets before calling execute" );
}
startTestSuite( reporter );
run( suiteFilePaths, testSourceDirectory, options, reporter, reportsDirectory, skipAfterFailureCount );
finishTestSuite( reporter );
}
void locateTestSets()
throws TestSetFailedException
{
if ( suiteFilePaths != null )
{
throw new IllegalStateException( "You can't call locateTestSets twice" );
}
if ( suiteFiles.isEmpty() )
{
throw new IllegalStateException( "No suite files were specified" );
}
suiteFilePaths = new ArrayList<String>();
for ( File suiteFile : suiteFiles )
{
if ( !suiteFile.isFile() )
{
throw new TestSetFailedException( "Suite file " + suiteFile + " is not a valid file" );
}
suiteFilePaths.add( suiteFile.getAbsolutePath() );
}
}
@Override
Map<String, String> getOptions()
{
return options;
}
}
import static org.apache.maven.surefire.testng.TestNGExecutor.run; 直接执行TestNGExecutor.run
final class TestNGExecutor
{
/** The default name for a suite launched from the maven surefire plugin */
private static final String DEFAULT_SUREFIRE_SUITE_NAME = "Surefire suite";
/** The default name for a test launched from the maven surefire plugin */
private static final String DEFAULT_SUREFIRE_TEST_NAME = "Surefire test";
private static final boolean HAS_TEST_ANNOTATION_ON_CLASSPATH =
tryLoadClass( TestNGExecutor.class.getClassLoader(), "org.testng.annotations.Test" ) != null;
private TestNGExecutor()
{
throw new IllegalStateException( "not instantiable constructor" );
}
static void run( Iterable<Class<?>> testClasses, String testSourceDirectory,
Map<String, String> options, // string,string because TestNGMapConfigurator#configure()
RunListener reportManager, File reportsDirectory,
TestListResolver methodFilter, List<CommandLineOption> mainCliOptions,
int skipAfterFailureCount )
throws TestSetFailedException
{
TestNG testng = new TestNG( true );
Configurator configurator = getConfigurator( options.get( "testng.configurator" ) );
if ( isCliDebugOrShowErrors( mainCliOptions ) )
{
System.out.println( "Configuring TestNG with: " + configurator.getClass().getSimpleName() );
}
XmlMethodSelector groupMatchingSelector = createGroupMatchingSelector( options );
XmlMethodSelector methodNameFilteringSelector = createMethodNameFilteringSelector( methodFilter );
Map<String, SuiteAndNamedTests> suitesNames = new HashMap<String, SuiteAndNamedTests>();
List<XmlSuite> xmlSuites = new ArrayList<XmlSuite>();
for ( Class<?> testClass : testClasses )
{
TestMetadata metadata = findTestMetadata( testClass );
SuiteAndNamedTests suiteAndNamedTests = suitesNames.get( metadata.suiteName );
if ( suiteAndNamedTests == null )
{
suiteAndNamedTests = new SuiteAndNamedTests();
suiteAndNamedTests.xmlSuite.setName( metadata.suiteName );
configurator.configure( suiteAndNamedTests.xmlSuite, options );
xmlSuites.add( suiteAndNamedTests.xmlSuite );
suitesNames.put( metadata.suiteName, suiteAndNamedTests );
}
XmlTest xmlTest = suiteAndNamedTests.testNameToTest.get( metadata.testName );
if ( xmlTest == null )
{
xmlTest = new XmlTest( suiteAndNamedTests.xmlSuite );
xmlTest.setName( metadata.testName );
addSelector( xmlTest, groupMatchingSelector );
addSelector( xmlTest, methodNameFilteringSelector );
xmlTest.setXmlClasses( new ArrayList<XmlClass>() );
suiteAndNamedTests.testNameToTest.put( metadata.testName, xmlTest );
}
xmlTest.getXmlClasses().add( new XmlClass( testClass.getName() ) );
}
testng.setXmlSuites( xmlSuites );
configurator.configure( testng, options );
postConfigure( testng, testSourceDirectory, reportManager, reportsDirectory, skipAfterFailureCount,
extractVerboseLevel( options ) );
testng.run();
}
private static boolean isCliDebugOrShowErrors( List<CommandLineOption> mainCliOptions )
{
return mainCliOptions.contains( CommandLineOption.LOGGING_LEVEL_DEBUG )
|| mainCliOptions.contains( CommandLineOption.SHOW_ERRORS );
}
private static TestMetadata findTestMetadata( Class<?> testClass )
{
TestMetadata result = new TestMetadata();
if ( HAS_TEST_ANNOTATION_ON_CLASSPATH )
{
Test testAnnotation = findAnnotation( testClass, Test.class );
if ( null != testAnnotation )
{
if ( !StringUtils.isBlank( testAnnotation.suiteName() ) )
{
result.suiteName = testAnnotation.suiteName();
}
if ( !StringUtils.isBlank( testAnnotation.testName() ) )
{
result.testName = testAnnotation.testName();
}
}
}
return result;
}
private static <T extends Annotation> T findAnnotation( Class<?> clazz, Class<T> annotationType )
{
if ( clazz == null )
{
return null;
}
T result = clazz.getAnnotation( annotationType );
if ( result != null )
{
return result;
}
return findAnnotation( clazz.getSuperclass(), annotationType );
}
private static class TestMetadata
{
private String testName = DEFAULT_SUREFIRE_TEST_NAME;
private String suiteName = DEFAULT_SUREFIRE_SUITE_NAME;
}
private static class SuiteAndNamedTests
{
private XmlSuite xmlSuite = new XmlSuite();
private Map<String, XmlTest> testNameToTest = new HashMap<String, XmlTest>();
}
private static void addSelector( XmlTest xmlTest, XmlMethodSelector selector )
{
if ( selector != null )
{
xmlTest.getMethodSelectors().add( selector );
}
}
@SuppressWarnings( "checkstyle:magicnumber" )
private static XmlMethodSelector createMethodNameFilteringSelector( TestListResolver methodFilter )
throws TestSetFailedException
{
if ( methodFilter != null && !methodFilter.isEmpty() )
{
// the class is available in the testClassPath
String clazzName = "org.apache.maven.surefire.testng.utils.MethodSelector";
try
{
Class<?> clazz = Class.forName( clazzName );
Method method = clazz.getMethod( "setTestListResolver", TestListResolver.class );
method.invoke( null, methodFilter );
}
catch ( Exception e )
{
throw new TestSetFailedException( e.getMessage(), e );
}
XmlMethodSelector xms = new XmlMethodSelector();
xms.setName( clazzName );
// looks to need a high value
xms.setPriority( 10000 );
return xms;
}
else
{
return null;
}
}
@SuppressWarnings( "checkstyle:magicnumber" )
private static XmlMethodSelector createGroupMatchingSelector( Map<String, String> options )
throws TestSetFailedException
{
final String groups = options.get( ProviderParameterNames.TESTNG_GROUPS_PROP );
final String excludedGroups = options.get( ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP );
if ( groups == null && excludedGroups == null )
{
return null;
}
// the class is available in the testClassPath
final String clazzName = "org.apache.maven.surefire.testng.utils.GroupMatcherMethodSelector";
try
{
Class<?> clazz = Class.forName( clazzName );
// HORRIBLE hack, but TNG doesn't allow us to setup a method selector instance directly.
Method method = clazz.getMethod( "setGroups", String.class, String.class );
method.invoke( null, groups, excludedGroups );
}
catch ( Exception e )
{
throw new TestSetFailedException( e.getMessage(), e );
}
XmlMethodSelector xms = new XmlMethodSelector();
xms.setName( clazzName );
// looks to need a high value
xms.setPriority( 9999 );
return xms;
}
static void run( List<String> suiteFiles, String testSourceDirectory,
Map<String, String> options, // string,string because TestNGMapConfigurator#configure()
RunListener reportManager, File reportsDirectory, int skipAfterFailureCount )
throws TestSetFailedException
{
TestNG testng = new TestNG( true );
Configurator configurator = getConfigurator( options.get( "testng.configurator" ) );
configurator.configure( testng, options );
postConfigure( testng, testSourceDirectory, reportManager, reportsDirectory, skipAfterFailureCount,
extractVerboseLevel( options ) );
testng.setTestSuites( suiteFiles );
testng.run();
}
private static Configurator getConfigurator( String className )
{
try
{
return (Configurator) Class.forName( className ).newInstance();
}
catch ( InstantiationException e )
{
throw new RuntimeException( e );
}
catch ( IllegalAccessException e )
{
throw new RuntimeException( e );
}
catch ( ClassNotFoundException e )
{
throw new RuntimeException( e );
}
}
private static void postConfigure( TestNG testNG, String sourcePath, final RunListener reportManager,
File reportsDirectory, int skipAfterFailureCount, int verboseLevel )
{
// 0 (default): turn off all TestNG output
testNG.setVerbose( verboseLevel );
TestNGReporter reporter = createTestNGReporter( reportManager );
testNG.addListener( (Object) reporter );
if ( skipAfterFailureCount > 0 )
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
testNG.addListener( instantiate( cl, "org.apache.maven.surefire.testng.utils.FailFastNotifier",
Object.class ) );
testNG.addListener( new FailFastListener( createStoppable( reportManager, skipAfterFailureCount ) ) );
}
// FIXME: use classifier to decide if we need to pass along the source dir (only for JDK14)
if ( sourcePath != null )
{
testNG.setSourcePath( sourcePath );
}
testNG.setOutputDirectory( reportsDirectory.getAbsolutePath() );
}
private static Stoppable createStoppable( final RunListener reportManager, int skipAfterFailureCount )
{
final AtomicInteger currentFaultCount = new AtomicInteger( skipAfterFailureCount );
return new Stoppable()
{
public void fireStopEvent()
{
if ( countDownToZero( currentFaultCount ) )
{
FailFastEventsSingleton.getInstance().setSkipOnNextTest();
}
reportManager.testExecutionSkippedByUser();
}
};
}
// If we have access to IResultListener, return a ConfigurationAwareTestNGReporter
// But don't cause NoClassDefFoundErrors if it isn't available; just return a regular TestNGReporter instead
private static TestNGReporter createTestNGReporter( RunListener reportManager )
{
try
{
Class.forName( "org.testng.internal.IResultListener" );
Class c = Class.forName( "org.apache.maven.surefire.testng.ConfigurationAwareTestNGReporter" );
@SuppressWarnings( "unchecked" ) Constructor<?> ctor = c.getConstructor( RunListener.class );
return (TestNGReporter) ctor.newInstance( reportManager );
}
catch ( InvocationTargetException e )
{
throw new RuntimeException( "Bug in ConfigurationAwareTestNGReporter", e.getCause() );
}
catch ( ClassNotFoundException e )
{
return new TestNGReporter( reportManager );
}
catch ( Exception e )
{
throw new RuntimeException( "Bug in ConfigurationAwareTestNGReporter", e );
}
}
private static int extractVerboseLevel( Map<String, String> options )
throws TestSetFailedException
{
try
{
String verbose = options.get( "surefire.testng.verbose" );
return verbose == null ? 0 : Integer.parseInt( verbose );
}
catch ( NumberFormatException e )
{
throw new TestSetFailedException( "Provider property 'surefire.testng.verbose' should refer to "
+ "number -1 (debug mode), 0, 1 .. 10 (most detailed).", e );
}
}
}
run 方法中直接调用
TestNG testng = new TestNG( true );
本文分析了基于Surefire 2.19.1版本的单元测试执行流程及异常情况,详细解析了Surefire如何启动TestNG进行测试,并深入探讨了测试执行过程中遇到的非法类格式异常。
998

被折叠的 条评论
为什么被折叠?



