1 package org.testng.internal; 2 3 import org.testng.ClassMethodMap; 4 import org.testng.IClassListener; 5 import org.testng.IMethodInstance; 6 import org.testng.ITestClass; 7 import org.testng.ITestContext; 8 import org.testng.ITestNGMethod; 9 import org.testng.ITestResult; 10 import org.testng.collections.Lists; 11 import org.testng.internal.thread.ThreadUtil; 12 import org.testng.internal.thread.graph.IWorker; 13 import org.testng.xml.XmlSuite; 14 15 import java.util.HashMap; 16 import java.util.HashSet; 17 import java.util.List; 18 import java.util.Map; 19 import java.util.Set; 20 21 /** 22 * FIXME: reduce contention when this class is used through parallel invocation due to 23 * invocationCount and threadPoolSize by not invoking the @BeforeClass and @AfterClass 24 * which are already invoked on the original method. 25 * 26 * This class implements Runnable and will invoke the ITestMethod passed in its 27 * constructor on its run() method. 28 * 29 * @author <a href="mailto:cedric@beust.com">Cedric Beust</a> 30 * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a> 31 */ 32 public class TestMethodWorker implements IWorker<ITestNGMethod> { 33 // Map of the test methods and their associated instances 34 // It has to be a set because the same method can be passed several times 35 // and associated to a different instance 36 private IMethodInstance[] m_methodInstances; 37 private final IInvoker m_invoker; 38 private final Map<String, String> m_parameters; 39 private final XmlSuite m_suite; 40 private List<ITestResult> m_testResults = Lists.newArrayList(); 41 private final ConfigurationGroupMethods m_groupMethods; 42 private final ClassMethodMap m_classMethodMap; 43 private final ITestContext m_testContext; 44 private final List<IClassListener> m_listeners; 45 TestMethodWorker(IInvoker invoker, IMethodInstance[] testMethods, XmlSuite suite, Map<String, String> parameters, ConfigurationGroupMethods groupMethods, ClassMethodMap classMethodMap, ITestContext testContext, List<IClassListener> listeners)46 public TestMethodWorker(IInvoker invoker, 47 IMethodInstance[] testMethods, 48 XmlSuite suite, 49 Map<String, String> parameters, 50 ConfigurationGroupMethods groupMethods, 51 ClassMethodMap classMethodMap, 52 ITestContext testContext, 53 List<IClassListener> listeners) 54 { 55 m_invoker = invoker; 56 m_methodInstances = testMethods; 57 m_suite = suite; 58 m_parameters = parameters; 59 m_groupMethods = groupMethods; 60 m_classMethodMap = classMethodMap; 61 m_testContext = testContext; 62 m_listeners = listeners; 63 } 64 65 /** 66 * Retrieves the maximum specified timeout of all ITestNGMethods to 67 * be run. 68 * 69 * @return the max timeout or 0 if no timeout was specified 70 */ 71 @Override getTimeOut()72 public long getTimeOut() { 73 long result = 0; 74 for (IMethodInstance mi : m_methodInstances) { 75 ITestNGMethod tm = mi.getMethod(); 76 if (tm.getTimeOut() > result) { 77 result = tm.getTimeOut(); 78 } 79 } 80 81 return result; 82 } 83 84 @Override toString()85 public String toString() { 86 StringBuilder result = new StringBuilder("[Worker thread:" + Thread.currentThread().getId() 87 + " priority:" + getPriority() + " "); 88 89 for (IMethodInstance m : m_methodInstances) { 90 result.append(m.getMethod()).append(" "); 91 } 92 result.append("]"); 93 94 return result.toString(); 95 } 96 97 /** 98 * Run all the ITestNGMethods passed in through the constructor. 99 * 100 * @see java.lang.Runnable#run() 101 */ 102 @Override run()103 public void run() { 104 for (IMethodInstance testMthdInst : m_methodInstances) { 105 ITestNGMethod testMethod = testMthdInst.getMethod(); 106 ITestClass testClass = testMethod.getTestClass(); 107 108 invokeBeforeClassMethods(testClass, testMthdInst); 109 110 // Invoke test method 111 try { 112 invokeTestMethods(testMethod, testMthdInst.getInstance(), m_testContext); 113 } 114 finally { 115 invokeAfterClassMethods(testClass, testMthdInst); 116 } 117 } 118 } 119 invokeTestMethods(ITestNGMethod tm, Object instance, ITestContext testContext)120 protected void invokeTestMethods(ITestNGMethod tm, Object instance, 121 ITestContext testContext) 122 { 123 // Potential bug here: we look up the method index of tm among all 124 // the test methods (not very efficient) but if this method appears 125 // several times and these methods are run in parallel, the results 126 // are unpredictable... Need to think about this more (and make it 127 // more efficient) 128 List<ITestResult> testResults = 129 m_invoker.invokeTestMethods(tm, 130 m_suite, 131 m_parameters, 132 m_groupMethods, 133 instance, 134 testContext); 135 136 if (testResults != null) { 137 m_testResults.addAll(testResults); 138 } 139 } 140 141 /** 142 * Invoke the @BeforeClass methods if not done already 143 * @param testClass 144 * @param mi 145 */ invokeBeforeClassMethods(ITestClass testClass, IMethodInstance mi)146 protected void invokeBeforeClassMethods(ITestClass testClass, IMethodInstance mi) { 147 for (IClassListener listener : m_listeners) { 148 listener.onBeforeClass(testClass, mi); 149 } 150 151 // if no BeforeClass than return immediately 152 // used for parallel case when BeforeClass were already invoked 153 if( (null == m_classMethodMap) || (null == m_classMethodMap.getInvokedBeforeClassMethods())) { 154 return; 155 } 156 ITestNGMethod[] classMethods= testClass.getBeforeClassMethods(); 157 if(null == classMethods || classMethods.length == 0) { 158 return; 159 } 160 161 // the whole invocation must be synchronized as other threads must 162 // get a full initialized test object (not the same for @After) 163 Map<ITestClass, Set<Object>> invokedBeforeClassMethods = 164 m_classMethodMap.getInvokedBeforeClassMethods(); 165 // System.out.println("SYNCHRONIZING ON " + testClass 166 // + " thread:" + Thread.currentThread().getId() 167 // + " invokedMap:" + invokedBeforeClassMethods.hashCode() + " " 168 // + invokedBeforeClassMethods); 169 synchronized(testClass) { 170 Set<Object> instances= invokedBeforeClassMethods.get(testClass); 171 if(null == instances) { 172 instances= new HashSet<>(); 173 invokedBeforeClassMethods.put(testClass, instances); 174 } 175 for(Object instance: mi.getInstances()) { 176 if (! instances.contains(instance)) { 177 instances.add(instance); 178 m_invoker.invokeConfigurations(testClass, 179 testClass.getBeforeClassMethods(), 180 m_suite, 181 m_parameters, 182 null, /* no parameter values */ 183 instance); 184 } 185 } 186 } 187 } 188 189 /** 190 * Invoke the @AfterClass methods if not done already 191 * @param testClass 192 * @param mi 193 */ invokeAfterClassMethods(ITestClass testClass, IMethodInstance mi)194 protected void invokeAfterClassMethods(ITestClass testClass, IMethodInstance mi) { 195 for (IClassListener listener : m_listeners) { 196 listener.onAfterClass(testClass, mi); 197 } 198 199 // if no BeforeClass than return immediately 200 // used for parallel case when BeforeClass were already invoked 201 if( (null == m_classMethodMap) || (null == m_classMethodMap.getInvokedAfterClassMethods()) ) { 202 return; 203 } 204 ITestNGMethod[] afterClassMethods= testClass.getAfterClassMethods(); 205 206 if(null == afterClassMethods || afterClassMethods.length == 0) { 207 return; 208 } 209 210 // 211 // Invoke after class methods if this test method is the last one 212 // 213 List<Object> invokeInstances= Lists.newArrayList(); 214 ITestNGMethod tm= mi.getMethod(); 215 if (m_classMethodMap.removeAndCheckIfLast(tm, mi.getInstance())) { 216 Map<ITestClass, Set<Object>> invokedAfterClassMethods 217 = m_classMethodMap.getInvokedAfterClassMethods(); 218 synchronized(invokedAfterClassMethods) { 219 Set<Object> instances = invokedAfterClassMethods.get(testClass); 220 if(null == instances) { 221 instances= new HashSet<>(); 222 invokedAfterClassMethods.put(testClass, instances); 223 } 224 for(Object inst: mi.getInstances()) { 225 if(! instances.contains(inst)) { 226 invokeInstances.add(inst); 227 } 228 } 229 } 230 231 for(Object inst: invokeInstances) { 232 m_invoker.invokeConfigurations(testClass, 233 afterClassMethods, 234 m_suite, 235 m_parameters, 236 null, /* no parameter values */ 237 inst); 238 } 239 } 240 } 241 indexOf(ITestNGMethod tm, ITestNGMethod[] allTestMethods)242 protected int indexOf(ITestNGMethod tm, ITestNGMethod[] allTestMethods) { 243 for (int i = 0; i < allTestMethods.length; i++) { 244 if (allTestMethods[i] == tm) { 245 return i; 246 } 247 } 248 return -1; 249 } 250 getTestResults()251 public List<ITestResult> getTestResults() { 252 return m_testResults; 253 } 254 ppp(String s)255 private void ppp(String s) { 256 Utils.log("TestMethodWorker", 2, ThreadUtil.currentThreadInfo() + ":" + s); 257 } 258 259 @Override getTasks()260 public List<ITestNGMethod> getTasks() 261 { 262 List<ITestNGMethod> result = Lists.newArrayList(); 263 for (IMethodInstance m : m_methodInstances) { 264 result.add(m.getMethod()); 265 } 266 return result; 267 } 268 269 @Override compareTo(IWorker<ITestNGMethod> other)270 public int compareTo(IWorker<ITestNGMethod> other) { 271 return getPriority() - other.getPriority(); 272 } 273 274 /** 275 * The priority of a worker is the priority of the first method it's going to run. 276 */ 277 @Override getPriority()278 public int getPriority() { 279 return m_methodInstances.length > 0 280 ? m_methodInstances[0].getMethod().getPriority() 281 : 0; 282 } 283 } 284 285 /** 286 * Extends {@code TestMethodWorker} and is used to work on only a single method 287 * instance 288 */ 289 class SingleTestMethodWorker extends TestMethodWorker { 290 private static final ConfigurationGroupMethods EMPTY_GROUP_METHODS = 291 new ConfigurationGroupMethods(new ITestNGMethod[0], 292 new HashMap<String, List<ITestNGMethod>>(), new HashMap<String, List<ITestNGMethod>>()); 293 SingleTestMethodWorker(IInvoker invoker, MethodInstance testMethod, XmlSuite suite, Map<String, String> parameters, ITestContext testContext, List<IClassListener> listeners)294 public SingleTestMethodWorker(IInvoker invoker, 295 MethodInstance testMethod, 296 XmlSuite suite, 297 Map<String, String> parameters, 298 ITestContext testContext, 299 List<IClassListener> listeners) 300 { 301 super(invoker, 302 new MethodInstance[] {testMethod}, 303 suite, 304 parameters, 305 EMPTY_GROUP_METHODS, 306 null, 307 testContext, 308 listeners); 309 } 310 } 311