001: package org.testng.internal;
002:
003: import java.util.ArrayList;
004: import java.util.HashMap;
005: import java.util.HashSet;
006: import java.util.List;
007: import java.util.Map;
008: import java.util.Set;
009:
010: import org.testng.ClassMethodMap;
011: import org.testng.ITestClass;
012: import org.testng.ITestContext;
013: import org.testng.ITestNGMethod;
014: import org.testng.ITestResult;
015: import org.testng.internal.thread.ThreadUtil;
016: import org.testng.xml.XmlSuite;
017:
018: /**
019: * FIXME: reduce contention when this class is used through parallel invocation due to
020: * invocationCount and threadPoolSize by not invoking the @BeforeClass and @AfterClass
021: * which are already invoked on the original method.
022: *
023: * This class implements Runnable and will invoke the ITestMethod passed in its
024: * constructor on its run() method.
025: *
026: * @author <a href="mailto:cedric@beust.com">Cedric Beust</a>
027: * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
028: */
029: public class TestMethodWorker implements IMethodWorker {
030: // Map of the test methods and their associated instances
031: // It has to be a set because the same method can be passed several times
032: // and associated to a different instance
033: protected MethodInstance[] m_testMethods;
034: protected IInvoker m_invoker = null;
035: protected Map<String, String> m_parameters = null;
036: protected XmlSuite m_suite = null;
037: // protected Map<ITestClass, Set<Object>> m_invokedBeforeClassMethods = null;
038: // protected Map<ITestClass, Set<Object>> m_invokedAfterClassMethods = null;
039: protected ITestNGMethod[] m_allTestMethods;
040: protected List<ITestResult> m_testResults = new ArrayList<ITestResult>();
041: protected ConfigurationGroupMethods m_groupMethods = null;
042: protected ClassMethodMap m_classMethodMap = null;
043: private ITestContext m_testContext = null;
044:
045: public TestMethodWorker(IInvoker invoker,
046: MethodInstance[] testMethods, XmlSuite suite,
047: Map<String, String> parameters,
048: ITestNGMethod[] allTestMethods,
049: ConfigurationGroupMethods groupMethods,
050: ClassMethodMap classMethodMap, ITestContext testContext) {
051: m_invoker = invoker;
052: m_testMethods = testMethods;
053: m_suite = suite;
054: m_parameters = parameters;
055: // m_invokedBeforeClassMethods = invokedBeforeClassMethods;
056: // m_invokedAfterClassMethods = invokedAfterClassMethods;
057: m_allTestMethods = allTestMethods;
058: m_groupMethods = groupMethods;
059: m_classMethodMap = classMethodMap;
060: m_testContext = testContext;
061: }
062:
063: /**
064: * Retrieves the maximum specified timeout of all ITestNGMethods to
065: * be run.
066: *
067: * @return the max timeout or 0 if no timeout was specified
068: */
069: public long getMaxTimeOut() {
070: long result = 0;
071: for (MethodInstance mi : m_testMethods) {
072: ITestNGMethod tm = mi.getMethod();
073: if (tm.getTimeOut() > result) {
074: result = tm.getTimeOut();
075: }
076: }
077:
078: return result;
079: }
080:
081: @Override
082: public String toString() {
083: return "[Worker on thread:" + Thread.currentThread().getId()
084: + " " + m_testMethods[0].getMethod() + "]";
085: }
086:
087: /**
088: * Run all the ITestNGMethods passed in through the constructor.
089: *
090: * @see java.lang.Runnable#run()
091: */
092: public void run() {
093: // Using an index here because we need to tell the invoker
094: // the index of the current method
095: for (int indexMethod = 0; indexMethod < m_testMethods.length; indexMethod++) {
096: ITestNGMethod tm = m_testMethods[indexMethod].getMethod();
097:
098: ITestClass testClass = tm.getTestClass();
099:
100: invokeBeforeClassMethods(testClass,
101: m_testMethods[indexMethod]);
102:
103: //
104: // Invoke test method
105: //
106: try {
107: invokeTestMethods(tm, m_testMethods[indexMethod]
108: .getInstances(), m_testContext);
109: } finally {
110: invokeAfterClassMethods(testClass,
111: m_testMethods[indexMethod]);
112: }
113: }
114: }
115:
116: protected void invokeTestMethods(ITestNGMethod tm,
117: Object[] instances, ITestContext testContext) {
118: // Potential bug here: we look up the method index of tm among all
119: // the test methods (not very efficient) but if this method appears
120: // several times and these methods are run in parallel, the results
121: // are unpredictable... Need to think about this more (and make it
122: // more efficient)
123: List<ITestResult> testResults = m_invoker.invokeTestMethods(tm,
124: m_allTestMethods, indexOf(tm, m_allTestMethods),
125: m_suite, m_parameters, m_groupMethods, instances,
126: testContext);
127:
128: if (testResults != null) {
129: m_testResults.addAll(testResults);
130: }
131: }
132:
133: //
134: // Invoke the before class methods if not done already
135: //
136: protected void invokeBeforeClassMethods(ITestClass testClass,
137: MethodInstance mi) {
138: // if no BeforeClass than return immediately
139: // used for parallel case when BeforeClass were already invoked
140: if ((null == m_classMethodMap)
141: || (null == m_classMethodMap
142: .getInvokedBeforeClassMethods())) {
143: return;
144: }
145: ITestNGMethod[] classMethods = testClass
146: .getBeforeClassMethods();
147: if (null == classMethods || classMethods.length == 0) {
148: return;
149: }
150:
151: // the whole invocation must be synchronized as other threads must
152: // get a full initialized test object (not the same for @After)
153: Map<ITestClass, Set<Object>> invokedBeforeClassMethods = m_classMethodMap
154: .getInvokedBeforeClassMethods();
155: synchronized (invokedBeforeClassMethods) {
156: Set<Object> instances = invokedBeforeClassMethods
157: .get(testClass);
158: if (null == instances) {
159: instances = new HashSet<Object>();
160: invokedBeforeClassMethods.put(testClass, instances);
161: }
162: for (Object instance : mi.getInstances()) {
163: if (!instances.contains(instance)) {
164: instances.add(instance);
165: m_invoker.invokeConfigurations(testClass, testClass
166: .getBeforeClassMethods(), m_suite,
167: m_parameters, instance);
168: }
169: }
170: }
171: }
172:
173: protected void invokeAfterClassMethods(ITestClass testClass,
174: MethodInstance mi) {
175: // if no BeforeClass than return immediately
176: // used for parallel case when BeforeClass were already invoked
177: if ((null == m_classMethodMap)
178: || (null == m_classMethodMap
179: .getInvokedAfterClassMethods())) {
180: return;
181: }
182: ITestNGMethod[] afterClassMethods = testClass
183: .getAfterClassMethods();
184:
185: if (null == afterClassMethods || afterClassMethods.length == 0) {
186: return;
187: }
188:
189: //
190: // Invoke after class methods if this test method is the last one
191: //
192: List<Object> invokeInstances = new ArrayList<Object>();
193: ITestNGMethod tm = mi.getMethod();
194: if (m_classMethodMap.removeAndCheckIfLast(tm)) {
195: Map<ITestClass, Set<Object>> invokedAfterClassMethods = m_classMethodMap
196: .getInvokedAfterClassMethods();
197: synchronized (invokedAfterClassMethods) {
198: Set<Object> instances = invokedAfterClassMethods
199: .get(testClass);
200: if (null == instances) {
201: instances = new HashSet<Object>();
202: invokedAfterClassMethods.put(testClass, instances);
203: }
204: for (Object inst : mi.getInstances()) {
205: if (!instances.contains(inst)) {
206: invokeInstances.add(inst);
207: }
208: }
209: }
210:
211: for (Object inst : invokeInstances) {
212: m_invoker.invokeConfigurations(testClass,
213: afterClassMethods, m_suite, m_parameters, inst);
214: }
215: }
216: }
217:
218: protected int indexOf(ITestNGMethod tm,
219: ITestNGMethod[] allTestMethods) {
220: for (int i = 0; i < allTestMethods.length; i++) {
221: if (allTestMethods[i] == tm)
222: return i;
223: }
224: return -1;
225: }
226:
227: public List<ITestResult> getTestResults() {
228: return m_testResults;
229: }
230:
231: private void ppp(String s) {
232: Utils.log("TestMethodWorker", 2, ThreadUtil.currentThreadInfo()
233: + ":" + s);
234: }
235:
236: public void setAllTestMethods(ITestNGMethod[] allTestMethods) {
237: m_allTestMethods = allTestMethods;
238: }
239: }
240:
241: class SingleTestMethodWorker extends TestMethodWorker {
242: private static final ConfigurationGroupMethods EMPTY_GROUP_METHODS = new ConfigurationGroupMethods(
243: new ITestNGMethod[0],
244: new HashMap<String, List<ITestNGMethod>>(),
245: new HashMap<String, List<ITestNGMethod>>());
246:
247: public SingleTestMethodWorker(IInvoker invoker,
248: MethodInstance testMethod, XmlSuite suite,
249: Map<String, String> parameters,
250: ITestNGMethod[] allTestMethods, ITestContext testContext) {
251: super (invoker, new MethodInstance[] { testMethod }, suite,
252: parameters, allTestMethods, EMPTY_GROUP_METHODS, null,
253: testContext);
254: }
255:
256: protected void invokeAfterClassMethods(ITestClass testClass,
257: ITestNGMethod tm) {
258: // HINT: do nothing
259: }
260:
261: protected void invokeBeforeClassMethods(ITestClass testClass) {
262: // HINT: do nothing
263: }
264: }
|