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, Object configurableInstance, final Method thisMethod, ITestResult testResult) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, Throwable { Method runMethod = configurableInstance.getClass().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 } } @Override public Object[] getParameters() { return parameters; } }; runMethod.invoke(configurableInstance, new Object[] { callback, testResult }); if (error[0] != null) { throw error[0]; } } /** * Invokes the <code>run</code> method of the <code>IHookable</code>. * * @param testInstance the instance to invoke the method in * @param parameters the parameters to be passed to <code>IHookCallBack</code> * @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 testInstance, final Object[] parameters, Object hookableInstance, final Method thisMethod, TestResult testResult) throws Throwable { Method runMethod = hookableInstance.getClass().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, testInstance, parameters); } catch(Throwable t) { error[0] = t; tr.setThrowable( t ); // make Throwable available to IHookable } } @Override public Object[] getParameters() { return parameters; } }; runMethod.invoke(hookableInstance, 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.getClass().getName() + "." + tm.getMethodName() + "()" + " 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 } }