MethodHelper.java revision 56691e893563aefdb066bc7e4bd44c43efe33c63
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.getMethod() 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