001: package org.testng;
002:
003: import java.io.File;
004: import java.io.Serializable;
005: import java.lang.reflect.Method;
006: import java.util.ArrayList;
007: import java.util.Collection;
008: import java.util.HashMap;
009: import java.util.LinkedHashMap;
010: import java.util.List;
011: import java.util.Map;
012:
013: import org.testng.internal.AnnotationTypeEnum;
014: import org.testng.internal.IInvoker;
015: import org.testng.internal.Utils;
016: import org.testng.internal.annotations.IAnnotationFinder;
017: import org.testng.internal.thread.ThreadUtil;
018: import org.testng.reporters.JUnitXMLReporter;
019: import org.testng.reporters.TestHTMLReporter;
020: import org.testng.reporters.TextReporter;
021: import org.testng.xml.XmlSuite;
022: import org.testng.xml.XmlTest;
023:
024: /**
025: * <CODE>SuiteRunner</CODE> is responsible for running all the tests included in one
026: * suite. The test start is triggered by {@link #run()} method.
027: *
028: * @author Cedric Beust, Apr 26, 2004
029: * @author <a href = "mailto:the_mindstorm@evolva.ro">Alex Popescu</a>
030: */
031: public class SuiteRunner implements ISuite, Serializable {
032:
033: /* generated */
034: private static final long serialVersionUID = 5284208932089503131L;
035:
036: private static final String DEFAULT_OUTPUT_DIR = "test-output";
037:
038: private Map<String, ISuiteResult> m_suiteResults = new HashMap<String, ISuiteResult>();
039: transient private List<TestRunner> m_testRunners = new ArrayList<TestRunner>();
040: transient private List<ISuiteListener> m_listeners = new ArrayList<ISuiteListener>();
041: transient private TestListenerAdapter m_textReporter = new TestListenerAdapter();
042:
043: private String m_outputDir; // DEFAULT_OUTPUT_DIR;
044: private XmlSuite m_suite;
045:
046: transient private List<ITestListener> m_testlisteners = new ArrayList<ITestListener>();
047: transient private ITestRunnerFactory m_tmpRunnerFactory;
048:
049: transient private ITestRunnerFactory m_runnerFactory;
050: transient private boolean m_useDefaultListeners = true;
051:
052: // The remote host where this suite was run, or null if run locally
053: private String m_host;
054:
055: // The suite can run in some cases both JAVADOC and JDK5 annotated tests
056: transient private IAnnotationFinder m_javadocAnnotationFinder;
057: transient private IAnnotationFinder m_jdkAnnotationFinder;
058:
059: transient private IObjectFactory m_objectFactory;
060: transient private Boolean m_skipFailedInvocationCounts = Boolean.FALSE;
061:
062: // transient private IAnnotationTransformer m_annotationTransformer = null;
063:
064: public SuiteRunner(XmlSuite suite, String outputDir,
065: IAnnotationFinder[] finders) {
066: this (suite, outputDir, null, false, finders);
067: }
068:
069: public SuiteRunner(XmlSuite suite, String outputDir,
070: ITestRunnerFactory runnerFactory,
071: IAnnotationFinder[] finders) {
072: this (suite, outputDir, runnerFactory, false, finders);
073: }
074:
075: public SuiteRunner(XmlSuite suite, String outputDir,
076: ITestRunnerFactory runnerFactory,
077: boolean useDefaultListeners, IAnnotationFinder[] finders) {
078: this (suite, outputDir, runnerFactory, useDefaultListeners,
079: finders, null);
080: }
081:
082: public SuiteRunner(XmlSuite suite, String outputDir,
083: ITestRunnerFactory runnerFactory,
084: boolean useDefaultListeners, IAnnotationFinder[] finders,
085: IObjectFactory factory) {
086: init(suite, outputDir, runnerFactory, useDefaultListeners,
087: finders, factory);
088: }
089:
090: private void init(XmlSuite suite, String outputDir,
091: ITestRunnerFactory runnerFactory,
092: boolean useDefaultListeners, IAnnotationFinder[] finders,
093: IObjectFactory factory) {
094: m_suite = suite;
095: m_useDefaultListeners = useDefaultListeners;
096: m_tmpRunnerFactory = runnerFactory;
097: // TODO: very ugly code, but how to change it?
098: if (null != finders) {
099: m_javadocAnnotationFinder = finders[0];
100:
101: if (finders.length == 2) {
102: m_jdkAnnotationFinder = finders[1];
103: }
104: }
105: setOutputDir(outputDir);
106: m_objectFactory = factory;
107: if (m_objectFactory == null) {
108: m_objectFactory = suite.getObjectFactory();
109: }
110: }
111:
112: public XmlSuite getXmlSuite() {
113: return m_suite;
114: }
115:
116: public String getName() {
117: return m_suite.getName();
118: }
119:
120: public void setObjectFactory(IObjectFactory objectFactory) {
121: m_objectFactory = objectFactory;
122: }
123:
124: public void setTestListeners(List<ITestListener> testlisteners) {
125: m_testlisteners = testlisteners;
126: }
127:
128: public void setReportResults(boolean reportResults) {
129: m_useDefaultListeners = reportResults;
130: }
131:
132: private void invokeListeners(boolean start) {
133: for (ISuiteListener sl : m_listeners) {
134: if (start) {
135: sl.onStart(this );
136: } else {
137: sl.onFinish(this );
138: }
139: }
140: }
141:
142: private void setOutputDir(String outputdir) {
143: if (((null == outputdir) || "".equals(outputdir.trim()))
144: && m_useDefaultListeners) {
145: outputdir = DEFAULT_OUTPUT_DIR;
146: }
147:
148: m_outputDir = (null != outputdir) ? new File(outputdir)
149: .getAbsolutePath() : null;
150: }
151:
152: private void lazyInit() {
153: m_runnerFactory = buildRunnerFactory(m_testlisteners);
154: }
155:
156: protected ITestRunnerFactory buildRunnerFactory(List testListeners) {
157: ITestRunnerFactory factory = null;
158:
159: if (null == m_tmpRunnerFactory) {
160: factory = new DefaultTestRunnerFactory(
161: m_testlisteners
162: .toArray(new ITestListener[m_testlisteners
163: .size()]), m_useDefaultListeners,
164: m_skipFailedInvocationCounts);
165: } else {
166: factory = new ProxyTestRunnerFactory(
167: m_testlisteners
168: .toArray(new ITestListener[m_testlisteners
169: .size()]), m_tmpRunnerFactory);
170: }
171:
172: return factory;
173: }
174:
175: public String getParallel() {
176: return m_suite.getParallel();
177: }
178:
179: public void run() {
180: lazyInit();
181:
182: invokeListeners(true /* start */);
183: try {
184: privateRun();
185: } finally {
186: invokeListeners(false /* start */);
187:
188: //
189: // Display the final statistics
190: //
191: if (m_suite.getVerbose() > 0) {
192: int total = m_textReporter.getAllTestMethods().length;
193: List<ITestResult> skipped = m_textReporter
194: .getSkippedTests();
195: List<ITestResult> failed = m_textReporter
196: .getFailedTests();
197: int confFailures = m_textReporter
198: .getConfigurationFailures().size();
199: int confSkips = m_textReporter.getConfigurationSkips()
200: .size();
201: StringBuffer bufLog = new StringBuffer(getName());
202: bufLog.append("\nTotal tests run: ").append(total)
203: .append(", Failures: ").append(failed.size())
204: .append(", Skips: ").append(skipped.size());
205: ;
206: if (confFailures > 0 || confSkips > 0) {
207: bufLog.append("\nConfiguration Failures: ").append(
208: confFailures).append(", Skips: ").append(
209: confSkips);
210: }
211:
212: System.out
213: .println("\n===============================================\n"
214: + bufLog.toString()
215: + "\n===============================================\n");
216: }
217: }
218: }
219:
220: private void privateRun() {
221:
222: // Map for unicity, Linked for guaranteed order
223: Map<Method, ITestNGMethod> beforeSuiteMethods = new LinkedHashMap<Method, ITestNGMethod>();
224: Map<Method, ITestNGMethod> afterSuiteMethods = new LinkedHashMap<Method, ITestNGMethod>();
225:
226: IInvoker invoker = null;
227:
228: //
229: // First, we create all the test runners so we can discover all the ITestClasses
230: //
231: for (XmlTest test : m_suite.getTests()) {
232: TestRunner tr = m_runnerFactory.newTestRunner(this , test);
233:
234: // Reuse the same text reporter so we can accumulate all the results
235: // (this is used to display the final suite report at the end)
236: tr.addListener(m_textReporter);
237: m_testRunners.add(tr);
238:
239: // TODO: Code smell. Invoker should belong to SuiteRunner, not TestRunner
240: // -- cbeust
241: invoker = tr.getInvoker();
242:
243: for (ITestNGMethod m : tr.getBeforeSuiteMethods()) {
244: beforeSuiteMethods.put(m.getMethod(), m);
245: }
246:
247: for (ITestNGMethod m : tr.getAfterSuiteMethods()) {
248: afterSuiteMethods.put(m.getMethod(), m);
249: }
250: }
251:
252: //
253: // Invoke beforeSuite methods (the invoker can be null
254: // if the suite we are currently running only contains
255: // a <file-suite> tag and no real tests)
256: //
257: if (invoker != null) {
258: if (beforeSuiteMethods.values().size() > 0) {
259: invoker.invokeConfigurations(null, beforeSuiteMethods
260: .values().toArray(
261: new ITestNGMethod[beforeSuiteMethods
262: .size()]), m_suite, m_suite
263: .getParameters(), null /* instance */
264: );
265: }
266:
267: Utils.log("SuiteRunner", 3, "Created "
268: + m_testRunners.size() + " TestRunners");
269:
270: //
271: // Run all the test runners
272: //
273: boolean testsInParallel = XmlSuite.PARALLEL_TESTS
274: .equals(m_suite.getParallel());
275: if (!testsInParallel) {
276: runSequentially();
277: } else {
278: runConcurrently();
279: }
280:
281: //
282: // Invoke afterSuite methods
283: //
284: if (afterSuiteMethods.values().size() > 0) {
285: invoker.invokeConfigurations(null, afterSuiteMethods
286: .values().toArray(
287: new ITestNGMethod[afterSuiteMethods
288: .size()]), m_suite, m_suite
289: .getAllParameters(), null /* instance */);
290: }
291: }
292: }
293:
294: private void runSequentially() {
295: for (TestRunner tr : m_testRunners) {
296: runTest(tr);
297: }
298: }
299:
300: private void runTest(TestRunner tr) {
301: tr.run();
302:
303: ISuiteResult sr = new SuiteResult(m_suite, tr);
304: m_suiteResults.put(tr.getName(), sr);
305: }
306:
307: private void runConcurrently() {
308: List<Runnable> tasks = new ArrayList<Runnable>(m_testRunners
309: .size());
310: for (TestRunner tr : m_testRunners) {
311: tasks.add(new SuiteWorker(tr));
312: }
313:
314: ThreadUtil.execute(tasks, m_suite.getThreadCount(), m_suite
315: .getTimeOut(m_suite.getTests().size() * 1000L), false);
316: }
317:
318: private class SuiteWorker implements Runnable {
319: private TestRunner m_testRunner;
320:
321: public SuiteWorker(TestRunner tr) {
322: m_testRunner = tr;
323: }
324:
325: public void run() {
326: Utils.log("[SuiteWorker]", 4, "Running XML Test '"
327: + m_testRunner.getTest().getName()
328: + "' in Parallel");
329: runTest(m_testRunner);
330: }
331: }
332:
333: /**
334: * Registers ISuiteListeners interested in reporting the result of the current
335: * suite.
336: *
337: * @param reporter
338: */
339: public void addListener(ISuiteListener reporter) {
340: m_listeners.add(reporter);
341: }
342:
343: public String getOutputDirectory() {
344: return m_outputDir + File.separatorChar + getName();
345: }
346:
347: public Map<String, ISuiteResult> getResults() {
348: return m_suiteResults;
349: }
350:
351: /**
352: * FIXME: should be removed?
353: *
354: * @see org.testng.ISuite#getParameter(java.lang.String)
355: */
356: public String getParameter(String parameterName) {
357: return m_suite.getParameter(parameterName);
358: }
359:
360: /**
361: * @see org.testng.ISuite#getMethodsByGroups()
362: */
363: public Map<String, Collection<ITestNGMethod>> getMethodsByGroups() {
364: Map<String, Collection<ITestNGMethod>> result = new HashMap<String, Collection<ITestNGMethod>>();
365:
366: for (TestRunner tr : m_testRunners) {
367: ITestNGMethod[] methods = tr.getAllTestMethods();
368: for (ITestNGMethod m : methods) {
369: String[] groups = m.getGroups();
370: for (String groupName : groups) {
371: Collection<ITestNGMethod> testMethods = result
372: .get(groupName);
373: if (null == testMethods) {
374: testMethods = new ArrayList<ITestNGMethod>();
375: result.put(groupName, testMethods);
376: }
377: testMethods.add(m);
378: }
379: }
380: }
381:
382: return result;
383: }
384:
385: /**
386: * @see org.testng.ISuite#getInvokedMethods()
387: */
388: public Collection<ITestNGMethod> getInvokedMethods() {
389: return getIncludedOrExcludedMethods(true /* included */);
390: }
391:
392: /**
393: * @see org.testng.ISuite#getExcludedMethods()
394: */
395: public Collection<ITestNGMethod> getExcludedMethods() {
396: return getIncludedOrExcludedMethods(false/* included */);
397: }
398:
399: private Collection<ITestNGMethod> getIncludedOrExcludedMethods(
400: boolean included) {
401: List<ITestNGMethod> result = new ArrayList<ITestNGMethod>();
402:
403: for (TestRunner tr : m_testRunners) {
404: Collection<ITestNGMethod> methods = included ? tr
405: .getInvokedMethods() : tr.getExcludedMethods();
406: for (ITestNGMethod m : methods) {
407: result.add(m);
408: }
409: }
410:
411: return result;
412: }
413:
414: public IObjectFactory getObjectFactory() {
415: return m_objectFactory;
416: }
417:
418: /**
419: * Returns the annotation finder for the given annotation type.
420: * @param pAnnotationType the annotation type
421: * @return the annotation finder for the given annotation type.
422: */
423: public IAnnotationFinder getAnnotationFinder(String pAnnotationType) {
424: AnnotationTypeEnum annotationType = AnnotationTypeEnum
425: .valueOf(pAnnotationType);
426:
427: return annotationType == AnnotationTypeEnum.JDK ? m_jdkAnnotationFinder
428: : m_javadocAnnotationFinder;
429: }
430:
431: public static void ppp(String s) {
432: System.out.println("[SuiteRunner] " + s);
433: }
434:
435: /**
436: * The default implementation of {@link ITestRunnerFactory}.
437: */
438: public static class DefaultTestRunnerFactory implements
439: ITestRunnerFactory {
440: private ITestListener[] m_failureGenerators;
441: private boolean m_useDefaultListeners;
442: private boolean m_skipFailedInvocationCounts;
443:
444: public DefaultTestRunnerFactory(
445: ITestListener[] failureListeners,
446: boolean useDefaultListeners,
447: boolean skipFailedInvocationCounts) {
448: m_failureGenerators = failureListeners;
449: m_useDefaultListeners = useDefaultListeners;
450: m_skipFailedInvocationCounts = skipFailedInvocationCounts;
451: }
452:
453: /**
454: * @see ITestRunnerFactory#newTestRunner(org.testng.ISuite, org.testng.xml.XmlTest)
455: */
456: public TestRunner newTestRunner(ISuite suite, XmlTest test) {
457: boolean skip = m_skipFailedInvocationCounts;
458: if (!skip) {
459: skip = test.skipFailedInvocationCounts();
460: }
461: TestRunner testRunner = new TestRunner(suite, test, suite
462: .getOutputDirectory(), suite
463: .getAnnotationFinder(test.getAnnotations()), skip);
464:
465: if (m_useDefaultListeners) {
466: testRunner.addListener(new TestHTMLReporter());
467: testRunner.addListener(new JUnitXMLReporter());
468:
469: //TODO: Moved these here because maven2 has output reporters running
470: //already, the output from these causes directories to be created with
471: //files. This is not the desired behaviour of running tests in maven2.
472: //Don't know what to do about this though, are people relying on these
473: //to be added even with defaultListeners set to false?
474: testRunner.addListener(new TextReporter(testRunner
475: .getName(), TestRunner.getVerbose()));
476: }
477:
478: for (ITestListener itl : m_failureGenerators) {
479: testRunner.addListener(itl);
480: }
481:
482: return testRunner;
483: }
484: }
485:
486: public static class ProxyTestRunnerFactory implements
487: ITestRunnerFactory {
488: private ITestListener[] m_failureGenerators;
489: private ITestRunnerFactory m_target;
490:
491: public ProxyTestRunnerFactory(ITestListener[] failureListeners,
492: ITestRunnerFactory target) {
493: m_failureGenerators = failureListeners;
494: m_target = target;
495: }
496:
497: /**
498: * @see ITestRunnerFactory#newTestRunner(org.testng.ISuite, org.testng.xml.XmlTest)
499: */
500: public TestRunner newTestRunner(ISuite suite, XmlTest test) {
501: TestRunner testRunner = m_target.newTestRunner(suite, test);
502:
503: testRunner.addListener(new TextReporter(testRunner
504: .getName(), TestRunner.getVerbose()));
505:
506: for (ITestListener itl : m_failureGenerators) {
507: testRunner.addListener(itl);
508: }
509:
510: return testRunner;
511: }
512: }
513:
514: public void setHost(String host) {
515: m_host = host;
516: }
517:
518: public String getHost() {
519: return m_host;
520: }
521:
522: private SuiteRunState m_suiteState = new SuiteRunState();
523:
524: /**
525: * @see org.testng.ISuite#getSuiteState()
526: */
527: public SuiteRunState getSuiteState() {
528: return m_suiteState;
529: }
530:
531: public void setSkipFailedInvocationCounts(
532: Boolean skipFailedInvocationCounts) {
533: if (skipFailedInvocationCounts != null) {
534: m_skipFailedInvocationCounts = skipFailedInvocationCounts;
535: }
536: }
537: }
|