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