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