001: package org.testng.internal;
002:
003: import java.lang.reflect.InvocationTargetException;
004: import java.lang.reflect.Method;
005: import java.lang.reflect.Modifier;
006: import java.util.ArrayList;
007: import java.util.Collection;
008: import java.util.HashMap;
009: import java.util.Iterator;
010: import java.util.List;
011: import java.util.Map;
012: import java.util.Set;
013: import java.util.regex.Pattern;
014:
015: import org.testng.IHookCallBack;
016: import org.testng.ITestClass;
017: import org.testng.ITestContext;
018: import org.testng.ITestNGMethod;
019: import org.testng.ITestResult;
020: import org.testng.TestNGException;
021: import org.testng.internal.annotations.AnnotationHelper;
022: import org.testng.internal.annotations.IAnnotationFinder;
023: import org.testng.internal.annotations.IConfiguration;
024: import org.testng.internal.annotations.IExpectedExceptions;
025: import org.testng.internal.annotations.ITest;
026: import org.testng.internal.annotations.ITestOrConfiguration;
027: import org.testng.internal.thread.IExecutor;
028: import org.testng.internal.thread.IFutureResult;
029: import org.testng.internal.thread.ThreadExecutionException;
030: import org.testng.internal.thread.ThreadTimeoutException;
031: import org.testng.internal.thread.ThreadUtil;
032:
033: /**
034: * Collection of helper methods to help sort and arrange methods.
035: *
036: * @author <a href="mailto:cedric@beust.com">Cedric Beust</a>
037: * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
038: */
039: public class MethodHelper {
040: // static private boolean m_quiet = true;
041:
042: // ///
043: // public methods
044: //
045:
046: public static ITestNGMethod[] collectAndOrderMethods(
047: List<ITestNGMethod> methods, RunInfo runInfo,
048: IAnnotationFinder finder,
049: List<ITestNGMethod> outExcludedMethods) {
050: return internalCollectAndOrderMethods(methods
051: .toArray(new ITestNGMethod[methods.size()]),
052: true /* forTest */, runInfo, finder,
053: false /* unique */, outExcludedMethods);
054: }
055:
056: /**
057: * @param methods
058: * @return All the methods that match the filtered groups. If a method belongs
059: * to an excluded group, it is automatically excluded.
060: */
061: public static ITestNGMethod[] collectAndOrderConfigurationMethods(
062: List<ITestNGMethod> methods, RunInfo runInfo,
063: IAnnotationFinder finder, boolean unique,
064: List<ITestNGMethod> outExcludedMethods) {
065: return internalCollectAndOrderMethods(methods
066: .toArray(new ITestNGMethod[methods.size()]),
067: false /* forTests */, runInfo, finder, unique,
068: outExcludedMethods);
069: }
070:
071: private static ITestNGMethod[] internalCollectAndOrderMethods(
072: ITestNGMethod[] methods, boolean forTests, RunInfo runInfo,
073: IAnnotationFinder finder, boolean unique,
074: List<ITestNGMethod> outExcludedMethods) {
075: List<ITestNGMethod> includedMethods = new ArrayList<ITestNGMethod>();
076: collectMethodsByGroup(methods, forTests, includedMethods,
077: outExcludedMethods, runInfo, finder, unique);
078: List<ITestNGMethod> vResult = sortMethods(forTests,
079: includedMethods, finder);
080:
081: ITestNGMethod[] result = vResult
082: .toArray(new ITestNGMethod[vResult.size()]);
083:
084: return result;
085: }
086:
087: /**
088: * @return all the methods that belong to the group specified by the regular
089: * expression groupRegExp. methods[] is the list of all the methods we
090: * are choosing from and method is the method that owns the dependsOnGroups
091: * statement (only used if a group is missing to flag an error on that method).
092: */
093: public static ITestNGMethod[] findMethodsThatBelongToGroup(
094: ITestNGMethod method, ITestNGMethod[] methods,
095: String groupRegexp) {
096: boolean foundGroup = false;
097: List<ITestNGMethod> vResult = new ArrayList<ITestNGMethod>();
098: for (ITestNGMethod tm : methods) {
099: String[] groups = tm.getGroups();
100: for (String group : groups) {
101: if (Pattern.matches(groupRegexp, group)) {
102: vResult.add(tm);
103: foundGroup = true;
104: }
105: }
106: }
107:
108: if (!foundGroup) {
109: method.setMissingGroup(groupRegexp);
110: }
111:
112: ITestNGMethod[] result = vResult
113: .toArray(new ITestNGMethod[vResult.size()]);
114: return result;
115: }
116:
117: public static ITestNGMethod[] findMethodsNamed(String mainMethod,
118: ITestNGMethod[] methods, String[] regexps) {
119: List<ITestNGMethod> vResult = new ArrayList<ITestNGMethod>();
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 this Method = method.getMethod();
131: String this MethodName = this Method.getName();
132: String methodName = usePackage ? calculateMethodCanonicalName(this Method)
133: : this MethodName;
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: throw new TestNGException(mainMethod
144: + "() is depending on nonexistent method "
145: + currentRegexp);
146: }
147: }
148:
149: ITestNGMethod[] result = vResult
150: .toArray(new ITestNGMethod[vResult.size()]);
151:
152: return result;
153: }
154:
155: /**
156: * Escapes $ in regexps as it is not meant for end-line matching, but inner class matches.
157: * Impl.is weird as the String methods are not available in 1.4
158: */
159: private static String escapeRegexp(String regex) {
160: if (regex.indexOf('$') == -1)
161: return regex;
162: String[] fragments = regex.split("\\$");
163: StringBuffer result = new StringBuffer();
164: for (int i = 0; i < fragments.length - 1; i++) {
165: result.append(fragments[i]).append("\\$");
166: }
167: result.append(fragments[fragments.length - 1]);
168: if (regex.endsWith("$"))
169: result.append("\\$");
170:
171: return result.toString();
172: }
173:
174: /**
175: * Read the expected exceptions, if any (need to handle both the old and new
176: * syntax
177: */
178: public static Class<?>[] findExpectedExceptions(
179: IAnnotationFinder finder, Method method) {
180: Class<?>[] result = {};
181: IExpectedExceptions expectedExceptions = (IExpectedExceptions) finder
182: .findAnnotation(method, IExpectedExceptions.class);
183: // Old syntax
184: if (expectedExceptions != null) {
185: result = expectedExceptions.getValue();
186: } else {
187: // New syntax
188: ITest testAnnotation = (ITest) finder.findAnnotation(
189: method, ITest.class);
190: if (testAnnotation != null) {
191: Class<?>[] ee = testAnnotation.getExpectedExceptions();
192: if (testAnnotation != null && ee.length > 0) {
193: result = ee;
194: }
195: }
196: }
197:
198: return result;
199: }
200:
201: //
202: // End of public methods
203: // ///
204:
205: public static boolean isEnabled(Class<?> objectClass,
206: IAnnotationFinder finder) {
207: ITest testClassAnnotation = AnnotationHelper.findTest(finder,
208: objectClass);
209:
210: return isEnabled(testClassAnnotation);
211: }
212:
213: public static boolean isEnabled(Method m, IAnnotationFinder finder) {
214: ITest annotation = AnnotationHelper.findTest(finder, m);
215:
216: // If no method annotation, look for one on the class
217: if (null == annotation) {
218: annotation = AnnotationHelper.findTest(finder, m
219: .getDeclaringClass());
220: }
221:
222: return isEnabled(annotation);
223: }
224:
225: public static boolean isEnabled(ITestOrConfiguration test) {
226: return null == test || (null != test && test.getEnabled());
227: }
228:
229: public static ITestNGMethod[] findMethodsThatBelongToGroup(
230: ITestNGMethod method, List<ITestNGMethod> methods,
231: String groupRegexp) {
232: ITestNGMethod[] allMethods = methods
233: .toArray(new ITestNGMethod[methods.size()]);
234: return findMethodsThatBelongToGroup(method, allMethods,
235: groupRegexp);
236: }
237:
238: /**
239: * @return The transitive closure of all the groups/methods included.
240: */
241: public static void findGroupTransitiveClosure(
242: XmlMethodSelector xms, List<ITestNGMethod> includedMethods,
243: List<ITestNGMethod> allMethods, String[] includedGroups,
244: Set<String> outGroups, Set<ITestNGMethod> outMethods) {
245: Map<ITestNGMethod, ITestNGMethod> runningMethods = new HashMap<ITestNGMethod, ITestNGMethod>();
246: for (ITestNGMethod m : includedMethods) {
247: runningMethods.put(m, m);
248: }
249:
250: Map<String, String> runningGroups = new HashMap<String, String>();
251: for (String this Group : includedGroups) {
252: runningGroups.put(this Group, this Group);
253: }
254:
255: boolean keepGoing = true;
256:
257: Map<ITestNGMethod, ITestNGMethod> newMethods = new HashMap<ITestNGMethod, ITestNGMethod>();
258: while (keepGoing) {
259: for (ITestNGMethod m : includedMethods) {
260:
261: //
262: // Depends on groups?
263: // Adds all included methods to runningMethods
264: //
265: String[] ig = m.getGroupsDependedUpon();
266: for (String g : ig) {
267: if (!runningGroups.containsKey(g)) {
268: // Found a new included group, add all the methods it contains to
269: // our outMethod closure
270: runningGroups.put(g, g);
271: ITestNGMethod[] im = findMethodsThatBelongToGroup(
272: m, allMethods, g);
273: for (ITestNGMethod this Method : im) {
274: if (!runningMethods.containsKey(this Method)) {
275: runningMethods.put(this Method,
276: this Method);
277: newMethods.put(this Method, this Method);
278: }
279: }
280: }
281: } // groups
282:
283: //
284: // Depends on methods?
285: // Adds all depended methods to runningMethods
286: //
287: String[] mdu = m.getMethodsDependedUpon();
288: for (String tm : mdu) {
289: ITestNGMethod this Method = findMethodNamed(tm,
290: allMethods);
291: if (this Method != null
292: && !runningMethods.containsKey(this Method)) {
293: runningMethods.put(this Method, this Method);
294: newMethods.put(this Method, this Method);
295: }
296: }
297:
298: } // methods
299:
300: //
301: // Only keep going if new methods have been added
302: //
303: keepGoing = newMethods.size() > 0;
304: includedMethods = new ArrayList<ITestNGMethod>();
305: includedMethods.addAll(newMethods.keySet());
306: newMethods = new HashMap<ITestNGMethod, ITestNGMethod>();
307: } // while keepGoing
308:
309: outMethods.addAll(runningMethods.keySet());
310: outGroups.addAll(runningGroups.keySet());
311: }
312:
313: /**
314: * Extracts the map of groups and their corresponding methods from the <code>classes</code>.
315: */
316: public static Map<String, List<ITestNGMethod>> findGroupsMethods(
317: Collection<ITestClass> classes, boolean before) {
318: Map<String, List<ITestNGMethod>> result = new HashMap<String, List<ITestNGMethod>>();
319: for (ITestClass cls : classes) {
320: ITestNGMethod[] methods = before ? cls
321: .getBeforeGroupsMethods() : cls
322: .getAfterGroupsMethods();
323: for (ITestNGMethod method : methods) {
324: for (String group : before ? method.getBeforeGroups()
325: : method.getAfterGroups()) {
326: List<ITestNGMethod> methodList = result.get(group);
327: if (methodList == null) {
328: methodList = new ArrayList<ITestNGMethod>();
329: result.put(group, methodList);
330: }
331: // NOTE(cbeust, 2007/01/23)
332: // BeforeGroups/AfterGroups methods should only be invoked once.
333: // I should probably use a map instead of a list for a contains(), but
334: // this list should usually be fairly short
335: if (!methodList.contains(method)) {
336: methodList.add(method);
337: }
338: }
339: }
340: }
341:
342: return result;
343: }
344:
345: /**
346: * Extracts the unique list of <code>ITestNGMethod</code>s.
347: */
348: public static List<ITestNGMethod> uniqueMethodList(
349: Collection<List<ITestNGMethod>> methods) {
350: Map<ITestNGMethod, ITestNGMethod> uniq = new HashMap<ITestNGMethod, ITestNGMethod>();
351:
352: for (List<ITestNGMethod> l : methods) {
353: for (ITestNGMethod m : l) {
354: uniq.put(m, m);
355: }
356: }
357:
358: List<ITestNGMethod> result = new ArrayList<ITestNGMethod>();
359: result.addAll(uniq.values());
360:
361: return result;
362: }
363:
364: private static ITestNGMethod findMethodNamed(String tm,
365: List<ITestNGMethod> allMethods) {
366: for (ITestNGMethod m : allMethods) {
367: // TODO(cbeust): account for package
368: String methodName = m.getMethod().getDeclaringClass()
369: .getName()
370: + "." + m.getMethodName();
371: if (methodName.equals(tm))
372: return m;
373: }
374:
375: return null;
376: }
377:
378: private static boolean includeMethod(
379: ITestOrConfiguration annotation, RunInfo runInfo,
380: ITestNGMethod tm, boolean forTests, boolean unique,
381: List<ITestNGMethod> outIncludedMethods) {
382: boolean result = false;
383:
384: if (isEnabled(annotation)) {
385: if (runInfo.includeMethod(tm, forTests)) {
386: if (unique) {
387: if (!isMethodAlreadyPresent(outIncludedMethods, tm)) {
388: result = true;
389: }
390: } else {
391: result = true;
392: }
393: }
394: }
395:
396: return result;
397: }
398:
399: /**
400: * Collect all the methods that belong to the included groups and exclude all
401: * the methods that belong to an excluded group.
402: */
403: private static void collectMethodsByGroup(ITestNGMethod[] methods,
404: boolean forTests, List<ITestNGMethod> outIncludedMethods,
405: List<ITestNGMethod> outExcludedMethods, RunInfo runInfo,
406: IAnnotationFinder finder, boolean unique) {
407: for (ITestNGMethod tm : methods) {
408: boolean in = false;
409: Method m = tm.getMethod();
410: //
411: // @Test method
412: //
413: if (forTests) {
414: in = includeMethod(
415: AnnotationHelper.findTest(finder, m), runInfo,
416: tm, forTests, unique, outIncludedMethods);
417: }
418:
419: //
420: // @Configuration method
421: //
422: else {
423: IConfiguration annotation = AnnotationHelper
424: .findConfiguration(finder, m);
425: if (annotation.getAlwaysRun()) {
426: in = true;
427: } else {
428: in = includeMethod(AnnotationHelper.findTest(
429: finder, m), runInfo, tm, forTests, unique,
430: outIncludedMethods);
431: }
432: }
433: if (in) {
434: outIncludedMethods.add(tm);
435: } else {
436: outExcludedMethods.add(tm);
437: }
438: }
439: }
440:
441: /**
442: * @param result
443: * @param tm
444: * @return true if a method by a similar name (and same hierarchy) already
445: * exists
446: */
447: private static boolean isMethodAlreadyPresent(
448: List<ITestNGMethod> result, ITestNGMethod tm) {
449: for (ITestNGMethod m : result) {
450: Method jm1 = m.getMethod();
451: Method jm2 = tm.getMethod();
452: if (jm1.getName().equals(jm2.getName())) {
453: // Same names, see if they are in the same hierarchy
454: Class<?> c1 = jm1.getDeclaringClass();
455: Class<?> c2 = jm2.getDeclaringClass();
456: if (c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1)) {
457: return true;
458: }
459: }
460: }
461:
462: return false;
463: }
464:
465: static public Graph<ITestNGMethod> topologicalSort(
466: ITestNGMethod[] methods,
467: List<ITestNGMethod> sequentialList,
468: List<ITestNGMethod> parallelList) {
469: Graph<ITestNGMethod> result = new Graph<ITestNGMethod>();
470:
471: if (methods.length == 0)
472: return result;
473:
474: //
475: // Create the graph
476: //
477: for (ITestNGMethod m : methods) {
478: result.addNode(m);
479:
480: Map<ITestNGMethod, ITestNGMethod> predecessors = new HashMap<ITestNGMethod, ITestNGMethod>();
481:
482: String[] methodsDependedUpon = m.getMethodsDependedUpon();
483: String[] groupsDependedUpon = m.getGroupsDependedUpon();
484: if (methodsDependedUpon.length > 0) {
485: String methodName = calculateMethodCanonicalName(m);
486: ITestNGMethod[] methodsNamed = MethodHelper
487: .findMethodsNamed(methodName, methods,
488: methodsDependedUpon);
489: for (ITestNGMethod pred : methodsNamed) {
490: predecessors.put(pred, pred);
491: }
492: }
493: if (groupsDependedUpon.length > 0) {
494: for (String group : groupsDependedUpon) {
495: ITestNGMethod[] methodsThatBelongToGroup = MethodHelper
496: .findMethodsThatBelongToGroup(m, methods,
497: group);
498: for (ITestNGMethod pred : methodsThatBelongToGroup) {
499: predecessors.put(pred, pred);
500: }
501: }
502: }
503:
504: for (ITestNGMethod predecessor : predecessors.values()) {
505: // ppp("METHOD:" + m + " ADDED PREDECESSOR:" + predecessor);
506: result.addPredecessor(m, predecessor);
507: }
508: }
509:
510: result.topologicalSort();
511: sequentialList.addAll(result.getStrictlySortedNodes());
512: parallelList.addAll(result.getIndependentNodes());
513:
514: return result;
515: }
516:
517: public static String calculateMethodCanonicalName(ITestNGMethod m) {
518: return calculateMethodCanonicalName(m.getMethod());
519: }
520:
521: private static String calculateMethodCanonicalName(Method m) {
522: String packageName = m.getDeclaringClass().getName() + "."
523: + m.getName();
524:
525: // Try to find the method on this class or parents
526: Class<?> cls = m.getDeclaringClass();
527: while (cls != Object.class) {
528: try {
529: if (cls.getDeclaredMethod(m.getName(), m
530: .getParameterTypes()) != null) {
531: packageName = cls.getName();
532: break;
533: }
534: } catch (Exception e) {
535: // ignore
536: }
537: cls = cls.getSuperclass();
538: }
539:
540: String result = packageName + "." + m.getName();
541: return result;
542: }
543:
544: private static List<ITestNGMethod> sortMethods(boolean forTests,
545: List<ITestNGMethod> allMethods, IAnnotationFinder finder) {
546: List<ITestNGMethod> sl = new ArrayList<ITestNGMethod>();
547: List<ITestNGMethod> pl = new ArrayList<ITestNGMethod>();
548: ITestNGMethod[] allMethodsArray = allMethods
549: .toArray(new ITestNGMethod[allMethods.size()]);
550:
551: // Fix the method inheritance if these are @Configuration methods to make
552: // sure base classes are invoked before child classes if 'before' and the
553: // other way around if they are 'after'
554: if (!forTests && allMethodsArray.length > 0) {
555: ITestNGMethod m = allMethodsArray[0];
556: boolean before = m.isBeforeClassConfiguration()
557: || m.isBeforeMethodConfiguration()
558: || m.isBeforeSuiteConfiguration()
559: || m.isBeforeTestConfiguration();
560: MethodInheritance.fixMethodInheritance(allMethodsArray,
561: before);
562: }
563:
564: topologicalSort(allMethodsArray, sl, pl);
565:
566: List<ITestNGMethod> result = new ArrayList<ITestNGMethod>();
567: result.addAll(sl);
568: result.addAll(pl);
569: return result;
570: }
571:
572: public static void ppp(String s) {
573: System.out.println("[MethodHelper] " + s);
574: }
575:
576: /**
577: * @param method
578: * @param allTestMethods
579: * @return A sorted array containing all the methods 'method' depends on
580: */
581: public static List<ITestNGMethod> getMethodsDependedUpon(
582: ITestNGMethod method, ITestNGMethod[] methods) {
583: List<ITestNGMethod> parallelList = new ArrayList<ITestNGMethod>();
584: List<ITestNGMethod> sequentialList = new ArrayList<ITestNGMethod>();
585: Graph<ITestNGMethod> g = topologicalSort(methods,
586: sequentialList, parallelList);
587:
588: List<ITestNGMethod> result = g.findPredecessors(method);
589: return result;
590: }
591:
592: public static Object invokeMethod(Method this Method,
593: Object instance, Object[] parameters)
594: throws InvocationTargetException, IllegalAccessException {
595: Object result = null;
596: boolean isPublic = Modifier.isPublic(this Method.getModifiers());
597:
598: try {
599: if (!isPublic) {
600: this Method.setAccessible(true);
601: }
602: result = this Method.invoke(instance, parameters);
603: } finally {
604: if (!isPublic) {
605: this Method.setAccessible(false);
606: }
607: }
608:
609: return result;
610: }
611:
612: public static Iterator<Object[]> createArrayIterator(
613: final Object[][] objects) {
614: ArrayIterator result = new ArrayIterator(objects);
615: return result;
616: }
617:
618: public static Iterator<Object[]> invokeDataProvider(
619: Object instance, Method dataProvider, ITestNGMethod method,
620: ITestContext testContext) {
621: Iterator<Object[]> result = null;
622: Method testMethod = method.getMethod();
623:
624: // If it returns an Object[][], convert it to an Iterable<Object[]>
625: try {
626: List<Object> lParameters = new ArrayList<Object>();
627:
628: // Go through all the parameters declared on this Data Provider and
629: // make sure we have at most one Method and one ITestContext.
630: // Anything else is an error
631: Class<?>[] parameterTypes = dataProvider
632: .getParameterTypes();
633: if (parameterTypes.length > 2) {
634: throw new TestNGException("DataProvider "
635: + dataProvider
636: + " cannot have more than two parameters");
637: }
638:
639: for (Class<?> cls : parameterTypes) {
640: if (cls.equals(Method.class)) {
641: lParameters.add(testMethod);
642: } else if (cls.equals(ITestContext.class)) {
643: lParameters.add(testContext);
644: }
645: }
646: Object[] parameters = lParameters
647: .toArray(new Object[lParameters.size()]);
648:
649: Class<?> returnType = dataProvider.getReturnType();
650: if (Object[][].class.isAssignableFrom(returnType)) {
651: Object[][] oResult = (Object[][]) MethodHelper
652: .invokeMethod(dataProvider, instance,
653: parameters);
654: method.setParameterInvocationCount(oResult.length);
655: result = MethodHelper.createArrayIterator(oResult);
656: } else if (Iterator.class.isAssignableFrom(returnType)) {
657: // Already an Iterable<Object[]>, assign it directly
658: result = (Iterator<Object[]>) MethodHelper
659: .invokeMethod(dataProvider, instance,
660: parameters);
661: } else {
662: throw new TestNGException(
663: "Data Provider "
664: + dataProvider
665: + " must return"
666: + " either Object[][] or Iterator<Object>[], not "
667: + returnType);
668: }
669: } catch (InvocationTargetException e) {
670: throw new TestNGException(e);
671: } catch (IllegalAccessException e) {
672: throw new TestNGException(e);
673: }
674:
675: return result;
676: }
677:
678: public static String calculateMethodCanonicalName(
679: Class<?> methodClass, String methodName) {
680: Set<Method> methods = ClassHelper
681: .getAvailableMethods(methodClass); // TESTNG-139
682: Method result = null;
683: for (Method m : methods) {
684: if (methodName.equals(m.getName())) {
685: result = m;
686: break;
687: }
688: }
689:
690: return result != null ? calculateMethodCanonicalName(result)
691: : null;
692: }
693:
694: /**
695: * Invokes the <code>run</code> method of the <code>IHookable</code>.
696: *
697: * @param instance the instance to invoke the method in
698: * @param parameters the parameters to be passed to <code>IHookCallBack</code>
699: * @param testClass the test class
700: * @param thisMethod the method to be invoked through the <code>IHookCallBack</code>
701: * @param testResult the current <code>ITestResult</code> passed to <code>IHookable.run</code>
702: * @throws NoSuchMethodException
703: * @throws IllegalAccessException
704: * @throws InvocationTargetException
705: * @throws Throwable thrown if the reflective call to <tt>thisMethod</code> results in an exception
706: */
707: public static void invokeHookable(final Object instance,
708: final Object[] parameters, ITestClass testClass,
709: final Method this Method, TestResult testResult)
710: throws NoSuchMethodException, IllegalAccessException,
711: InvocationTargetException, Throwable {
712: Method runMethod = testClass.getRealClass().getMethod("run",
713: new Class[] { IHookCallBack.class, ITestResult.class });
714: final Throwable[] error = new Throwable[1];
715:
716: IHookCallBack callback = new IHookCallBack() {
717: public void runTestMethod(ITestResult tr) {
718: try {
719: invokeMethod(this Method, instance, parameters);
720: } catch (Throwable t) {
721: error[0] = t;
722: tr.setThrowable(t); // make Throwable available to IHookable
723: }
724: }
725: };
726: runMethod.invoke(instance,
727: new Object[] { callback, testResult });
728: if (error[0] != null) {
729: throw error[0];
730: }
731: }
732:
733: /**
734: * Invokes a method on a separate thread in order to allow us to timeout the invocation.
735: * It uses as implementation an <code>Executor</code> and a <code>CountDownLatch</code>.
736: * @param tm the
737: * @param instance
738: * @param parameterValues
739: * @param testResult
740: * @throws InterruptedException
741: * @throws ThreadExecutionException
742: */
743: public static void invokeWithTimeout(ITestNGMethod tm,
744: Object instance, Object[] parameterValues,
745: ITestResult testResult) throws InterruptedException,
746: ThreadExecutionException {
747: // ICountDown done= ThreadUtil.createCountDown(1);
748: // IThreadFactory factory= ThreadUtil.createFactory();
749: IExecutor exec = ThreadUtil.createExecutor(1, tm.getMethod()
750: .getName());
751:
752: InvokeMethodRunnable imr = new InvokeMethodRunnable(tm,
753: instance, parameterValues);
754: IFutureResult future = exec.submitRunnable(imr);
755: exec.shutdown();
756: boolean finished = exec.awaitTermination(tm.getTimeOut());
757:
758: if (!finished) {
759: exec.stopNow();
760: testResult.setThrowable(new ThreadTimeoutException(
761: "Method " + tm.getMethod()
762: + " didn't finish within the time-out "
763: + tm.getTimeOut()));
764: testResult.setStatus(ITestResult.FAILURE);
765: } else {
766: Utils.log("Invoker " + Thread.currentThread().hashCode(),
767: 3, "Method " + tm.getMethod()
768: + " completed within the time-out "
769: + tm.getTimeOut());
770:
771: // We don't need the result from the future but invoking get() on it
772: // will trigger the exception that was thrown, if any
773: future.get();
774: // done.await();
775:
776: testResult.setStatus(ITestResult.SUCCESS); // if no exception till here than SUCCESS
777: }
778: }
779: }
780:
781: // ///
782:
783: class ArrayIterator implements Iterator {
784: private Object[][] m_objects;
785:
786: private int m_count;
787:
788: public ArrayIterator(Object[][] objects) {
789: m_objects = objects;
790: m_count = 0;
791: }
792:
793: public boolean hasNext() {
794: return m_count < m_objects.length;
795: }
796:
797: public Object next() {
798: return m_objects[m_count++];
799: }
800:
801: public void remove() {
802: // TODO Auto-generated method stub
803:
804: }
805:
806: }
|