1 package org.testng.internal; 2 3 import org.testng.IConfigurable; 4 import org.testng.IConfigureCallBack; 5 import org.testng.IHookCallBack; 6 import org.testng.IHookable; 7 import org.testng.ITestContext; 8 import org.testng.ITestNGMethod; 9 import org.testng.ITestResult; 10 import org.testng.TestNGException; 11 import org.testng.annotations.DataProvider; 12 import org.testng.collections.Lists; 13 import org.testng.internal.annotations.IAnnotationFinder; 14 import org.testng.internal.collections.Pair; 15 import org.testng.internal.thread.IExecutor; 16 import org.testng.internal.thread.IFutureResult; 17 import org.testng.internal.thread.ThreadExecutionException; 18 import org.testng.internal.thread.ThreadTimeoutException; 19 import org.testng.internal.thread.ThreadUtil; 20 import org.testng.xml.XmlSuite; 21 22 import java.lang.reflect.Constructor; 23 import java.lang.reflect.InvocationTargetException; 24 import java.lang.reflect.Method; 25 import java.lang.reflect.Modifier; 26 import java.util.ArrayList; 27 import java.util.Collection; 28 import java.util.Iterator; 29 import java.util.List; 30 31 /** 32 * Collections of helper methods to help deal with invocation of TestNG methods 33 * 34 * @author Cedric Beust <cedric@beust.com> 35 * @author nullin <nalin.makar * gmail.com> 36 * 37 */ 38 public class MethodInvocationHelper { 39 invokeMethod(Method thisMethod, Object instance, Object[] parameters)40 protected static Object invokeMethod(Method thisMethod, Object instance, Object[] parameters) 41 throws InvocationTargetException, IllegalAccessException { 42 Utils.checkInstanceOrStatic(instance, thisMethod); 43 44 // TESTNG-326, allow IObjectFactory to load from non-standard classloader 45 // If the instance has a different classloader, its class won't match the 46 // method's class 47 if (instance == null || !thisMethod.getDeclaringClass().isAssignableFrom(instance.getClass())) { 48 // for some reason, we can't call this method on this class 49 // is it static? 50 boolean isStatic = Modifier.isStatic(thisMethod.getModifiers()); 51 if (!isStatic) { 52 // not static, so grab a method with the same name and signature in this case 53 Class<?> clazz = instance.getClass(); 54 try { 55 thisMethod = clazz.getMethod(thisMethod.getName(), thisMethod.getParameterTypes()); 56 } catch (Exception e) { 57 // ignore, the method may be private 58 boolean found = false; 59 for (; clazz != null; clazz = clazz.getSuperclass()) { 60 try { 61 thisMethod = clazz.getDeclaredMethod(thisMethod.getName(), 62 thisMethod.getParameterTypes()); 63 found = true; 64 break; 65 } catch (Exception e2) { 66 } 67 } 68 if (!found) { 69 // should we assert here? Or just allow it to fail on invocation? 70 if (thisMethod.getDeclaringClass().getName().equals(instance.getClass().getName())) { 71 throw new RuntimeException("Can't invoke method " + thisMethod 72 + ", probably due to classloader mismatch"); 73 } 74 throw new RuntimeException("Can't invoke method " + thisMethod 75 + " on this instance of " + instance.getClass() + " due to class mismatch"); 76 } 77 } 78 } 79 } 80 81 synchronized(thisMethod) { 82 if (! Modifier.isPublic(thisMethod.getModifiers())) { 83 thisMethod.setAccessible(true); 84 } 85 } 86 return thisMethod.invoke(instance, parameters); 87 } 88 invokeDataProvider(Object instance, Method dataProvider, ITestNGMethod method, ITestContext testContext, Object fedInstance, IAnnotationFinder annotationFinder)89 protected static Iterator<Object[]> invokeDataProvider(Object instance, Method dataProvider, 90 ITestNGMethod method, ITestContext testContext, Object fedInstance, 91 IAnnotationFinder annotationFinder) { 92 Iterator<Object[]> result; 93 final ConstructorOrMethod com = method.getConstructorOrMethod(); 94 95 // If it returns an Object[][], convert it to an Iterable<Object[]> 96 try { 97 List<Object> lParameters = Lists.newArrayList(); 98 99 // Go through all the parameters declared on this Data Provider and 100 // make sure we have at most one Method and one ITestContext. 101 // Anything else is an error 102 Class<?>[] parameterTypes = dataProvider.getParameterTypes(); 103 104 final Collection<Pair<Integer, Class<?>>> unresolved = new ArrayList<>(parameterTypes.length); 105 int i = 0; 106 for (Class<?> cls : parameterTypes) { 107 boolean isTestInstance = annotationFinder.hasTestInstance(dataProvider, i++); 108 if (cls.equals(Method.class)) { 109 lParameters.add(com.getMethod()); 110 } else if (cls.equals(Constructor.class)) { 111 lParameters.add(com.getConstructor()); 112 } else if (cls.equals(ConstructorOrMethod.class)) { 113 lParameters.add(com); 114 } else if (cls.equals(ITestNGMethod.class)) { 115 lParameters.add(method); 116 } else if (cls.equals(ITestContext.class)) { 117 lParameters.add(testContext); 118 } else if (isTestInstance) { 119 lParameters.add(fedInstance); 120 } else { 121 unresolved.add(new Pair<Integer, Class<?>>(i, cls)); 122 } 123 } 124 if (!unresolved.isEmpty()) { 125 final StringBuilder sb = new StringBuilder(); 126 sb.append("Some DataProvider ").append(dataProvider).append(" parameters unresolved: "); 127 for (Pair<Integer, Class<?>> pair : unresolved) { 128 sb.append(" at ").append(pair.first()).append(" type ").append(pair.second()).append("\n"); 129 } 130 throw new TestNGException(sb.toString()); 131 } 132 133 Object[] parameters = lParameters.toArray(new Object[lParameters.size()]); 134 135 Class<?> returnType = dataProvider.getReturnType(); 136 if (Object[][].class.isAssignableFrom(returnType)) { 137 Object[][] originalResult = (Object[][]) invokeMethod(dataProvider, instance, parameters); 138 139 // If the data provider is restricting the indices to return, filter them out 140 int[] indices = dataProvider.getAnnotation(DataProvider.class).indices(); 141 Object[][] oResult; 142 if (indices.length > 0) { 143 oResult = new Object[indices.length][]; 144 for (int j = 0; j < indices.length; j++) { 145 oResult[j] = originalResult[indices[j]]; 146 } 147 } else { 148 oResult = originalResult; 149 } 150 151 method.setParameterInvocationCount(oResult.length); 152 result = MethodHelper.createArrayIterator(oResult); 153 } else if (Iterator.class.isAssignableFrom(returnType)) { 154 // Already an Iterator<Object[]>, assign it directly 155 result = (Iterator<Object[]>) invokeMethod(dataProvider, instance, parameters); 156 } else { 157 throw new TestNGException("Data Provider " + dataProvider + " must return" 158 + " either Object[][] or Iterator<Object>[], not " + returnType); 159 } 160 } catch (InvocationTargetException | IllegalAccessException e) { 161 // Don't throw TestNGException here or this test won't be reported as a 162 // skip or failure 163 throw new RuntimeException(e.getCause()); 164 } 165 166 return result; 167 } 168 169 /** 170 * Invokes the <code>run</code> method of the <code>IHookable</code>. 171 * 172 * @param testInstance 173 * the instance to invoke the method in 174 * @param parameters 175 * the parameters to be passed to <code>IHookCallBack</code> 176 * @param thisMethod 177 * the method to be invoked through the <code>IHookCallBack</code> 178 * @param testResult 179 * the current <code>ITestResult</code> passed to 180 * <code>IHookable.run</code> 181 * @throws NoSuchMethodException 182 * @throws IllegalAccessException 183 * @throws InvocationTargetException 184 * @throws Throwable 185 * thrown if the reflective call to 186 * <tt>thisMethod</code> results in an exception 187 */ invokeHookable(final Object testInstance, final Object[] parameters, final IHookable hookable, final Method thisMethod, final ITestResult testResult)188 protected static void invokeHookable(final Object testInstance, final Object[] parameters, 189 final IHookable hookable, final Method thisMethod, 190 final ITestResult testResult) throws Throwable { 191 final Throwable[] error = new Throwable[1]; 192 193 IHookCallBack callback = new IHookCallBack() { 194 @Override 195 public void runTestMethod(ITestResult tr) { 196 try { 197 invokeMethod(thisMethod, testInstance, parameters); 198 } catch (Throwable t) { 199 error[0] = t; 200 tr.setThrowable(t); // make Throwable available to IHookable 201 } 202 } 203 204 @Override 205 public Object[] getParameters() { 206 return parameters; 207 } 208 }; 209 hookable.run(callback, testResult); 210 if (error[0] != null) { 211 throw error[0]; 212 } 213 } 214 215 /** 216 * Invokes a method on a separate thread in order to allow us to timeout the 217 * invocation. It uses as implementation an <code>Executor</code> and a 218 * <code>CountDownLatch</code>. 219 */ invokeWithTimeout(ITestNGMethod tm, Object instance, Object[] parameterValues, ITestResult testResult)220 protected static void invokeWithTimeout(ITestNGMethod tm, Object instance, 221 Object[] parameterValues, ITestResult testResult) 222 throws InterruptedException, ThreadExecutionException { 223 invokeWithTimeout(tm, instance, parameterValues, testResult, null); 224 } 225 invokeWithTimeout(ITestNGMethod tm, Object instance, Object[] parameterValues, ITestResult testResult, IHookable hookable)226 protected static void invokeWithTimeout(ITestNGMethod tm, Object instance, 227 Object[] parameterValues, ITestResult testResult, IHookable hookable) 228 throws InterruptedException, ThreadExecutionException { 229 if (ThreadUtil.isTestNGThread() && testResult.getTestContext().getCurrentXmlTest().getParallel() != XmlSuite.ParallelMode.TESTS) { 230 // We are already running in our own executor, don't create another one (or we will 231 // lose the time out of the enclosing executor). 232 invokeWithTimeoutWithNoExecutor(tm, instance, parameterValues, testResult, hookable); 233 } else { 234 invokeWithTimeoutWithNewExecutor(tm, instance, parameterValues, testResult, hookable); 235 } 236 } 237 invokeWithTimeoutWithNoExecutor(ITestNGMethod tm, Object instance, Object[] parameterValues, ITestResult testResult, IHookable hookable)238 private static void invokeWithTimeoutWithNoExecutor(ITestNGMethod tm, Object instance, 239 Object[] parameterValues, ITestResult testResult, IHookable hookable) { 240 241 InvokeMethodRunnable imr = new InvokeMethodRunnable(tm, instance, parameterValues, hookable, testResult); 242 try { 243 imr.run(); 244 testResult.setStatus(ITestResult.SUCCESS); 245 } catch (Exception ex) { 246 testResult.setThrowable(ex.getCause()); 247 testResult.setStatus(ITestResult.FAILURE); 248 } 249 } 250 invokeWithTimeoutWithNewExecutor(ITestNGMethod tm, Object instance, Object[] parameterValues, ITestResult testResult, IHookable hookable)251 private static void invokeWithTimeoutWithNewExecutor(ITestNGMethod tm, Object instance, 252 Object[] parameterValues, ITestResult testResult, IHookable hookable) 253 throws InterruptedException, ThreadExecutionException { 254 IExecutor exec = ThreadUtil.createExecutor(1, tm.getMethodName()); 255 256 InvokeMethodRunnable imr = new InvokeMethodRunnable(tm, instance, parameterValues, hookable, testResult); 257 IFutureResult future = exec.submitRunnable(imr); 258 exec.shutdown(); 259 long realTimeOut = MethodHelper.calculateTimeOut(tm); 260 boolean finished = exec.awaitTermination(realTimeOut); 261 262 if (!finished) { 263 exec.stopNow(); 264 ThreadTimeoutException exception = new ThreadTimeoutException("Method " 265 + tm.getClass().getName() + "." + tm.getMethodName() + "()" 266 + " didn't finish within the time-out " + realTimeOut); 267 exception.setStackTrace(exec.getStackTraces()[0]); 268 testResult.setThrowable(exception); 269 testResult.setStatus(ITestResult.FAILURE); 270 } else { 271 Utils.log("Invoker " + Thread.currentThread().hashCode(), 3, "Method " + tm.getMethodName() 272 + " completed within the time-out " + tm.getTimeOut()); 273 274 // We don't need the result from the future but invoking get() on it 275 // will trigger the exception that was thrown, if any 276 future.get(); 277 // done.await(); 278 279 testResult.setStatus(ITestResult.SUCCESS); // if no exception till here 280 // than SUCCESS 281 } 282 } 283 invokeConfigurable(final Object instance, final Object[] parameters, final IConfigurable configurableInstance, final Method thisMethod, final ITestResult testResult)284 protected static void invokeConfigurable(final Object instance, final Object[] parameters, 285 final IConfigurable configurableInstance, final Method thisMethod, 286 final ITestResult testResult) throws Throwable { 287 final Throwable[] error = new Throwable[1]; 288 289 IConfigureCallBack callback = new IConfigureCallBack() { 290 @Override 291 public void runConfigurationMethod(ITestResult tr) { 292 try { 293 invokeMethod(thisMethod, instance, parameters); 294 } catch (Throwable t) { 295 error[0] = t; 296 tr.setThrowable(t); // make Throwable available to IConfigurable 297 } 298 } 299 300 @Override 301 public Object[] getParameters() { 302 return parameters; 303 } 304 }; 305 configurableInstance.run(callback, testResult); 306 if (error[0] != null) { 307 throw error[0]; 308 } 309 } 310 311 } 312