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