org.testng.TestRunner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qaf Show documentation
Show all versions of qaf Show documentation
Functional test automation framework for web, mobile-web, mobile native and web-service
/*******************************************************************************
* QMetry Automation Framework provides a powerful and versatile platform to author
* Automated Test Cases in Behavior Driven, Keyword Driven or Code Driven approach
*
* Copyright 2016 Infostretch Corporation
*
* THIS IS A MODIFIED SOURCE VERSION OF ORIGINAL TESTNG COMPONENT (REFER LICENSING TERMS FOR TESTNG: http://www.apache.org/licenses/LICENSE-2.0.html
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
*
* You should have received a copy of the GNU General Public License along with this program in the name of LICENSE.txt in the root folder of the distribution. If not, see https://opensource.org/licenses/gpl-3.0.html
*
* See the NOTICE.TXT file in root folder of this source files distribution
* for additional information regarding copyright ownership and licenses
* of other open source software / files used by QMetry Automation Framework.
*
* For any inquiry or need additional information, please contact [email protected]
*******************************************************************************/
package org.testng;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.testng.annotations.Guice;
import org.testng.annotations.IListenersAnnotation;
import org.testng.collections.ListMultiMap;
import org.testng.collections.Lists;
import org.testng.collections.Maps;
import org.testng.collections.Sets;
import org.testng.internal.Attributes;
import org.testng.internal.ClassHelper;
import org.testng.internal.ClassImpl;
import org.testng.internal.ClassInfoMap;
import org.testng.internal.ConfigurationGroupMethods;
import org.testng.internal.Constants;
import org.testng.internal.DynamicGraph;
import org.testng.internal.DynamicGraph.Status;
import org.testng.internal.IConfiguration;
import org.testng.internal.IInvoker;
import org.testng.internal.ITestResultNotifier;
import org.testng.internal.InvokedMethod;
import org.testng.internal.Invoker;
import org.testng.internal.MethodGroupsHelper;
import org.testng.internal.MethodHelper;
import org.testng.internal.MethodInstance;
import org.testng.internal.ResultMap;
import org.testng.internal.RunInfo;
import org.testng.internal.TestMethodWorker;
import org.testng.internal.TestNGClassFinder;
import org.testng.internal.TestNGMethodFinder;
import org.testng.internal.Utils;
import org.testng.internal.XmlMethodSelector;
import org.testng.internal.annotations.AnnotationHelper;
import org.testng.internal.annotations.IAnnotationFinder;
import org.testng.internal.thread.graph.GraphThreadPoolExecutor;
import org.testng.internal.thread.graph.IThreadWorkerFactory;
import org.testng.internal.thread.graph.IWorker;
import org.testng.junit.IJUnitTestRunner;
import org.testng.xml.XmlClass;
import org.testng.xml.XmlInclude;
import org.testng.xml.XmlPackage;
import org.testng.xml.XmlSuite;
import org.testng.xml.XmlTest;
import com.google.inject.Injector;
import com.google.inject.Module;
/**
* This class takes care of running one Test.
*
* @author Cedric Beust, Apr 26, 2004
*/
public class TestRunner
implements ITestContext, ITestResultNotifier, IThreadWorkerFactory
{
/* generated */
private static final long serialVersionUID = 4247820024988306670L;
private ISuite m_suite;
private XmlTest m_xmlTest;
private String m_testName;
transient private List m_testClassesFromXml= null;
transient private List m_packageNamesFromXml= null;
transient private IInvoker m_invoker= null;
transient private IAnnotationFinder m_annotationFinder= null;
/** ITestListeners support. */
transient private List m_testListeners = Lists.newArrayList();
transient private Set m_configurationListeners = Sets.newHashSet();
transient private IConfigurationListener m_confListener= new ConfigurationListener();
transient private boolean m_skipFailedInvocationCounts;
transient private Collection m_invokedMethodListeners = Lists.newArrayList();
transient private final Map, IClassListener> m_classListeners = Maps.newHashMap();
/**
* All the test methods we found, associated with their respective classes.
* Note that these test methods might belong to different classes.
* We pick which ones to run at runtime.
*/
private ITestNGMethod[] m_allTestMethods = new ITestNGMethod[0];
// Information about this test run
private Date m_startDate = null;
private Date m_endDate = null;
/** A map to keep track of Class <-> IClass. */
transient private Map, ITestClass> m_classMap = Maps.newLinkedHashMap();
/** Where the reports will be created. */
private String m_outputDirectory= Constants.getDefaultValueFor(Constants.PROP_OUTPUT_DIR);
// The XML method selector (groups/methods included/excluded in XML)
private XmlMethodSelector m_xmlMethodSelector = new XmlMethodSelector();
private static int m_verbose = 1;
//
// These next fields contain all the configuration methods found on this class.
// At initialization time, they just contain all the various @Configuration methods
// found in all the classes we are going to run. When comes the time to run them,
// only a subset of them are run: those that are enabled and belong on the same class as
// (or a parent of) the test class.
//
/** */
private ITestNGMethod[] m_beforeSuiteMethods = {};
private ITestNGMethod[] m_afterSuiteMethods = {};
private ITestNGMethod[] m_beforeXmlTestMethods = {};
private ITestNGMethod[] m_afterXmlTestMethods = {};
private List m_excludedMethods = Lists.newArrayList();
private ConfigurationGroupMethods m_groupMethods = null;
// Meta groups
private Map> m_metaGroups = Maps.newHashMap();
// All the tests that were run along with their result
private IResultMap m_passedTests = new ResultMap();
private IResultMap m_failedTests = new ResultMap();
private IResultMap m_failedButWithinSuccessPercentageTests = new ResultMap();
private IResultMap m_skippedTests = new ResultMap();
private RunInfo m_runInfo= new RunInfo();
// The host where this test was run, or null if run locally
private String m_host;
// Defined dynamically depending on
transient private List m_methodInterceptors;
private transient ClassMethodMap m_classMethodMap;
private transient TestNGClassFinder m_testClassFinder;
private transient IConfiguration m_configuration;
private IMethodInterceptor builtinInterceptor;
private enum PriorityWeight {
groupByInstance, preserveOrder, priority, dependsOnGroups, dependsOnMethods
}
protected TestRunner(IConfiguration configuration,
ISuite suite,
XmlTest test,
String outputDirectory,
IAnnotationFinder finder,
boolean skipFailedInvocationCounts,
Collection invokedMethodListeners,
List classListeners)
{
init(configuration, suite, test, outputDirectory, finder, skipFailedInvocationCounts,
invokedMethodListeners, classListeners);
}
public TestRunner(IConfiguration configuration, ISuite suite, XmlTest test,
boolean skipFailedInvocationCounts,
Collection invokedMethodListeners,
List classListeners) {
init(configuration, suite, test, suite.getOutputDirectory(),
suite.getAnnotationFinder(),
skipFailedInvocationCounts, invokedMethodListeners, classListeners);
}
private void init(IConfiguration configuration,
ISuite suite,
XmlTest test,
String outputDirectory,
IAnnotationFinder annotationFinder,
boolean skipFailedInvocationCounts,
Collection invokedMethodListeners,
List classListeners)
{
m_configuration = configuration;
m_xmlTest= test;
m_suite = suite;
m_testName = test.getName();
m_host = suite.getHost();
m_testClassesFromXml= test.getXmlClasses();
m_skipFailedInvocationCounts = skipFailedInvocationCounts;
setVerbose(test.getVerbose());
boolean preserveOrder = test.getPreserveOrder();
m_methodInterceptors = new ArrayList();
builtinInterceptor = preserveOrder ? new PreserveOrderMethodInterceptor() : new InstanceOrderingMethodInterceptor();
m_packageNamesFromXml= test.getXmlPackages();
if(null != m_packageNamesFromXml) {
for(XmlPackage xp: m_packageNamesFromXml) {
m_testClassesFromXml.addAll(xp.getXmlClasses());
}
}
m_annotationFinder= annotationFinder;
m_invokedMethodListeners = invokedMethodListeners;
m_classListeners.clear();
for (IClassListener classListener : classListeners) {
m_classListeners.put(classListener.getClass(), classListener);
}
m_invoker = new Invoker(m_configuration, this, this, m_suite.getSuiteState(),
m_skipFailedInvocationCounts, invokedMethodListeners, classListeners);
if (test.getParallel() != null) {
log(3, "Running the tests in '" + test.getName() + "' with parallel mode:" + test.getParallel());
}
setOutputDirectory(outputDirectory);
// Finish our initialization
init();
}
public IInvoker getInvoker() {
return m_invoker;
}
public ITestNGMethod[] getBeforeSuiteMethods() {
return m_beforeSuiteMethods;
}
public ITestNGMethod[] getAfterSuiteMethods() {
return m_afterSuiteMethods;
}
public ITestNGMethod[] getBeforeTestConfigurationMethods() {
return m_beforeXmlTestMethods;
}
public ITestNGMethod[] getAfterTestConfigurationMethods() {
return m_afterXmlTestMethods;
}
private void init() {
initMetaGroups(m_xmlTest);
initRunInfo(m_xmlTest);
// Init methods and class map
// JUnit behavior is different and doesn't need this initialization step
if(!m_xmlTest.isJUnit()) {
initMethods();
}
initListeners();
addConfigurationListener(m_confListener);
for (IConfigurationListener cl : m_configuration.getConfigurationListeners()) {
addConfigurationListener(cl);
}
}
private static class ListenerHolder {
private List> listenerClasses;
private Class extends ITestNGListenerFactory> listenerFactoryClass;
}
/**
* @return all the @Listeners annotations found in the current class and its
* superclasses.
*/
private ListenerHolder findAllListeners(Class> cls) {
ListenerHolder result = new ListenerHolder();
result.listenerClasses = Lists.newArrayList();
do {
IListenersAnnotation l = m_annotationFinder.findAnnotation(cls, IListenersAnnotation.class);
if (l != null) {
Class extends ITestNGListener>[] classes = l.getValue();
for (Class extends ITestNGListener> c : classes) {
result.listenerClasses.add(c);
if (ITestNGListenerFactory.class.isAssignableFrom(c)) {
if (result.listenerFactoryClass == null) {
result.listenerFactoryClass = (Class extends ITestNGListenerFactory>) c;
}
else {
throw new TestNGException("Found more than one class implementing" +
"ITestNGListenerFactory:" + c + " and " + result.listenerFactoryClass);
}
}
}
}
cls = cls.getSuperclass();
} while (cls != Object.class);
return result;
}
private void initListeners() {
//
// Find all the listener factories and collect all the listeners requested in a
// @Listeners annotation.
//
Set> listenerClasses = Sets.newHashSet();
Class extends ITestNGListenerFactory> listenerFactoryClass = null;
for (IClass cls : getTestClasses()) {
Class> realClass = cls.getRealClass();
ListenerHolder listenerHolder = findAllListeners(realClass);
if (listenerFactoryClass == null) {
listenerFactoryClass = listenerHolder.listenerFactoryClass;
}
listenerClasses.addAll(listenerHolder.listenerClasses);
}
//
// Now we have all the listeners collected from @Listeners and at most one
// listener factory collected from a class implementing ITestNGListenerFactory.
// Instantiate all the requested listeners.
//
ITestNGListenerFactory listenerFactory = null;
// If we found a test listener factory, instantiate it.
try {
if (m_testClassFinder != null) {
IClass ic = m_testClassFinder.getIClass(listenerFactoryClass);
if (ic != null) {
listenerFactory = (ITestNGListenerFactory) ic.getInstances(false)[0];
}
}
if (listenerFactory == null) {
listenerFactory = listenerFactoryClass != null ? listenerFactoryClass.newInstance() : null;
}
}
catch(Exception ex) {
throw new TestNGException("Couldn't instantiate the ITestNGListenerFactory: "
+ ex);
}
// Instantiate all the listeners
for (Class extends ITestNGListener> c : listenerClasses) {
if (IClassListener.class.isAssignableFrom(c) && m_classListeners.containsKey(c)) {
continue;
}
ITestNGListener listener = listenerFactory != null ? listenerFactory.createListener(c) : null;
if (listener == null) {
listener = ClassHelper.newInstance(c);
}
addListener(listener);
}
}
/**
* Initialize meta groups
*/
private void initMetaGroups(XmlTest xmlTest) {
Map> metaGroups = xmlTest.getMetaGroups();
for (Map.Entry> entry : metaGroups.entrySet()) {
addMetaGroup(entry.getKey(), entry.getValue());
}
}
private void initRunInfo(final XmlTest xmlTest) {
// Groups
m_xmlMethodSelector.setIncludedGroups(createGroups(m_xmlTest.getIncludedGroups()));
m_xmlMethodSelector.setExcludedGroups(createGroups(m_xmlTest.getExcludedGroups()));
m_xmlMethodSelector.setExpression(m_xmlTest.getExpression());
// Methods
m_xmlMethodSelector.setXmlClasses(m_xmlTest.getXmlClasses());
m_runInfo.addMethodSelector(m_xmlMethodSelector, 10);
// Add user-specified method selectors (only class selectors, we can ignore
// script selectors here)
if (null != xmlTest.getMethodSelectors()) {
for (org.testng.xml.XmlMethodSelector selector : xmlTest.getMethodSelectors()) {
if (selector.getClassName() != null) {
IMethodSelector s = ClassHelper.createSelector(selector);
m_runInfo.addMethodSelector(s, selector.getPriority());
}
}
}
}
private void initMethods() {
//
// Calculate all the methods we need to invoke
//
List beforeClassMethods = Lists.newArrayList();
List testMethods = Lists.newArrayList();
List afterClassMethods = Lists.newArrayList();
List beforeSuiteMethods = Lists.newArrayList();
List afterSuiteMethods = Lists.newArrayList();
List beforeXmlTestMethods = Lists.newArrayList();
List afterXmlTestMethods = Lists.newArrayList();
ClassInfoMap classMap = new ClassInfoMap(m_testClassesFromXml);
m_testClassFinder= new TestNGClassFinder(classMap,
m_xmlTest,
m_configuration,
this);
ITestMethodFinder testMethodFinder
= new TestNGMethodFinder(m_runInfo, m_annotationFinder);
m_runInfo.setTestMethods(testMethods);
//
// Initialize TestClasses
//
IClass[] classes = m_testClassFinder.findTestClasses();
for (IClass ic : classes) {
// Create TestClass
ITestClass tc = new TestClass(ic,
testMethodFinder,
m_annotationFinder,
m_runInfo,
m_xmlTest,
classMap.getXmlClass(ic.getRealClass()));
m_classMap.put(ic.getRealClass(), tc);
}
//
// Calculate groups methods
//
Map> beforeGroupMethods =
MethodGroupsHelper.findGroupsMethods(m_classMap.values(), true);
Map> afterGroupMethods =
MethodGroupsHelper.findGroupsMethods(m_classMap.values(), false);
//
// Walk through all the TestClasses, store their method
// and initialize them with the correct ITestClass
//
for (ITestClass tc : m_classMap.values()) {
fixMethodsWithClass(tc.getTestMethods(), tc, testMethods);
fixMethodsWithClass(tc.getBeforeClassMethods(), tc, beforeClassMethods);
fixMethodsWithClass(tc.getBeforeTestMethods(), tc, null);
fixMethodsWithClass(tc.getAfterTestMethods(), tc, null);
fixMethodsWithClass(tc.getAfterClassMethods(), tc, afterClassMethods);
fixMethodsWithClass(tc.getBeforeSuiteMethods(), tc, beforeSuiteMethods);
fixMethodsWithClass(tc.getAfterSuiteMethods(), tc, afterSuiteMethods);
fixMethodsWithClass(tc.getBeforeTestConfigurationMethods(), tc, beforeXmlTestMethods);
fixMethodsWithClass(tc.getAfterTestConfigurationMethods(), tc, afterXmlTestMethods);
fixMethodsWithClass(tc.getBeforeGroupsMethods(), tc,
MethodHelper.uniqueMethodList(beforeGroupMethods.values()));
fixMethodsWithClass(tc.getAfterGroupsMethods(), tc,
MethodHelper.uniqueMethodList(afterGroupMethods.values()));
}
//
// Sort the methods
//
m_beforeSuiteMethods = MethodHelper.collectAndOrderMethods(beforeSuiteMethods,
false /* forTests */,
m_runInfo,
m_annotationFinder,
true /* unique */,
m_excludedMethods);
m_beforeXmlTestMethods = MethodHelper.collectAndOrderMethods(beforeXmlTestMethods,
false /* forTests */,
m_runInfo,
m_annotationFinder,
true /* unique (CQ added by me)*/,
m_excludedMethods);
m_allTestMethods = MethodHelper.collectAndOrderMethods(testMethods,
true /* forTest? */,
m_runInfo,
m_annotationFinder,
false /* unique */,
m_excludedMethods);
m_classMethodMap = new ClassMethodMap(testMethods, m_xmlMethodSelector);
m_afterXmlTestMethods = MethodHelper.collectAndOrderMethods(afterXmlTestMethods,
false /* forTests */,
m_runInfo,
m_annotationFinder,
true /* unique (CQ added by me)*/,
m_excludedMethods);
m_afterSuiteMethods = MethodHelper.collectAndOrderMethods(afterSuiteMethods,
false /* forTests */,
m_runInfo,
m_annotationFinder,
true /* unique */,
m_excludedMethods);
// shared group methods
m_groupMethods = new ConfigurationGroupMethods(m_allTestMethods, beforeGroupMethods, afterGroupMethods);
}
private void fixMethodsWithClass(ITestNGMethod[] methods,
ITestClass testCls,
List methodList) {
for (ITestNGMethod itm : methods) {
itm.setTestClass(testCls);
if (methodList != null) {
methodList.add(itm);
}
}
}
public Collection getTestClasses() {
return m_classMap.values();
}
public void setTestName(String name) {
m_testName = name;
}
public void setOutputDirectory(String od) {
m_outputDirectory= od;
// FIX: empty directories were created
// if (od == null) { m_outputDirectory = null; return; } //for maven2
// File file = new File(od);
// file.mkdirs();
// m_outputDirectory= file.getAbsolutePath();
}
private void addMetaGroup(String name, List groupNames) {
m_metaGroups.put(name, groupNames);
}
/**
* Calculate the transitive closure of all the MetaGroups
*
* @param groups
* @param unfinishedGroups
* @param result The transitive closure containing all the groups found
*/
private void collectGroups(String[] groups,
List unfinishedGroups,
Map result) {
for (String gn : groups) {
List subGroups = m_metaGroups.get(gn);
if (null != subGroups) {
for (String sg : subGroups) {
if (null == result.get(sg)) {
result.put(sg, sg);
unfinishedGroups.add(sg);
}
}
}
}
}
private Map createGroups(List groups) {
return createGroups(groups.toArray(new String[groups.size()]));
}
private Map createGroups(String[] groups) {
Map result = Maps.newHashMap();
// Groups that were passed on the command line
for (String group : groups) {
result.put(group, group);
}
// See if we have any MetaGroups and
// expand them if they match one of the groups
// we have just been passed
List unfinishedGroups = Lists.newArrayList();
if (m_metaGroups.size() > 0) {
collectGroups(groups, unfinishedGroups, result);
// Do we need to loop over unfinished groups?
while (unfinishedGroups.size() > 0) {
String[] uGroups = unfinishedGroups.toArray(new String[unfinishedGroups.size()]);
unfinishedGroups = Lists.newArrayList();
collectGroups(uGroups, unfinishedGroups, result);
}
}
// Utils.dumpMap(result);
return result;
}
/**
* The main entry method for TestRunner.
*
* This is where all the hard work is done:
* - Invoke configuration methods
* - Invoke test methods
* - Catch exceptions
* - Collect results
* - Invoke listeners
* - etc...
*/
public void run() {
beforeRun();
try {
XmlTest test= getTest();
if(test.isJUnit()) {
privateRunJUnit(test);
}
else {
privateRun(test);
}
}
finally {
afterRun();
}
}
/** Before run preparements. */
private void beforeRun() {
//
// Log the start date
//
m_startDate = new Date(System.currentTimeMillis());
// Log start
logStart();
// Invoke listeners
fireEvent(true /*start*/);
// invoke @BeforeTest
ITestNGMethod[] testConfigurationMethods= getBeforeTestConfigurationMethods();
if(null != testConfigurationMethods && testConfigurationMethods.length > 0) {
m_invoker.invokeConfigurations(null,
testConfigurationMethods,
m_xmlTest.getSuite(),
m_xmlTest.getAllParameters(),
null, /* no parameter values */
null /* instance */);
}
}
private void privateRunJUnit(XmlTest xmlTest) {
final ClassInfoMap cim = new ClassInfoMap(m_testClassesFromXml, false);
final Set> classes = cim.getClasses();
final List runMethods = Lists.newArrayList();
List> workers = Lists.newArrayList();
// FIXME: directly referencing JUnitTestRunner which uses JUnit classes
// may result in an class resolution exception under different JVMs
// The resolution process is not specified in the JVM spec with a specific implementation,
// so it can be eager => failure
workers.add(new IWorker() {
/**
* @see TestMethodWorker#getTimeOut()
*/
@Override
public long getTimeOut() {
return 0;
}
/**
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
for(Class> tc: classes) {
List includedMethods = cim.getXmlClass(tc).getIncludedMethods();
List methods = Lists.newArrayList();
for (XmlInclude inc: includedMethods) {
methods.add(inc.getName());
}
IJUnitTestRunner tr= ClassHelper.createTestRunner(TestRunner.this);
tr.setInvokedMethodListeners(m_invokedMethodListeners);
try {
tr.run(tc, methods.toArray(new String[methods.size()]));
}
catch(Exception ex) {
ex.printStackTrace();
}
finally {
runMethods.addAll(tr.getTestMethods());
}
}
}
@Override
public List getTasks() {
throw new TestNGException("JUnit not supported");
}
@Override
public int getPriority() {
if (m_allTestMethods.length == 1) {
return m_allTestMethods[0].getPriority();
} else {
return 0;
}
}
@Override
public int compareTo(IWorker other) {
return getPriority() - other.getPriority();
}
});
runJUnitWorkers(workers);
m_allTestMethods= runMethods.toArray(new ITestNGMethod[runMethods.size()]);
}
/**
* Main method that create a graph of methods and then pass it to the
* graph executor to run them.
*/
private void privateRun(XmlTest xmlTest) {
boolean parallel = xmlTest.getParallel().isParallel();
{
// parallel
int threadCount = parallel ? xmlTest.getThreadCount() : 1;
// Make sure we create a graph based on the intercepted methods, otherwise an interceptor
// removing methods would cause the graph never to terminate (because it would expect
// termination from methods that never get invoked).
DynamicGraph graph = createDynamicGraph(intercept(m_allTestMethods));
if (parallel) {
if (graph.getNodeCount() > 0) {
GraphThreadPoolExecutor executor =
new GraphThreadPoolExecutor(graph, this,
threadCount, threadCount, 0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
executor.run();
try {
long timeOut = m_xmlTest.getTimeOut(XmlTest.DEFAULT_TIMEOUT_MS);
Utils.log("TestRunner", 2, "Starting executor for test " + m_xmlTest.getName()
+ " with time out:" + timeOut + " milliseconds.");
executor.awaitTermination(timeOut, TimeUnit.MILLISECONDS);
executor.shutdownNow();
} catch (InterruptedException handled) {
handled.printStackTrace();
Thread.currentThread().interrupt();
}
}
} else {
boolean debug = false;
List freeNodes = graph.getFreeNodes();
if (debug) {
System.out.println("Free nodes:" + freeNodes);
}
if (graph.getNodeCount() > 0 && freeNodes.isEmpty()) {
throw new TestNGException("No free nodes found in:" + graph);
}
while (! freeNodes.isEmpty()) {
List> runnables = createWorkers(freeNodes);
for (IWorker r : runnables) {
r.run();
}
graph.setStatus(freeNodes, Status.FINISHED);
freeNodes = graph.getFreeNodes();
if (debug) {
System.out.println("Free nodes:" + freeNodes);
}
}
}
}
}
/**
* Apply the method interceptor (if applicable) to the list of methods.
*/
private ITestNGMethod[] intercept(ITestNGMethod[] methods) {
List methodInstances = methodsToMethodInstances(Arrays.asList(methods));
// add built-in interceptor (PreserveOrderMethodInterceptor or InstanceOrderingMethodInterceptor at the end of the list
m_methodInterceptors.add(builtinInterceptor);
for (IMethodInterceptor m_methodInterceptor : m_methodInterceptors) {
methodInstances = m_methodInterceptor.intercept(methodInstances, this);
}
List result = Lists.newArrayList();
for (IMethodInstance imi : methodInstances) {
result.add(imi.getMethod());
}
//Since an interceptor is involved, we would need to ensure that the ClassMethodMap object is in sync with the
//output of the interceptor, else @AfterClass doesn't get executed at all when interceptors are involved.
//so let's update the current classMethodMap object with the list of methods obtained from the interceptor.
this.m_classMethodMap = new ClassMethodMap(result, null);
return result.toArray(new ITestNGMethod[result.size()]);
}
/**
* Create a list of workers to run the methods passed in parameter.
* Each test method is run in its own worker except in the following cases:
* - The method belongs to a class that has @Test(sequential=true)
* - The parallel attribute is set to "classes"
* In both these cases, all the methods belonging to that class will then
* be put in the same worker in order to run in the same thread.
*/
@Override
public List> createWorkers(List methods) {
List> result;
if (XmlSuite.ParallelMode.INSTANCES.equals(m_xmlTest.getParallel())) {
result = createInstanceBasedParallelWorkers(methods);
} else {
result = createClassBasedParallelWorkers(methods);
}
return result;
}
/**
* Create workers for parallel="classes" and similar cases.
*/
private List> createClassBasedParallelWorkers(List methods) {
List> result = Lists.newArrayList();
// Methods that belong to classes with a sequential=true or parallel=classes
// attribute must all be run in the same worker
Set sequentialClasses = Sets.newHashSet();
for (ITestNGMethod m : methods) {
Class extends ITestClass> cls = m.getRealClass();
org.testng.annotations.ITestAnnotation test =
m_annotationFinder.findAnnotation(cls, org.testng.annotations.ITestAnnotation.class);
// If either sequential=true or parallel=classes, mark this class sequential
if (test != null && (test.getSequential() || test.getSingleThreaded()) ||
XmlSuite.ParallelMode.CLASSES.equals(m_xmlTest.getParallel())) {
sequentialClasses.add(cls);
}
}
List methodInstances = Lists.newArrayList();
for (ITestNGMethod tm : methods) {
methodInstances.addAll(methodsToMultipleMethodInstances(tm));
}
Map params = m_xmlTest.getAllParameters();
Set> processedClasses = Sets.newHashSet();
for (IMethodInstance im : methodInstances) {
Class> c = im.getMethod().getTestClass().getRealClass();
if (sequentialClasses.contains(c)) {
if (!processedClasses.contains(c)) {
processedClasses.add(c);
if (System.getProperty("experimental") != null) {
List> instances = createInstances(methodInstances);
for (List inst : instances) {
TestMethodWorker worker = createTestMethodWorker(inst, params, c);
result.add(worker);
}
}
else {
// Sequential class: all methods in one worker
TestMethodWorker worker = createTestMethodWorker(methodInstances, params, c);
result.add(worker);
}
}
}
else {
// Parallel class: each method in its own worker
TestMethodWorker worker = createTestMethodWorker(Arrays.asList(im), params, c);
result.add(worker);
}
}
// Sort by priorities
Collections.sort(result);
return result;
}
/**
* Create workers for parallel="instances".
*/
private List>
createInstanceBasedParallelWorkers(List methods) {
List> result = Lists.newArrayList();
ListMultiMap