MethodHelper.java revision dc0051b2828ddc734f076f959a1d9d54c322654d
1package org.testng.internal;
2
3import org.testng.IHookCallBack;
4import org.testng.ITestClass;
5import org.testng.ITestContext;
6import org.testng.ITestNGMethod;
7import org.testng.ITestResult;
8import org.testng.TestNGException;
9import org.testng.annotations.IConfigurationAnnotation;
10import org.testng.annotations.IExpectedExceptionsAnnotation;
11import org.testng.annotations.ITestAnnotation;
12import org.testng.annotations.ITestOrConfiguration;
13import org.testng.collections.Lists;
14import org.testng.collections.Maps;
15import org.testng.internal.annotations.AnnotationHelper;
16import org.testng.internal.annotations.IAnnotationFinder;
17import org.testng.internal.thread.IExecutor;
18import org.testng.internal.thread.IFutureResult;
19import org.testng.internal.thread.ThreadExecutionException;
20import org.testng.internal.thread.ThreadTimeoutException;
21import org.testng.internal.thread.ThreadUtil;
22
23import java.lang.reflect.InvocationTargetException;
24import java.lang.reflect.Method;
25import java.lang.reflect.Modifier;
26import java.util.Collection;
27import java.util.Iterator;
28import java.util.List;
29import java.util.Map;
30import java.util.Set;
31import java.util.regex.Pattern;
32
33/**
34 * Collection of helper methods to help sort and arrange methods.
35 *
36 * @author <a href="mailto:cedric@beust.com">Cedric Beust</a>
37 * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
38 */
39public class MethodHelper {
40//  static private boolean m_quiet = true;
41
42  // ///
43  // public methods
44  //
45
46  public static ITestNGMethod[] collectAndOrderMethods(List<ITestNGMethod> methods,
47      RunInfo runInfo, IAnnotationFinder finder, List<ITestNGMethod> outExcludedMethods)
48  {
49    return internalCollectAndOrderMethods(methods.toArray(new ITestNGMethod[methods.size()]),
50        true /* forTest */, runInfo, finder, false /* unique */, outExcludedMethods);
51  }
52
53  /**
54   * @param methods
55   * @return All the methods that match the filtered groups. If a method belongs
56   *         to an excluded group, it is automatically excluded.
57   */
58  public static ITestNGMethod[] collectAndOrderConfigurationMethods(List<ITestNGMethod> methods,
59      RunInfo runInfo, IAnnotationFinder finder, boolean unique, List<ITestNGMethod> outExcludedMethods)
60  {
61    return internalCollectAndOrderMethods(methods.toArray(new ITestNGMethod[methods.size()]),
62          false /* forTests */, runInfo, finder, unique, outExcludedMethods);
63  }
64
65  private static ITestNGMethod[] internalCollectAndOrderMethods(ITestNGMethod[] methods,
66      boolean forTests, RunInfo runInfo, IAnnotationFinder finder,
67      boolean unique, List<ITestNGMethod> outExcludedMethods)
68  {
69    List<ITestNGMethod> includedMethods = Lists.newArrayList();
70    collectMethodsByGroup(methods,
71        forTests,
72        includedMethods,
73        outExcludedMethods,
74        runInfo,
75        finder,
76        unique);
77
78    return includedMethods.size() > 1 ?
79      sortMethods(forTests, includedMethods, finder).toArray(new ITestNGMethod[]{})
80      : includedMethods.toArray(new ITestNGMethod[]{});
81  }
82
83
84  /**
85   * @return all the methods that belong to the group specified by the regular
86   * expression groupRegExp.  methods[] is the list of all the methods we
87   * are choosing from and method is the method that owns the dependsOnGroups
88   * statement (only used if a group is missing to flag an error on that method).
89   */
90  public static ITestNGMethod[] findMethodsThatBelongToGroup(
91      ITestNGMethod method,
92      ITestNGMethod[] methods, String groupRegexp)
93  {
94    boolean foundGroup = false;
95    List<ITestNGMethod> vResult = Lists.newArrayList();
96    for (ITestNGMethod tm : methods) {
97      String[] groups = tm.getGroups();
98      for (String group : groups) {
99        if (Pattern.matches(groupRegexp, group)) {
100          vResult.add(tm);
101          foundGroup = true;
102        }
103      }
104    }
105
106    if (! foundGroup) {
107      method.setMissingGroup(groupRegexp);
108    }
109
110    ITestNGMethod[] result = vResult.toArray(new ITestNGMethod[vResult.size()]);
111    return result;
112  }
113
114  public static ITestNGMethod[] findMethodsNamed(ITestNGMethod m,
115      ITestNGMethod[] methods, String[] regexps)
116  {
117    String mainMethod = calculateMethodCanonicalName(m);
118    List<ITestNGMethod> vResult = Lists.newArrayList();
119    String currentRegexp = null;
120    for (String fullyQualifiedRegexp : regexps) {
121      boolean foundAtLeastAMethod = false;
122
123      if(null != fullyQualifiedRegexp) {
124        String regexp = escapeRegexp(fullyQualifiedRegexp);
125        currentRegexp = regexp;
126        boolean usePackage = regexp.indexOf(".") != -1;
127
128        for (ITestNGMethod method : methods) {
129          Method thisMethod = method.getMethod();
130          String thisMethodName = thisMethod.getName();
131          String methodName = usePackage ?
132              calculateMethodCanonicalName(thisMethod)
133              : thisMethodName;
134//            ppp("COMPARING\n" + regexp + "\n" + methodName);
135          if (Pattern.matches(regexp, methodName)) {
136            vResult.add(method);
137            foundAtLeastAMethod = true;
138          }
139        }
140      }
141
142      if (!foundAtLeastAMethod) {
143        if (m.ignoreMissingDependencies()) continue;
144        if (m.isAlwaysRun()) continue;
145        Method maybeReferringTo = findMethodByName(m, currentRegexp);
146        if (maybeReferringTo != null) {
147          throw new TestNGException(mainMethod + "() is not allowed to depend on " + maybeReferringTo);
148        }
149        throw new TestNGException(mainMethod
150            + "() is depending on nonexistent method " + currentRegexp);
151      }
152    }
153
154    ITestNGMethod[] result = vResult.toArray(new ITestNGMethod[vResult.size()]);
155
156    return result;
157  }
158
159  private static Method findMethodByName(ITestNGMethod mainMethod, String regExp) {
160    if (regExp == null) return null;
161    int lastDot = regExp.lastIndexOf('.');
162    String className, methodName;
163    if (lastDot == -1) {
164      className = mainMethod.getMethod().getDeclaringClass().getCanonicalName();
165      methodName = regExp;
166    } else {
167      methodName = regExp.substring(lastDot+1);
168      className = regExp.substring(0, lastDot);
169    }
170
171    try {
172      Class<?> c = Class.forName(className);
173      for (Method m : c.getDeclaredMethods()) {
174        if (methodName.equals(m.getName())) return m;
175      }
176    } catch (Exception e) {} // ignore
177    return null;
178  }
179
180  /**
181   * Escapes $ in regexps as it is not meant for end-line matching, but inner class matches.
182   * Impl.is weird as the String methods are not available in 1.4
183   */
184  private static String escapeRegexp(String regex) {
185    if(regex.indexOf('$') == -1) return regex;
186    String[] fragments= regex.split("\\$");
187    StringBuffer result= new StringBuffer();
188    for(int i= 0; i < fragments.length - 1; i++) {
189      result.append(fragments[i]).append("\\$");
190    }
191    result.append(fragments[fragments.length - 1]);
192    if(regex.endsWith("$")) result.append("\\$");
193
194    return result.toString();
195  }
196
197  /**
198   * Read the expected exceptions, if any (need to handle both the old and new
199   * syntax)
200   */
201  public static ExpectedExceptionsHolder findExpectedExceptions(IAnnotationFinder finder,
202      Method method) {
203    ExpectedExceptionsHolder result = null;
204    IExpectedExceptionsAnnotation expectedExceptions=
205      (IExpectedExceptionsAnnotation) finder.findAnnotation(method,
206        IExpectedExceptionsAnnotation.class);
207    // Old syntax
208    if (expectedExceptions != null) {
209      result = new ExpectedExceptionsHolder(expectedExceptions.getValue(), ".*");
210    }
211    else {
212      // New syntax
213      ITestAnnotation testAnnotation =
214        (ITestAnnotation) finder.findAnnotation(method, ITestAnnotation.class);
215      if (testAnnotation != null) {
216        Class<?>[] ee = testAnnotation.getExpectedExceptions();
217        if (testAnnotation != null && ee.length > 0) {
218          result = new ExpectedExceptionsHolder(ee,
219              testAnnotation.getExpectedExceptionsMessageRegExp());
220        }
221      }
222    }
223
224    return result;
225  }
226
227  //
228  // End of public methods
229  // ///
230
231  public static boolean isEnabled(Class<?> objectClass, IAnnotationFinder finder) {
232    ITestAnnotation testClassAnnotation= AnnotationHelper.findTest(finder, objectClass);
233
234    return isEnabled(testClassAnnotation);
235  }
236
237  public static boolean isEnabled(Method m, IAnnotationFinder finder) {
238    ITestAnnotation annotation = AnnotationHelper.findTest(finder, m);
239
240    // If no method annotation, look for one on the class
241    if (null == annotation) {
242      annotation = AnnotationHelper.findTest(finder, m.getDeclaringClass());
243    }
244
245    return isEnabled(annotation);
246  }
247
248  public static boolean isEnabled(ITestOrConfiguration test) {
249    return null == test || (null != test && test.getEnabled());
250  }
251
252  public static ITestNGMethod[] findMethodsThatBelongToGroup(
253      ITestNGMethod method,
254      List<ITestNGMethod> methods, String groupRegexp)
255  {
256    ITestNGMethod[] allMethods = methods.toArray(new ITestNGMethod[methods
257        .size()]);
258    return findMethodsThatBelongToGroup(method, allMethods, groupRegexp);
259  }
260
261  /**
262   * @return The transitive closure of all the groups/methods included.
263   */
264  public static void findGroupTransitiveClosure(XmlMethodSelector xms,
265      List<ITestNGMethod> includedMethods,
266      List<ITestNGMethod> allMethods,
267      String[] includedGroups,
268      Set<String> outGroups, Set<ITestNGMethod> outMethods)
269  {
270    Map<ITestNGMethod, ITestNGMethod> runningMethods = Maps.newHashMap();
271    for (ITestNGMethod m : includedMethods) {
272      runningMethods.put(m, m);
273    }
274
275    Map<String, String> runningGroups = Maps.newHashMap();
276    for (String thisGroup : includedGroups) {
277      runningGroups.put(thisGroup, thisGroup);
278    }
279
280    boolean keepGoing = true;
281
282    Map<ITestNGMethod, ITestNGMethod> newMethods = Maps.newHashMap();
283    while (keepGoing) {
284      for (ITestNGMethod m : includedMethods) {
285
286        //
287        // Depends on groups?
288        // Adds all included methods to runningMethods
289        //
290        String[] ig = m.getGroupsDependedUpon();
291        for (String g : ig) {
292          if (! runningGroups.containsKey(g)) {
293            // Found a new included group, add all the methods it contains to
294            // our outMethod closure
295            runningGroups.put(g, g);
296            ITestNGMethod[] im =
297              findMethodsThatBelongToGroup(m, allMethods, g);
298            for (ITestNGMethod thisMethod : im) {
299              if (! runningMethods.containsKey(thisMethod)) {
300                runningMethods.put(thisMethod, thisMethod);
301                newMethods.put(thisMethod, thisMethod);
302              }
303            }
304          }
305        } // groups
306
307        //
308        // Depends on methods?
309        // Adds all depended methods to runningMethods
310        //
311        String[] mdu = m.getMethodsDependedUpon();
312        for (String tm : mdu) {
313          ITestNGMethod thisMethod = findMethodNamed(tm, allMethods);
314          if (thisMethod != null && ! runningMethods.containsKey(thisMethod)) {
315            runningMethods.put(thisMethod, thisMethod);
316            newMethods.put(thisMethod, thisMethod);
317          }
318        }
319
320      } // methods
321
322      //
323      // Only keep going if new methods have been added
324      //
325      keepGoing = newMethods.size() > 0;
326      includedMethods = Lists.newArrayList();
327      includedMethods.addAll(newMethods.keySet());
328      newMethods = Maps.newHashMap();
329    } // while keepGoing
330
331    outMethods.addAll(runningMethods.keySet());
332    outGroups.addAll(runningGroups.keySet());
333  }
334
335  /**
336   * Extracts the map of groups and their corresponding methods from the <code>classes</code>.
337   */
338  public static Map<String, List<ITestNGMethod>> findGroupsMethods(Collection<ITestClass> classes, boolean before) {
339    Map<String, List<ITestNGMethod>> result = Maps.newHashMap();
340    for (ITestClass cls : classes) {
341      ITestNGMethod[] methods = before ? cls.getBeforeGroupsMethods() : cls.getAfterGroupsMethods();
342      for (ITestNGMethod method : methods) {
343        for (String group : before ? method.getBeforeGroups() : method.getAfterGroups()) {
344          List<ITestNGMethod> methodList = result.get(group);
345          if (methodList == null) {
346            methodList = Lists.newArrayList();
347            result.put(group, methodList);
348          }
349          // NOTE(cbeust, 2007/01/23)
350          // BeforeGroups/AfterGroups methods should only be invoked once.
351          // I should probably use a map instead of a list for a contains(), but
352          // this list should usually be fairly short
353          if (! methodList.contains(method)) {
354            methodList.add(method);
355          }
356        }
357      }
358    }
359
360    return result;
361  }
362
363  /**
364   * Extracts the unique list of <code>ITestNGMethod</code>s.
365   */
366  public static List<ITestNGMethod> uniqueMethodList(Collection<List<ITestNGMethod>> methods) {
367    Map<ITestNGMethod, ITestNGMethod> uniq = Maps.newHashMap();
368
369    for (List<ITestNGMethod> l : methods) {
370      for (ITestNGMethod m : l) {
371        uniq.put(m, m);
372      }
373    }
374
375    List<ITestNGMethod> result = Lists.newArrayList();
376    result.addAll(uniq.values());
377
378    return result;
379  }
380
381  private static ITestNGMethod findMethodNamed(String tm, List<ITestNGMethod> allMethods) {
382    for (ITestNGMethod m : allMethods) {
383      // TODO(cbeust):  account for package
384      String methodName =
385        m.getMethod().getDeclaringClass().getName() + "." + m.getMethodName();
386      if (methodName.equals(tm)) return m;
387    }
388
389    return null;
390  }
391
392  private static boolean includeMethod(ITestOrConfiguration annotation,
393      RunInfo runInfo, ITestNGMethod tm, boolean forTests, boolean unique, List<ITestNGMethod> outIncludedMethods)
394  {
395    boolean result = false;
396
397    if (isEnabled(annotation)) {
398      if (runInfo.includeMethod(tm, forTests)) {
399        if (unique) {
400          if (!isMethodAlreadyPresent(outIncludedMethods, tm)) {
401            result = true;
402          }
403        }
404        else {
405          result = true;
406        }
407      }
408    }
409
410    return result;
411  }
412
413  /**
414   * Collect all the methods that belong to the included groups and exclude all
415   * the methods that belong to an excluded group.
416   */
417  private static void collectMethodsByGroup(ITestNGMethod[] methods,
418      boolean forTests,
419      List<ITestNGMethod> outIncludedMethods,
420      List<ITestNGMethod> outExcludedMethods,
421      RunInfo runInfo,
422      IAnnotationFinder finder, boolean unique)
423  {
424    for (ITestNGMethod tm : methods) {
425      boolean in = false;
426      Method m = tm.getMethod();
427      //
428      // @Test method
429      //
430      if (forTests) {
431        in = includeMethod(AnnotationHelper.findTest(finder, m),
432            runInfo, tm, forTests, unique, outIncludedMethods);
433      }
434
435      //
436      // @Configuration method
437      //
438      else {
439        IConfigurationAnnotation annotation = AnnotationHelper.findConfiguration(finder, m);
440        if (annotation.getAlwaysRun()) {
441          in = true;
442        }
443        else {
444          in = includeMethod(AnnotationHelper.findTest(finder, m),
445              runInfo, tm, forTests, unique, outIncludedMethods);
446        }
447      }
448      if (in) {
449        outIncludedMethods.add(tm);
450      }
451      else {
452        outExcludedMethods.add(tm);
453      }
454    }
455  }
456
457  /**
458   * @param result
459   * @param tm
460   * @return true if a method by a similar name (and same hierarchy) already
461   *         exists
462   */
463  private static boolean isMethodAlreadyPresent(List<ITestNGMethod> result,
464      ITestNGMethod tm) {
465    for (ITestNGMethod m : result) {
466      Method jm1 = m.getMethod();
467      Method jm2 = tm.getMethod();
468      if (jm1.getName().equals(jm2.getName())) {
469        // Same names, see if they are in the same hierarchy
470        Class<?> c1 = jm1.getDeclaringClass();
471        Class<?> c2 = jm2.getDeclaringClass();
472        if (c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1)) {
473          return true;
474        }
475      }
476    }
477
478    return false;
479  }
480
481  private static Graph<ITestNGMethod> topologicalSort(ITestNGMethod[] methods,
482      List<ITestNGMethod> sequentialList, List<ITestNGMethod> parallelList) {
483    Graph<ITestNGMethod> result = new Graph<ITestNGMethod>();
484
485    if (methods.length == 0) return result;
486
487    //
488    // Create the graph
489    //
490    for (ITestNGMethod m : methods) {
491      result.addNode(m);
492
493      List<ITestNGMethod> predecessors = Lists.newArrayList();
494
495      String[] methodsDependedUpon = m.getMethodsDependedUpon();
496      String[] groupsDependedUpon = m.getGroupsDependedUpon();
497      if (methodsDependedUpon.length > 0) {
498        ITestNGMethod[] methodsNamed =
499          MethodHelper.findMethodsNamed(m, methods, methodsDependedUpon);
500        for (ITestNGMethod pred : methodsNamed) {
501          predecessors.add(pred);
502        }
503      }
504      if (groupsDependedUpon.length > 0) {
505        for (String group : groupsDependedUpon) {
506          ITestNGMethod[] methodsThatBelongToGroup =
507            MethodHelper.findMethodsThatBelongToGroup(m, methods, group);
508          for (ITestNGMethod pred : methodsThatBelongToGroup) {
509            predecessors.add(pred);
510          }
511        }
512      }
513
514      for (ITestNGMethod predecessor : predecessors) {
515        result.addPredecessor(m, predecessor);
516      }
517    }
518
519    result.topologicalSort();
520    sequentialList.addAll(result.getStrictlySortedNodes());
521    parallelList.addAll(result.getIndependentNodes());
522
523    return result;
524  }
525
526  public static String calculateMethodCanonicalName(ITestNGMethod m) {
527    return calculateMethodCanonicalName(m.getMethod());
528  }
529
530  private static String calculateMethodCanonicalName(Method m) {
531    String packageName = m.getDeclaringClass().getName() + "." + m.getName();
532
533    // Try to find the method on this class or parents
534    Class<?> cls = m.getDeclaringClass();
535    while (cls != Object.class) {
536      try {
537        if (cls.getDeclaredMethod(m.getName(), m.getParameterTypes()) != null) {
538          packageName = cls.getName();
539          break;
540        }
541      }
542      catch (Exception e) {
543        // ignore
544      }
545      cls = cls.getSuperclass();
546    }
547
548    String result = packageName + "." + m.getName();
549    return result;
550  }
551
552  private static List<ITestNGMethod> sortMethods(boolean forTests,
553      List<ITestNGMethod> allMethods, IAnnotationFinder finder) {
554    List<ITestNGMethod> sl = Lists.newArrayList();
555    List<ITestNGMethod> pl = Lists.newArrayList();
556    ITestNGMethod[] allMethodsArray = allMethods.toArray(new ITestNGMethod[allMethods.size()]);
557
558    // Fix the method inheritance if these are @Configuration methods to make
559    // sure base classes are invoked before child classes if 'before' and the
560    // other way around if they are 'after'
561    if (!forTests && allMethodsArray.length > 0) {
562      ITestNGMethod m = allMethodsArray[0];
563      boolean before = m.isBeforeClassConfiguration()
564          || m.isBeforeMethodConfiguration() || m.isBeforeSuiteConfiguration()
565          || m.isBeforeTestConfiguration();
566      MethodInheritance.fixMethodInheritance(allMethodsArray, before);
567    }
568
569    topologicalSort(allMethodsArray, sl, pl);
570
571    List<ITestNGMethod> result = Lists.newArrayList();
572    result.addAll(sl);
573    result.addAll(pl);
574    return result;
575  }
576
577  public static void ppp(String s) {
578    System.out.println("[MethodHelper] " + s);
579  }
580
581  /**
582   * @param method
583   * @param allTestMethods
584   * @return A sorted array containing all the methods 'method' depends on
585   */
586  public static List<ITestNGMethod> getMethodsDependedUpon(ITestNGMethod method, ITestNGMethod[] methods) {
587    List<ITestNGMethod> parallelList = Lists.newArrayList();
588    List<ITestNGMethod> sequentialList = Lists.newArrayList();
589    Graph<ITestNGMethod> g = topologicalSort(methods, sequentialList, parallelList);
590
591    List<ITestNGMethod> result = g.findPredecessors(method);
592    return result;
593  }
594
595  public static Object invokeMethod(Method thisMethod, Object instance, Object[] parameters)
596  throws InvocationTargetException, IllegalAccessException
597  {
598    Object result = null;
599    // TESTNG-326, allow IObjectFactory to load from non-standard classloader
600    // If the instance has a different classloader, its class won't match the method's class
601    if (!thisMethod.getDeclaringClass().isAssignableFrom(instance.getClass())) {
602      // for some reason, we can't call this method on this class
603      // is it static?
604      boolean isStatic = Modifier.isStatic(thisMethod.getModifiers());
605      if (!isStatic) {
606        // not static, so grab a method with the same name and signature in this case
607        Class<?> clazz = instance.getClass();
608        try {
609          thisMethod = clazz.getMethod(thisMethod.getName(), thisMethod.getParameterTypes());
610        } catch (Exception e) {
611          // ignore, the method may be private
612          boolean found = false;
613          for (; clazz != null; clazz = clazz.getSuperclass()) {
614            try {
615              thisMethod = clazz.getDeclaredMethod(thisMethod.getName(), thisMethod.getParameterTypes());
616              found = true;
617              break;
618            } catch (Exception e2) {}
619          }
620          if (!found) {
621            //should we assert here?  Or just allow it to fail on invocation?
622            if (thisMethod.getDeclaringClass().getName().equals(instance.getClass().getName())) {
623              throw new RuntimeException("Can't invoke method " + thisMethod + ", probably due to classloader mismatch");
624            }
625            throw new RuntimeException("Can't invoke method " + thisMethod + " on this instance of " + instance.getClass()+ " due to class mismatch");
626          }
627        }
628      }
629
630
631
632    }
633
634    boolean isPublic = Modifier.isPublic(thisMethod.getModifiers());
635
636    try {
637      if (!isPublic) {
638        thisMethod.setAccessible(true);
639      }
640      result = thisMethod.invoke(instance, parameters);
641    }
642    finally {
643      if (!isPublic) {
644        thisMethod.setAccessible(false);
645      }
646    }
647
648    return result;
649  }
650
651  public static Iterator<Object[]> createArrayIterator(final Object[][] objects) {
652    ArrayIterator result = new ArrayIterator(objects);
653    return result;
654  }
655
656  public static Iterator<Object[]> invokeDataProvider(Object instance,
657      Method dataProvider, ITestNGMethod method, ITestContext testContext,
658      Object fedInstance, IAnnotationFinder annotationFinder)
659  {
660    Iterator<Object[]> result = null;
661    Method testMethod = method.getMethod();
662
663    // If it returns an Object[][], convert it to an Iterable<Object[]>
664    try {
665      List<Object> lParameters = Lists.newArrayList();
666
667      // Go through all the parameters declared on this Data Provider and
668      // make sure we have at most one Method and one ITestContext.
669      // Anything else is an error
670      Class<?>[] parameterTypes = dataProvider.getParameterTypes();
671      if (parameterTypes.length > 2) {
672        throw new TestNGException("DataProvider " + dataProvider + " cannot have more than two parameters");
673      }
674
675      int i = 0;
676      for (Class<?> cls : parameterTypes) {
677        boolean isTestInstance = annotationFinder.hasTestInstance(dataProvider, i++);
678        if (cls.equals(Method.class)) {
679          lParameters.add(testMethod);
680        }
681        else if (cls.equals(ITestContext.class)) {
682          lParameters.add(testContext);
683        }
684        else if (isTestInstance) {
685          lParameters.add(fedInstance);
686        }
687      }
688      Object[] parameters = lParameters.toArray(new Object[lParameters.size()]);
689
690      Class< ? > returnType = dataProvider.getReturnType();
691      if (Object[][].class.isAssignableFrom(returnType)) {
692        Object[][] oResult = (Object[][]) MethodHelper.invokeMethod(
693            dataProvider, instance, parameters);
694        method.setParameterInvocationCount(oResult.length);
695        result = MethodHelper.createArrayIterator(oResult);
696      }
697      else if (Iterator.class.isAssignableFrom(returnType)) {
698        // Already an Iterable<Object[]>, assign it directly
699        result = (Iterator<Object[]>) MethodHelper.invokeMethod(dataProvider,
700            instance, parameters);
701      }
702      else {
703        throw new TestNGException("Data Provider " + dataProvider
704            + " must return" + " either Object[][] or Iterator<Object>[], not "
705            + returnType);
706      }
707    }
708    catch (InvocationTargetException e) {
709      throw new TestNGException(e);
710    }
711    catch (IllegalAccessException e) {
712      throw new TestNGException(e);
713    }
714
715    return result;
716  }
717
718  public static String calculateMethodCanonicalName(Class<?> methodClass, String methodName) {
719    Set<Method> methods = ClassHelper.getAvailableMethods(methodClass); // TESTNG-139
720    Method result = null;
721    for (Method m : methods) {
722      if (methodName.equals(m.getName())) {
723        result = m;
724        break;
725      }
726    }
727
728    return result != null ? calculateMethodCanonicalName(result) : null;
729  }
730
731  /**
732   * Invokes the <code>run</code> method of the <code>IHookable</code>.
733   *
734   * @param instance the instance to invoke the method in
735   * @param parameters the parameters to be passed to <code>IHookCallBack</code>
736   * @param testClass the test class
737   * @param thisMethod the method to be invoked through the <code>IHookCallBack</code>
738   * @param testResult the current <code>ITestResult</code> passed to <code>IHookable.run</code>
739   * @throws NoSuchMethodException
740   * @throws IllegalAccessException
741   * @throws InvocationTargetException
742   * @throws Throwable thrown if the reflective call to <tt>thisMethod</code> results in an exception
743   */
744  public static void invokeHookable(final Object instance,
745                                    final Object[] parameters,
746                                    ITestClass testClass,
747                                    final Method thisMethod,
748                                    TestResult testResult)
749  throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, Throwable
750  {
751    Method runMethod = testClass.getRealClass().getMethod("run",
752        new Class[] { IHookCallBack.class, ITestResult.class });
753    final Throwable[] error = new Throwable[1];
754
755    IHookCallBack callback = new IHookCallBack() {
756      public void runTestMethod(ITestResult tr) {
757        try {
758          invokeMethod(thisMethod, instance, parameters);
759         }
760         catch(Throwable t) {
761           error[0] = t;
762           tr.setThrowable( t ); // make Throwable available to IHookable
763         }
764       }
765    };
766    runMethod.invoke(instance, new Object[]{callback, testResult});
767    if (error[0] != null) {
768      throw error[0];
769    }
770  }
771
772  public static long calculateTimeOut(ITestNGMethod tm) {
773    long result = tm.getTimeOut() > 0 ? tm.getTimeOut() : tm.getInvocationTimeOut();
774    return result;
775  }
776
777  /**
778   * Invokes a method on a separate thread in order to allow us to timeout the invocation.
779   * It uses as implementation an <code>Executor</code> and a <code>CountDownLatch</code>.
780   * @param tm the
781   * @param instance
782   * @param parameterValues
783   * @param testResult
784   * @throws InterruptedException
785   * @throws ThreadExecutionException
786   */
787  public static void invokeWithTimeout(ITestNGMethod tm, Object instance, Object[] parameterValues,
788      ITestResult testResult)
789  throws InterruptedException, ThreadExecutionException {
790    IExecutor exec= ThreadUtil.createExecutor(1, tm.getMethod().getName());
791
792    InvokeMethodRunnable imr = new InvokeMethodRunnable(tm, instance, parameterValues);
793    IFutureResult future= exec.submitRunnable(imr);
794    exec.shutdown();
795    long realTimeOut = calculateTimeOut(tm);
796    boolean finished= exec.awaitTermination(realTimeOut);
797
798    if(!finished) {
799      exec.stopNow();
800      ThreadTimeoutException exception = new ThreadTimeoutException("Method "
801        + tm.getMethod()
802        + " didn't finish within the time-out "
803        + realTimeOut);
804      exception.setStackTrace(exec.getStackTraces()[0]);
805      testResult.setThrowable(exception);
806      testResult.setStatus(ITestResult.FAILURE);
807    }
808    else {
809      Utils.log("Invoker " + Thread.currentThread().hashCode(), 3,
810          "Method " + tm.getMethod() + " completed within the time-out " + tm.getTimeOut());
811
812      // We don't need the result from the future but invoking get() on it
813      // will trigger the exception that was thrown, if any
814      future.get();
815//      done.await();
816
817      testResult.setStatus(ITestResult.SUCCESS); // if no exception till here than SUCCESS
818    }
819  }
820}
821
822// ///
823
824class ArrayIterator implements Iterator {
825  private Object[][] m_objects;
826
827  private int m_count;
828
829  public ArrayIterator(Object[][] objects) {
830    m_objects = objects;
831    m_count = 0;
832  }
833
834  public boolean hasNext() {
835    return m_count < m_objects.length;
836  }
837
838  public Object next() {
839    return m_objects[m_count++];
840  }
841
842  public void remove() {
843    // TODO Auto-generated method stub
844
845  }
846
847}
848