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