blob: 44d61cde8e96b719efd180a0bc432456a15b846d [file] [log] [blame]
package org.testng.internal;
import org.testng.IConfigureCallBack;
import org.testng.IHookCallBack;
import org.testng.ITestClass;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.TestNGException;
import org.testng.annotations.IConfigurationAnnotation;
import org.testng.annotations.IExpectedExceptionsAnnotation;
import org.testng.annotations.ITestAnnotation;
import org.testng.annotations.ITestOrConfiguration;
import org.testng.collections.Lists;
import org.testng.collections.Maps;
import org.testng.internal.annotations.AnnotationHelper;
import org.testng.internal.annotations.IAnnotationFinder;
import org.testng.internal.thread.IExecutor;
import org.testng.internal.thread.IFutureResult;
import org.testng.internal.thread.ThreadExecutionException;
import org.testng.internal.thread.ThreadTimeoutException;
import org.testng.internal.thread.ThreadUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
/**
* Collection of helper methods to help sort and arrange methods.
*
* @author <a href="mailto:cedric@beust.com">Cedric Beust</a>
* @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
*/
public class MethodHelper {
// static private boolean m_quiet = true;
// ///
// public methods
//
public static ITestNGMethod[] collectAndOrderMethods(List<ITestNGMethod> methods,
RunInfo runInfo, IAnnotationFinder finder, List<ITestNGMethod> outExcludedMethods)
{
return internalCollectAndOrderMethods(methods.toArray(new ITestNGMethod[methods.size()]),
true /* forTest */, runInfo, finder, false /* unique */, outExcludedMethods);
}
/**
* @param methods
* @return All the methods that match the filtered groups. If a method belongs
* to an excluded group, it is automatically excluded.
*/
public static ITestNGMethod[] collectAndOrderConfigurationMethods(List<ITestNGMethod> methods,
RunInfo runInfo, IAnnotationFinder finder, boolean unique, List<ITestNGMethod> outExcludedMethods)
{
return internalCollectAndOrderMethods(methods.toArray(new ITestNGMethod[methods.size()]),
false /* forTests */, runInfo, finder, unique, outExcludedMethods);
}
private static ITestNGMethod[] internalCollectAndOrderMethods(ITestNGMethod[] methods,
boolean forTests, RunInfo runInfo, IAnnotationFinder finder,
boolean unique, List<ITestNGMethod> outExcludedMethods)
{
List<ITestNGMethod> includedMethods = Lists.newArrayList();
collectMethodsByGroup(methods,
forTests,
includedMethods,
outExcludedMethods,
runInfo,
finder,
unique);
return includedMethods.size() > 1 ?
sortMethods(forTests, includedMethods, finder).toArray(new ITestNGMethod[]{})
: includedMethods.toArray(new ITestNGMethod[]{});
}
/**
* @return all the methods that belong to the group specified by the regular
* expression groupRegExp. methods[] is the list of all the methods we
* are choosing from and method is the method that owns the dependsOnGroups
* statement (only used if a group is missing to flag an error on that method).
*/
public static ITestNGMethod[] findMethodsThatBelongToGroup(
ITestNGMethod method,
ITestNGMethod[] methods, String groupRegexp)
{
boolean foundGroup = false;
List<ITestNGMethod> vResult = Lists.newArrayList();
for (ITestNGMethod tm : methods) {
String[] groups = tm.getGroups();
for (String group : groups) {
if (Pattern.matches(groupRegexp, group)) {
vResult.add(tm);
foundGroup = true;
}
}
}
if (! foundGroup) {
method.setMissingGroup(groupRegexp);
}
ITestNGMethod[] result = vResult.toArray(new ITestNGMethod[vResult.size()]);
return result;
}
public static ITestNGMethod[] findMethodsNamed(ITestNGMethod m,
ITestNGMethod[] methods, String[] regexps)
{
String mainMethod = calculateMethodCanonicalName(m);
List<ITestNGMethod> vResult = Lists.newArrayList();
String currentRegexp = null;
for (String fullyQualifiedRegexp : regexps) {
boolean foundAtLeastAMethod = false;
if(null != fullyQualifiedRegexp) {
String regexp = escapeRegexp(fullyQualifiedRegexp);
currentRegexp = regexp;
boolean usePackage = regexp.indexOf(".") != -1;
for (ITestNGMethod method : methods) {
Method thisMethod = method.getMethod();
String thisMethodName = thisMethod.getName();
String methodName = usePackage ?
calculateMethodCanonicalName(thisMethod)
: thisMethodName;
// ppp("COMPARING\n" + regexp + "\n" + methodName);
if (Pattern.matches(regexp, methodName)) {
vResult.add(method);
foundAtLeastAMethod = true;
}
}
}
if (!foundAtLeastAMethod) {
if (m.ignoreMissingDependencies()) continue;
if (m.isAlwaysRun()) continue;
Method maybeReferringTo = findMethodByName(m, currentRegexp);
if (maybeReferringTo != null) {
throw new TestNGException(mainMethod + "() is not allowed to depend on " + maybeReferringTo);
}
throw new TestNGException(mainMethod
+ "() is depending on nonexistent method " + currentRegexp);
}
}
ITestNGMethod[] result = vResult.toArray(new ITestNGMethod[vResult.size()]);
return result;
}
private static Method findMethodByName(ITestNGMethod mainMethod, String regExp) {
if (regExp == null) return null;
int lastDot = regExp.lastIndexOf('.');
String className, methodName;
if (lastDot == -1) {
className = mainMethod.getMethod().getDeclaringClass().getCanonicalName();
methodName = regExp;
} else {
methodName = regExp.substring(lastDot+1);
className = regExp.substring(0, lastDot);
}
try {
Class<?> c = Class.forName(className);
for (Method m : c.getDeclaredMethods()) {
if (methodName.equals(m.getName())) return m;
}
} catch (Exception e) {} // ignore
return null;
}
/**
* Escapes $ in regexps as it is not meant for end-line matching, but inner class matches.
* Impl.is weird as the String methods are not available in 1.4
*/
private static String escapeRegexp(String regex) {
if(regex.indexOf('$') == -1) return regex;
String[] fragments= regex.split("\\$");
StringBuffer result= new StringBuffer();
for(int i= 0; i < fragments.length - 1; i++) {
result.append(fragments[i]).append("\\$");
}
result.append(fragments[fragments.length - 1]);
if(regex.endsWith("$")) result.append("\\$");
return result.toString();
}
/**
* Read the expected exceptions, if any (need to handle both the old and new
* syntax)
*/
public static ExpectedExceptionsHolder findExpectedExceptions(IAnnotationFinder finder,
Method method) {
ExpectedExceptionsHolder result = null;
IExpectedExceptionsAnnotation expectedExceptions=
(IExpectedExceptionsAnnotation) finder.findAnnotation(method,
IExpectedExceptionsAnnotation.class);
// Old syntax
if (expectedExceptions != null) {
result = new ExpectedExceptionsHolder(expectedExceptions.getValue(), ".*");
}
else {
// New syntax
ITestAnnotation testAnnotation =
(ITestAnnotation) finder.findAnnotation(method, ITestAnnotation.class);
if (testAnnotation != null) {
Class<?>[] ee = testAnnotation.getExpectedExceptions();
if (testAnnotation != null && ee.length > 0) {
result = new ExpectedExceptionsHolder(ee,
testAnnotation.getExpectedExceptionsMessageRegExp());
}
}
}
return result;
}
//
// End of public methods
// ///
public static boolean isEnabled(Class<?> objectClass, IAnnotationFinder finder) {
ITestAnnotation testClassAnnotation= AnnotationHelper.findTest(finder, objectClass);
return isEnabled(testClassAnnotation);
}
public static boolean isEnabled(Method m, IAnnotationFinder finder) {
ITestAnnotation annotation = AnnotationHelper.findTest(finder, m);
// If no method annotation, look for one on the class
if (null == annotation) {
annotation = AnnotationHelper.findTest(finder, m.getDeclaringClass());
}
return isEnabled(annotation);
}
public static boolean isEnabled(ITestOrConfiguration test) {
return null == test || (null != test && test.getEnabled());
}
public static ITestNGMethod[] findMethodsThatBelongToGroup(
ITestNGMethod method,
List<ITestNGMethod> methods, String groupRegexp)
{
ITestNGMethod[] allMethods = methods.toArray(new ITestNGMethod[methods
.size()]);
return findMethodsThatBelongToGroup(method, allMethods, groupRegexp);
}
/**
* @return The transitive closure of all the groups/methods included.
*/
public static void findGroupTransitiveClosure(XmlMethodSelector xms,
List<ITestNGMethod> includedMethods,
List<ITestNGMethod> allMethods,
String[] includedGroups,
Set<String> outGroups, Set<ITestNGMethod> outMethods)
{
Map<ITestNGMethod, ITestNGMethod> runningMethods = Maps.newHashMap();
for (ITestNGMethod m : includedMethods) {
runningMethods.put(m, m);
}
Map<String, String> runningGroups = Maps.newHashMap();
for (String thisGroup : includedGroups) {
runningGroups.put(thisGroup, thisGroup);
}
boolean keepGoing = true;
Map<ITestNGMethod, ITestNGMethod> newMethods = Maps.newHashMap();
while (keepGoing) {
for (ITestNGMethod m : includedMethods) {
//
// Depends on groups?
// Adds all included methods to runningMethods
//
String[] ig = m.getGroupsDependedUpon();
for (String g : ig) {
if (! runningGroups.containsKey(g)) {
// Found a new included group, add all the methods it contains to
// our outMethod closure
runningGroups.put(g, g);
ITestNGMethod[] im =
findMethodsThatBelongToGroup(m, allMethods, g);
for (ITestNGMethod thisMethod : im) {
if (! runningMethods.containsKey(thisMethod)) {
runningMethods.put(thisMethod, thisMethod);
newMethods.put(thisMethod, thisMethod);
}
}
}
} // groups
//
// Depends on methods?
// Adds all depended methods to runningMethods
//
String[] mdu = m.getMethodsDependedUpon();
for (String tm : mdu) {
ITestNGMethod thisMethod = findMethodNamed(tm, allMethods);
if (thisMethod != null && ! runningMethods.containsKey(thisMethod)) {
runningMethods.put(thisMethod, thisMethod);
newMethods.put(thisMethod, thisMethod);
}
}
} // methods
//
// Only keep going if new methods have been added
//
keepGoing = newMethods.size() > 0;
includedMethods = Lists.newArrayList();
includedMethods.addAll(newMethods.keySet());
newMethods = Maps.newHashMap();
} // while keepGoing
outMethods.addAll(runningMethods.keySet());
outGroups.addAll(runningGroups.keySet());
}
/**
* Extracts the map of groups and their corresponding methods from the <code>classes</code>.
*/
public static Map<String, List<ITestNGMethod>> findGroupsMethods(Collection<ITestClass> classes, boolean before) {
Map<String, List<ITestNGMethod>> result = Maps.newHashMap();
for (ITestClass cls : classes) {
ITestNGMethod[] methods = before ? cls.getBeforeGroupsMethods() : cls.getAfterGroupsMethods();
for (ITestNGMethod method : methods) {
for (String group : before ? method.getBeforeGroups() : method.getAfterGroups()) {
List<ITestNGMethod> methodList = result.get(group);
if (methodList == null) {
methodList = Lists.newArrayList();
result.put(group, methodList);
}
// NOTE(cbeust, 2007/01/23)
// BeforeGroups/AfterGroups methods should only be invoked once.
// I should probably use a map instead of a list for a contains(), but
// this list should usually be fairly short
if (! methodList.contains(method)) {
methodList.add(method);
}
}
}
}
return result;
}
/**
* Extracts the unique list of <code>ITestNGMethod</code>s.
*/
public static List<ITestNGMethod> uniqueMethodList(Collection<List<ITestNGMethod>> methods) {
Map<ITestNGMethod, ITestNGMethod> uniq = Maps.newHashMap();
for (List<ITestNGMethod> l : methods) {
for (ITestNGMethod m : l) {
uniq.put(m, m);
}
}
List<ITestNGMethod> result = Lists.newArrayList();
result.addAll(uniq.values());
return result;
}
private static ITestNGMethod findMethodNamed(String tm, List<ITestNGMethod> allMethods) {
for (ITestNGMethod m : allMethods) {
// TODO(cbeust): account for package
String methodName =
m.getMethod().getDeclaringClass().getName() + "." + m.getMethodName();
if (methodName.equals(tm)) return m;
}
return null;
}
private static boolean includeMethod(ITestOrConfiguration annotation,
RunInfo runInfo, ITestNGMethod tm, boolean forTests, boolean unique, List<ITestNGMethod> outIncludedMethods)
{
boolean result = false;
if (isEnabled(annotation)) {
if (runInfo.includeMethod(tm, forTests)) {
if (unique) {
if (!isMethodAlreadyPresent(outIncludedMethods, tm)) {
result = true;
}
}
else {
result = true;
}
}
}
return result;
}
/**
* Collect all the methods that belong to the included groups and exclude all
* the methods that belong to an excluded group.
*/
private static void collectMethodsByGroup(ITestNGMethod[] methods,
boolean forTests,
List<ITestNGMethod> outIncludedMethods,
List<ITestNGMethod> outExcludedMethods,
RunInfo runInfo,
IAnnotationFinder finder, boolean unique)
{
for (ITestNGMethod tm : methods) {
boolean in = false;
Method m = tm.getMethod();
//
// @Test method
//
if (forTests) {
in = includeMethod(AnnotationHelper.findTest(finder, m),
runInfo, tm, forTests, unique, outIncludedMethods);
}
//
// @Configuration method
//
else {
IConfigurationAnnotation annotation = AnnotationHelper.findConfiguration(finder, m);
if (annotation.getAlwaysRun()) {
in = true;
}
else {
in = includeMethod(AnnotationHelper.findTest(finder, m),
runInfo, tm, forTests, unique, outIncludedMethods);
}
}
if (in) {
outIncludedMethods.add(tm);
}
else {
outExcludedMethods.add(tm);
}
}
}
/**
* @param result
* @param tm
* @return true if a method by a similar name (and same hierarchy) already
* exists
*/
private static boolean isMethodAlreadyPresent(List<ITestNGMethod> result,
ITestNGMethod tm) {
for (ITestNGMethod m : result) {
Method jm1 = m.getMethod();
Method jm2 = tm.getMethod();
if (jm1.getName().equals(jm2.getName())) {
// Same names, see if they are in the same hierarchy
Class<?> c1 = jm1.getDeclaringClass();
Class<?> c2 = jm2.getDeclaringClass();
if (c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1)) {
return true;
}
}
}
return false;
}
private static Graph<ITestNGMethod> topologicalSort(ITestNGMethod[] methods,
List<ITestNGMethod> sequentialList, List<ITestNGMethod> parallelList) {
Graph<ITestNGMethod> result = new Graph<ITestNGMethod>();
if (methods.length == 0) return result;
//
// Create the graph
//
for (ITestNGMethod m : methods) {
result.addNode(m);
List<ITestNGMethod> predecessors = Lists.newArrayList();
String[] methodsDependedUpon = m.getMethodsDependedUpon();
String[] groupsDependedUpon = m.getGroupsDependedUpon();
if (methodsDependedUpon.length > 0) {
ITestNGMethod[] methodsNamed =
MethodHelper.findMethodsNamed(m, methods, methodsDependedUpon);
for (ITestNGMethod pred : methodsNamed) {
predecessors.add(pred);
}
}
if (groupsDependedUpon.length > 0) {
for (String group : groupsDependedUpon) {
ITestNGMethod[] methodsThatBelongToGroup =
MethodHelper.findMethodsThatBelongToGroup(m, methods, group);
for (ITestNGMethod pred : methodsThatBelongToGroup) {
predecessors.add(pred);
}
}
}
for (ITestNGMethod predecessor : predecessors) {
result.addPredecessor(m, predecessor);
}
}
result.topologicalSort();
sequentialList.addAll(result.getStrictlySortedNodes());
parallelList.addAll(result.getIndependentNodes());
return result;
}
public static String calculateMethodCanonicalName(ITestNGMethod m) {
return calculateMethodCanonicalName(m.getMethod());
}
private static String calculateMethodCanonicalName(Method m) {
String packageName = m.getDeclaringClass().getName() + "." + m.getName();
// Try to find the method on this class or parents
Class<?> cls = m.getDeclaringClass();
while (cls != Object.class) {
try {
if (cls.getDeclaredMethod(m.getName(), m.getParameterTypes()) != null) {
packageName = cls.getName();
break;
}
}
catch (Exception e) {
// ignore
}
cls = cls.getSuperclass();
}
String result = packageName + "." + m.getName();
return result;
}
private static List<ITestNGMethod> sortMethods(boolean forTests,
List<ITestNGMethod> allMethods, IAnnotationFinder finder) {
List<ITestNGMethod> sl = Lists.newArrayList();
List<ITestNGMethod> pl = Lists.newArrayList();
ITestNGMethod[] allMethodsArray = allMethods.toArray(new ITestNGMethod[allMethods.size()]);
// Fix the method inheritance if these are @Configuration methods to make
// sure base classes are invoked before child classes if 'before' and the
// other way around if they are 'after'
if (!forTests && allMethodsArray.length > 0) {
ITestNGMethod m = allMethodsArray[0];
boolean before = m.isBeforeClassConfiguration()
|| m.isBeforeMethodConfiguration() || m.isBeforeSuiteConfiguration()
|| m.isBeforeTestConfiguration();
MethodInheritance.fixMethodInheritance(allMethodsArray, before);
}
topologicalSort(allMethodsArray, sl, pl);
List<ITestNGMethod> result = Lists.newArrayList();
result.addAll(sl);
result.addAll(pl);
return result;
}
public static void ppp(String s) {
System.out.println("[MethodHelper] " + s);
}
/**
* @param method
* @param allTestMethods
* @return A sorted array containing all the methods 'method' depends on
*/
public static List<ITestNGMethod> getMethodsDependedUpon(ITestNGMethod method, ITestNGMethod[] methods) {
List<ITestNGMethod> parallelList = Lists.newArrayList();
List<ITestNGMethod> sequentialList = Lists.newArrayList();
Graph<ITestNGMethod> g = topologicalSort(methods, sequentialList, parallelList);
List<ITestNGMethod> result = g.findPredecessors(method);
return result;
}
public static Object invokeMethod(Method thisMethod, Object instance, Object[] parameters)
throws InvocationTargetException, IllegalAccessException
{
Object result = null;
// TESTNG-326, allow IObjectFactory to load from non-standard classloader
// If the instance has a different classloader, its class won't match the method's class
if (!thisMethod.getDeclaringClass().isAssignableFrom(instance.getClass())) {
// for some reason, we can't call this method on this class
// is it static?
boolean isStatic = Modifier.isStatic(thisMethod.getModifiers());
if (!isStatic) {
// not static, so grab a method with the same name and signature in this case
Class<?> clazz = instance.getClass();
try {
thisMethod = clazz.getMethod(thisMethod.getName(), thisMethod.getParameterTypes());
} catch (Exception e) {
// ignore, the method may be private
boolean found = false;
for (; clazz != null; clazz = clazz.getSuperclass()) {
try {
thisMethod = clazz.getDeclaredMethod(thisMethod.getName(), thisMethod.getParameterTypes());
found = true;
break;
} catch (Exception e2) {}
}
if (!found) {
//should we assert here? Or just allow it to fail on invocation?
if (thisMethod.getDeclaringClass().getName().equals(instance.getClass().getName())) {
throw new RuntimeException("Can't invoke method " + thisMethod + ", probably due to classloader mismatch");
}
throw new RuntimeException("Can't invoke method " + thisMethod + " on this instance of " + instance.getClass()+ " due to class mismatch");
}
}
}
}
boolean isPublic = Modifier.isPublic(thisMethod.getModifiers());
try {
if (!isPublic) {
thisMethod.setAccessible(true);
}
result = thisMethod.invoke(instance, parameters);
}
finally {
if (!isPublic) {
thisMethod.setAccessible(false);
}
}
return result;
}
public static Iterator<Object[]> createArrayIterator(final Object[][] objects) {
ArrayIterator result = new ArrayIterator(objects);
return result;
}
public static Iterator<Object[]> invokeDataProvider(Object instance,
Method dataProvider, ITestNGMethod method, ITestContext testContext,
Object fedInstance, IAnnotationFinder annotationFinder)
{
Iterator<Object[]> result = null;
Method testMethod = method.getMethod();
// If it returns an Object[][], convert it to an Iterable<Object[]>
try {
List<Object> lParameters = Lists.newArrayList();
// Go through all the parameters declared on this Data Provider and
// make sure we have at most one Method and one ITestContext.
// Anything else is an error
Class<?>[] parameterTypes = dataProvider.getParameterTypes();
if (parameterTypes.length > 2) {
throw new TestNGException("DataProvider " + dataProvider + " cannot have more than two parameters");
}
int i = 0;
for (Class<?> cls : parameterTypes) {
boolean isTestInstance = annotationFinder.hasTestInstance(dataProvider, i++);
if (cls.equals(Method.class)) {
lParameters.add(testMethod);
}
else if (cls.equals(ITestContext.class)) {
lParameters.add(testContext);
}
else if (isTestInstance) {
lParameters.add(fedInstance);
}
}
Object[] parameters = lParameters.toArray(new Object[lParameters.size()]);
Class< ? > returnType = dataProvider.getReturnType();
if (Object[][].class.isAssignableFrom(returnType)) {
Object[][] oResult = (Object[][]) MethodHelper.invokeMethod(
dataProvider, instance, parameters);
method.setParameterInvocationCount(oResult.length);
result = MethodHelper.createArrayIterator(oResult);
}
else if (Iterator.class.isAssignableFrom(returnType)) {
// Already an Iterable<Object[]>, assign it directly
result = (Iterator<Object[]>) MethodHelper.invokeMethod(dataProvider,
instance, parameters);
}
else {
throw new TestNGException("Data Provider " + dataProvider
+ " must return" + " either Object[][] or Iterator<Object>[], not "
+ returnType);
}
}
catch (InvocationTargetException e) {
throw new TestNGException(e);
}
catch (IllegalAccessException e) {
throw new TestNGException(e);
}
return result;
}
public static String calculateMethodCanonicalName(Class<?> methodClass, String methodName) {
Set<Method> methods = ClassHelper.getAvailableMethods(methodClass); // TESTNG-139
Method result = null;
for (Method m : methods) {
if (methodName.equals(m.getName())) {
result = m;
break;
}
}
return result != null ? calculateMethodCanonicalName(result) : null;
}
public static void invokeConfigurable(final Object instance,
final Object[] parameters, ITestClass testClass, final Method thisMethod,
ITestResult testResult) throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException, Throwable
{
Method runMethod = testClass.getRealClass().getMethod("run",
new Class[] { IConfigureCallBack.class, ITestResult.class });
final Throwable[] error = new Throwable[1];
IConfigureCallBack callback = new IConfigureCallBack() {
@Override
public void runConfigurationMethod(ITestResult tr) {
try {
invokeMethod(thisMethod, instance, parameters);
} catch (Throwable t) {
error[0] = t;
tr.setThrowable(t); // make Throwable available to IConfigurable
}
}
};
runMethod.invoke(instance, new Object[] { callback, testResult });
if (error[0] != null) {
throw error[0];
}
}
/**
* Invokes the <code>run</code> method of the <code>IHookable</code>.
*
* @param instance the instance to invoke the method in
* @param parameters the parameters to be passed to <code>IHookCallBack</code>
* @param testClass the test class
* @param thisMethod the method to be invoked through the <code>IHookCallBack</code>
* @param testResult the current <code>ITestResult</code> passed to <code>IHookable.run</code>
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws Throwable thrown if the reflective call to <tt>thisMethod</code> results in an exception
*/
public static void invokeHookable(final Object instance,
final Object[] parameters,
ITestClass testClass,
final Method thisMethod,
TestResult testResult)
throws Throwable
{
Method runMethod = testClass.getRealClass().getMethod("run",
new Class[] { IHookCallBack.class, ITestResult.class });
final Throwable[] error = new Throwable[1];
IHookCallBack callback = new IHookCallBack() {
@Override
public void runTestMethod(ITestResult tr) {
try {
invokeMethod(thisMethod, instance, parameters);
}
catch(Throwable t) {
error[0] = t;
tr.setThrowable( t ); // make Throwable available to IHookable
}
}
};
runMethod.invoke(instance, new Object[]{callback, testResult});
if (error[0] != null) {
throw error[0];
}
}
public static long calculateTimeOut(ITestNGMethod tm) {
long result = tm.getTimeOut() > 0 ? tm.getTimeOut() : tm.getInvocationTimeOut();
return result;
}
/**
* Invokes a method on a separate thread in order to allow us to timeout the invocation.
* It uses as implementation an <code>Executor</code> and a <code>CountDownLatch</code>.
* @param tm the
* @param instance
* @param parameterValues
* @param testResult
* @throws InterruptedException
* @throws ThreadExecutionException
*/
public static void invokeWithTimeout(ITestNGMethod tm, Object instance, Object[] parameterValues,
ITestResult testResult)
throws InterruptedException, ThreadExecutionException {
IExecutor exec= ThreadUtil.createExecutor(1, tm.getMethod().getName());
InvokeMethodRunnable imr = new InvokeMethodRunnable(tm, instance, parameterValues);
IFutureResult future= exec.submitRunnable(imr);
exec.shutdown();
long realTimeOut = calculateTimeOut(tm);
boolean finished = exec.awaitTermination(realTimeOut);
if (! finished) {
exec.stopNow();
ThreadTimeoutException exception = new ThreadTimeoutException("Method "
+ tm.getMethod()
+ " didn't finish within the time-out "
+ realTimeOut);
exception.setStackTrace(exec.getStackTraces()[0]);
testResult.setThrowable(exception);
testResult.setStatus(ITestResult.FAILURE);
}
else {
Utils.log("Invoker " + Thread.currentThread().hashCode(), 3,
"Method " + tm.getMethod() + " completed within the time-out " + tm.getTimeOut());
// We don't need the result from the future but invoking get() on it
// will trigger the exception that was thrown, if any
future.get();
// done.await();
testResult.setStatus(ITestResult.SUCCESS); // if no exception till here than SUCCESS
}
}
}
// ///
class ArrayIterator implements Iterator {
private Object[][] m_objects;
private int m_count;
public ArrayIterator(Object[][] objects) {
m_objects = objects;
m_count = 0;
}
@Override
public boolean hasNext() {
return m_count < m_objects.length;
}
@Override
public Object next() {
return m_objects[m_count++];
}
@Override
public void remove() {
// TODO Auto-generated method stub
}
}