1package org.testng;
2
3import java.io.File;
4import java.io.FileNotFoundException;
5import java.io.IOException;
6import java.io.InputStream;
7import java.net.URLClassLoader;
8import java.util.ArrayList;
9import java.util.Arrays;
10import java.util.Collection;
11import java.util.Enumeration;
12import java.util.List;
13import java.util.Map;
14import java.util.ServiceLoader;
15import java.util.Set;
16import java.util.concurrent.LinkedBlockingQueue;
17import java.util.concurrent.TimeUnit;
18import java.util.jar.JarEntry;
19import java.util.jar.JarFile;
20
21import javax.xml.parsers.ParserConfigurationException;
22
23import org.testng.annotations.ITestAnnotation;
24import org.testng.collections.Lists;
25import org.testng.collections.Maps;
26import org.testng.collections.Sets;
27import org.testng.internal.ClassHelper;
28import org.testng.internal.Configuration;
29import org.testng.internal.DynamicGraph;
30import org.testng.internal.IConfiguration;
31import org.testng.internal.IPathUtils;
32import org.testng.internal.IResultListener2;
33import org.testng.internal.OverrideProcessor;
34import org.testng.internal.PathUtilsFactory;
35import org.testng.internal.SuiteRunnerMap;
36import org.testng.internal.Utils;
37import org.testng.internal.Version;
38import org.testng.internal.annotations.DefaultAnnotationTransformer;
39import org.testng.internal.annotations.IAnnotationFinder;
40import org.testng.internal.annotations.JDK15AnnotationFinder;
41import org.testng.internal.thread.graph.GraphThreadPoolExecutor;
42import org.testng.internal.thread.graph.IThreadWorkerFactory;
43import org.testng.internal.thread.graph.SuiteWorkerFactory;
44import org.testng.junit.JUnitTestFinder;
45import org.testng.log4testng.Logger;
46import org.testng.remote.SuiteDispatcher;
47import org.testng.remote.SuiteSlave;
48import org.testng.reporters.EmailableReporter;
49import org.testng.reporters.EmailableReporter2;
50import org.testng.reporters.FailedReporter;
51import org.testng.reporters.JUnitReportReporter;
52import org.testng.reporters.SuiteHTMLReporter;
53import org.testng.reporters.VerboseReporter;
54import org.testng.reporters.XMLReporter;
55import org.testng.reporters.jq.Main;
56import org.testng.xml.Parser;
57import org.testng.xml.XmlClass;
58import org.testng.xml.XmlInclude;
59import org.testng.xml.XmlMethodSelector;
60import org.testng.xml.XmlSuite;
61import org.testng.xml.XmlTest;
62import org.xml.sax.SAXException;
63
64import com.beust.jcommander.JCommander;
65import com.beust.jcommander.ParameterException;
66
67import static org.testng.internal.Utils.defaultIfStringEmpty;
68import static org.testng.internal.Utils.isStringEmpty;
69import static org.testng.internal.Utils.isStringNotEmpty;
70
71/**
72 * This class is the main entry point for running tests in the TestNG framework.
73 * Users can create their own TestNG object and invoke it in many different
74 * ways:
75 * <ul>
76 * <li>On an existing testng.xml
77 * <li>On a synthetic testng.xml, created entirely from Java
78 * <li>By directly setting the test classes
79 * </ul>
80 * You can also define which groups to include or exclude, assign parameters, etc...
81 * <P/>
82 * The command line parameters are:
83 * <UL>
84 *  <LI>-d <TT>outputdir</TT>: specify the output directory</LI>
85 *  <LI>-testclass <TT>class_name</TT>: specifies one or several class names </li>
86 *  <LI>-testjar <TT>jar_name</TT>: specifies the jar containing the tests</LI>
87 *  <LI>-sourcedir <TT>src1;src2</TT>: ; separated list of source directories
88 *    (used only when javadoc annotations are used)</LI>
89 *  <LI>-target</LI>
90 *  <LI>-groups</LI>
91 *  <LI>-testrunfactory</LI>
92 *  <LI>-listener</LI>
93 * </UL>
94 * <P/>
95 * Please consult documentation for more details.
96 *
97 * FIXME: should support more than simple paths for suite xmls
98 *
99 * @see #usage()
100 *
101 * @author <a href = "mailto:cedric&#64;beust.com">Cedric Beust</a>
102 */
103public class TestNG {
104
105  /** This class' log4testng Logger. */
106  private static final Logger LOGGER = Logger.getLogger(TestNG.class);
107
108  /** The default name for a suite launched from the command line */
109  public static final String DEFAULT_COMMAND_LINE_SUITE_NAME = "Command line suite";
110
111  /** The default name for a test launched from the command line */
112  public static final String DEFAULT_COMMAND_LINE_TEST_NAME = "Command line test";
113
114  /** The default name of the result's output directory (keep public, used by Eclipse). */
115  public static final String DEFAULT_OUTPUTDIR = "test-output";
116
117  /** System properties */
118  public static final String SHOW_TESTNG_STACK_FRAMES = "testng.show.stack.frames";
119  public static final String TEST_CLASSPATH = "testng.test.classpath";
120
121  private static TestNG m_instance;
122
123  private static JCommander m_jCommander;
124
125  private List<String> m_commandLineMethods;
126  protected List<XmlSuite> m_suites = Lists.newArrayList();
127  private List<XmlSuite> m_cmdlineSuites;
128  private String m_outputDir = DEFAULT_OUTPUTDIR;
129
130  private String[] m_includedGroups;
131  private String[] m_excludedGroups;
132
133  private Boolean m_isJUnit = XmlSuite.DEFAULT_JUNIT;
134  private Boolean m_isMixed = XmlSuite.DEFAULT_MIXED;
135  protected boolean m_useDefaultListeners = true;
136
137  private ITestRunnerFactory m_testRunnerFactory;
138
139  // These listeners can be overridden from the command line
140  private List<IClassListener> m_classListeners = Lists.newArrayList();
141  private List<ITestListener> m_testListeners = Lists.newArrayList();
142  private List<ISuiteListener> m_suiteListeners = Lists.newArrayList();
143  private Set<IReporter> m_reporters = Sets.newHashSet();
144
145  protected static final int HAS_FAILURE = 1;
146  protected static final int HAS_SKIPPED = 2;
147  protected static final int HAS_FSP = 4;
148  protected static final int HAS_NO_TEST = 8;
149
150  public static final Integer DEFAULT_VERBOSE = 1;
151
152  private int m_status;
153  private boolean m_hasTests= false;
154
155  private String m_slavefileName = null;
156  private String m_masterfileName = null;
157
158  // Command line suite parameters
159  private int m_threadCount;
160  private boolean m_useThreadCount;
161  private XmlSuite.ParallelMode m_parallelMode = XmlSuite.ParallelMode.FALSE;
162  private String m_configFailurePolicy;
163  private Class[] m_commandLineTestClasses;
164
165  private String m_defaultSuiteName=DEFAULT_COMMAND_LINE_SUITE_NAME;
166  private String m_defaultTestName=DEFAULT_COMMAND_LINE_TEST_NAME;
167
168  private Map<String, Integer> m_methodDescriptors = Maps.newHashMap();
169
170  private ITestObjectFactory m_objectFactory;
171
172  private List<IInvokedMethodListener> m_invokedMethodListeners = Lists.newArrayList();
173
174  private Integer m_dataProviderThreadCount = null;
175
176  private String m_jarPath;
177  /** The path of the testng.xml file inside the jar file */
178  private String m_xmlPathInJar = CommandLineArgs.XML_PATH_IN_JAR_DEFAULT;
179
180  private List<String> m_stringSuites = Lists.newArrayList();
181
182  private IHookable m_hookable;
183  private IConfigurable m_configurable;
184
185  protected long m_end;
186  protected long m_start;
187
188  private List<IExecutionListener> m_executionListeners = Lists.newArrayList();
189
190  private List<IAlterSuiteListener> m_alterSuiteListeners= Lists.newArrayList();
191
192  private boolean m_isInitialized = false;
193
194  private IPathUtils m_pathUtils = PathUtilsFactory.newInstance();
195
196  /**
197   * Default constructor. Setting also usage of default listeners/reporters.
198   */
199  public TestNG() {
200    init(true);
201  }
202
203  /**
204   * Used by maven2 to have 0 output of any kind come out
205   * of testng.
206   * @param useDefaultListeners Whether or not any default reports
207   * should be added to tests.
208   */
209  public TestNG(boolean useDefaultListeners) {
210    init(useDefaultListeners);
211  }
212
213  private void init(boolean useDefaultListeners) {
214    m_instance = this;
215
216    m_useDefaultListeners = useDefaultListeners;
217    m_configuration = new Configuration();
218  }
219
220  public int getStatus() {
221    return m_status;
222  }
223
224  private void setStatus(int status) {
225    m_status |= status;
226  }
227
228  /**
229   * Sets the output directory where the reports will be created.
230   * @param outputdir The directory.
231   */
232  public void setOutputDirectory(final String outputdir) {
233    if (isStringNotEmpty(outputdir)) {
234      m_outputDir = outputdir;
235    }
236  }
237
238  /**
239   * If this method is passed true before run(), the default listeners
240   * will not be used.
241   * <ul>
242   * <li>org.testng.reporters.TestHTMLReporter
243   * <li>org.testng.reporters.JUnitXMLReporter
244   * <li>org.testng.reporters.XMLReporter
245   * </ul>
246   *
247   * @see org.testng.reporters.TestHTMLReporter
248   * @see org.testng.reporters.JUnitXMLReporter
249   * @see org.testng.reporters.XMLReporter
250   */
251  public void setUseDefaultListeners(boolean useDefaultListeners) {
252    m_useDefaultListeners = useDefaultListeners;
253  }
254
255  /**
256   * Sets a jar containing a testng.xml file.
257   *
258   * @param jarPath
259   */
260  public void setTestJar(String jarPath) {
261    m_jarPath = jarPath;
262  }
263
264  /**
265   * Sets the path to the XML file in the test jar file.
266   */
267  public void setXmlPathInJar(String xmlPathInJar) {
268    m_xmlPathInJar = xmlPathInJar;
269  }
270
271  public void initializeSuitesAndJarFile() {
272    // The Eclipse plug-in (RemoteTestNG) might have invoked this method already
273    // so don't initialize suites twice.
274    if (m_isInitialized) {
275      return;
276    }
277
278    m_isInitialized = true;
279    if (m_suites.size() > 0) {
280    	//to parse the suite files (<suite-file>), if any
281    	for (XmlSuite s: m_suites) {
282        for (String suiteFile : s.getSuiteFiles()) {
283            try {
284                Collection<XmlSuite> childSuites = getParser(m_pathUtils.getSuiteNormalizedPath(s, suiteFile)).parse();
285                for (XmlSuite cSuite : childSuites){
286                    cSuite.setParentSuite(s);
287                    s.getChildSuites().add(cSuite);
288                }
289            } catch (ParserConfigurationException | IOException | SAXException e) {
290                e.printStackTrace(System.out);
291            }
292        }
293
294    	}
295      return;
296    }
297
298    //
299    // Parse the suites that were passed on the command line
300    //
301    for (String suitePath : m_stringSuites) {
302      if(LOGGER.isDebugEnabled()) {
303        LOGGER.debug("suiteXmlPath: \"" + suitePath + "\"");
304      }
305      try {
306        Collection<XmlSuite> allSuites = getParser(suitePath).parse();
307
308        for (XmlSuite s : allSuites) {
309          // If test names were specified, only run these test names
310          if (m_testNames != null) {
311            m_suites.add(extractTestNames(s, m_testNames));
312          }
313          else {
314            m_suites.add(s);
315          }
316        }
317      }
318      catch(SAXException | ParserConfigurationException | IOException e) {
319        e.printStackTrace(System.out);
320      } catch(Exception ex) {
321        // Probably a Yaml exception, unnest it
322        Throwable t = ex;
323        while (t.getCause() != null) t = t.getCause();
324//        t.printStackTrace();
325        if (t instanceof TestNGException) throw (TestNGException) t;
326        else throw new TestNGException(t);
327      }
328    }
329
330    //
331    // jar path
332    //
333    // If suites were passed on the command line, they take precedence over the suite file
334    // inside that jar path
335    if (m_jarPath != null && m_stringSuites.size() > 0) {
336      StringBuilder suites = new StringBuilder();
337      for (String s : m_stringSuites) {
338        suites.append(s);
339      }
340      Utils.log("TestNG", 2, "Ignoring the XML file inside " + m_jarPath + " and using "
341          + suites + " instead");
342      return;
343    }
344    if (isStringEmpty(m_jarPath)) {
345      return;
346    }
347
348    // We have a jar file and no XML file was specified: try to find an XML file inside the jar
349    File jarFile = new File(m_jarPath);
350
351    try {
352
353      Utils.log("TestNG", 2, "Trying to open jar file:" + jarFile);
354
355      boolean foundTestngXml = false;
356      List<String> classes = Lists.newArrayList();
357      try (JarFile jf = new JarFile(jarFile)) {
358//      System.out.println("   result: " + jf);
359        Enumeration<JarEntry> entries = jf.entries();
360        while (entries.hasMoreElements()) {
361          JarEntry je = entries.nextElement();
362          if (je.getName().equals(m_xmlPathInJar)) {
363            Parser parser = getParser(jf.getInputStream(je));
364            Collection<XmlSuite> suites = parser.parse();
365            for (XmlSuite suite : suites) {
366              // If test names were specified, only run these test names
367              if (m_testNames != null) {
368                m_suites.add(extractTestNames(suite, m_testNames));
369              } else {
370                m_suites.add(suite);
371              }
372            }
373
374            foundTestngXml = true;
375            break;
376          } else if (je.getName().endsWith(".class")) {
377            int n = je.getName().length() - ".class".length();
378            classes.add(je.getName().replace("/", ".").substring(0, n));
379          }
380        }
381      }
382      if (! foundTestngXml) {
383        Utils.log("TestNG", 1,
384            "Couldn't find the " + m_xmlPathInJar + " in the jar file, running all the classes");
385        XmlSuite xmlSuite = new XmlSuite();
386        xmlSuite.setVerbose(0);
387        xmlSuite.setName("Jar suite");
388        XmlTest xmlTest = new XmlTest(xmlSuite);
389        List<XmlClass> xmlClasses = Lists.newArrayList();
390        for (String cls : classes) {
391          XmlClass xmlClass = new XmlClass(cls);
392          xmlClasses.add(xmlClass);
393        }
394        xmlTest.setXmlClasses(xmlClasses);
395        m_suites.add(xmlSuite);
396      }
397    }
398    catch(ParserConfigurationException | IOException | SAXException ex) {
399      ex.printStackTrace();
400    }
401  }
402
403  private Parser getParser(String path) {
404    Parser result = new Parser(path);
405    initProcessor(result);
406    return result;
407  }
408
409  private Parser getParser(InputStream is) {
410    Parser result = new Parser(is);
411    initProcessor(result);
412    return result;
413  }
414
415  private void initProcessor(Parser result) {
416    result.setPostProcessor(new OverrideProcessor(m_includedGroups, m_excludedGroups));
417  }
418
419  /**
420   * If the XmlSuite contains at least one test named as testNames, return
421   * an XmlSuite that's made only of these tests, otherwise, return the
422   * original suite.
423   */
424  private static XmlSuite extractTestNames(XmlSuite s, List<String> testNames) {
425    extractTestNamesFromChildSuites(s, testNames);
426
427    List<XmlTest> tests = Lists.newArrayList();
428    for (XmlTest xt : s.getTests()) {
429      for (String tn : testNames) {
430        if (xt.getName().equals(tn)) {
431          tests.add(xt);
432        }
433      }
434    }
435
436    if (tests.size() == 0) {
437      return s;
438    }
439    else {
440      XmlSuite result = (XmlSuite) s.clone();
441      result.getTests().clear();
442      result.getTests().addAll(tests);
443      return result;
444    }
445  }
446
447  private static void extractTestNamesFromChildSuites(XmlSuite s, List<String> testNames) {
448    List<XmlSuite> childSuites = s.getChildSuites();
449    for (int i = 0; i < childSuites.size(); i++) {
450      XmlSuite child = childSuites.get(i);
451      XmlSuite extracted = extractTestNames(child, testNames);
452      // if a new xml suite is created, which means some tests was extracted, then we replace the child
453      if (extracted != child) {
454        childSuites.set(i, extracted);
455      }
456    }
457  }
458
459  /**
460   * Define the number of threads in the thread pool.
461   */
462  public void setThreadCount(int threadCount) {
463    if(threadCount < 1) {
464      exitWithError("Cannot use a threadCount parameter less than 1; 1 > " + threadCount);
465    }
466
467    m_threadCount = threadCount;
468    m_useThreadCount = true;
469  }
470
471  /**
472   * Define whether this run will be run in parallel mode.
473   * @deprecated Use #setParallel(XmlSuite.ParallelMode) instead
474   */
475  @Deprecated
476  public void setParallel(String parallel) {
477    if (parallel == null) {
478      setParallel(XmlSuite.ParallelMode.FALSE);
479    } else {
480      setParallel(XmlSuite.ParallelMode.getValidParallel(parallel));
481    }
482  }
483
484  public void setParallel(XmlSuite.ParallelMode parallel) {
485    m_parallelMode = parallel;
486  }
487
488  public void setCommandLineSuite(XmlSuite suite) {
489    m_cmdlineSuites = Lists.newArrayList();
490    m_cmdlineSuites.add(suite);
491    m_suites.add(suite);
492  }
493
494  /**
495   * Set the test classes to be run by this TestNG object.  This method
496   * will create a dummy suite that will wrap these classes called
497   * "Command Line Test".
498   * <p/>
499   * If used together with threadCount, parallel, groups, excludedGroups than this one must be set first.
500   *
501   * @param classes An array of classes that contain TestNG annotations.
502   */
503  public void setTestClasses(Class[] classes) {
504    m_suites.clear();
505    m_commandLineTestClasses = classes;
506  }
507
508  /**
509   * Given a string com.example.Foo.f1, return an array where [0] is the class and [1]
510   * is the method.
511   */
512  private String[] splitMethod(String m) {
513    int index = m.lastIndexOf(".");
514    if (index < 0) {
515      throw new TestNGException("Bad format for command line method:" + m
516          + ", expected <class>.<method>");
517    }
518
519    return new String[] { m.substring(0, index), m.substring(index + 1).replaceAll("\\*", "\\.\\*") };
520  }
521
522  /**
523   * @return a list of XmlSuite objects that represent the list of classes and methods passed
524   * in parameter.
525   *
526   * @param commandLineMethods a string with the form "com.example.Foo.f1,com.example.Bar.f2"
527   */
528  private List<XmlSuite> createCommandLineSuitesForMethods(List<String> commandLineMethods) {
529    //
530    // Create the <classes> tag
531    //
532    Set<Class> classes = Sets.newHashSet();
533    for (String m : commandLineMethods) {
534      Class c = ClassHelper.forName(splitMethod(m)[0]);
535      if (c != null) {
536          classes.add(c);
537      }
538    }
539
540    List<XmlSuite> result = createCommandLineSuitesForClasses(classes.toArray(new Class[0]));
541
542    //
543    // Add the method tags
544    //
545    List<XmlClass> xmlClasses = Lists.newArrayList();
546    for (XmlSuite s : result) {
547        for (XmlTest t : s.getTests()) {
548            xmlClasses.addAll(t.getClasses());
549        }
550    }
551
552    for (XmlClass xc : xmlClasses) {
553      for (String m : commandLineMethods) {
554        String[] split = splitMethod(m);
555        String className = split[0];
556        if (xc.getName().equals(className)) {
557          XmlInclude includedMethod = new XmlInclude(split[1]);
558          xc.getIncludedMethods().add(includedMethod);
559        }
560      }
561    }
562
563    return result;
564  }
565
566  private List<XmlSuite> createCommandLineSuitesForClasses(Class[] classes) {
567    //
568    // See if any of the classes has an xmlSuite or xmlTest attribute.
569    // If it does, create the appropriate XmlSuite, otherwise, create
570    // the default one
571    //
572    XmlClass[] xmlClasses = Utils.classesToXmlClasses(classes);
573    Map<String, XmlSuite> suites = Maps.newHashMap();
574    IAnnotationFinder finder = m_configuration.getAnnotationFinder();
575
576    for (int i = 0; i < classes.length; i++) {
577      Class c = classes[i];
578      ITestAnnotation test = finder.findAnnotation(c, ITestAnnotation.class);
579      String suiteName = getDefaultSuiteName();
580      String testName = getDefaultTestName();
581      boolean isJUnit = false;
582      if (test != null) {
583        suiteName = defaultIfStringEmpty(test.getSuiteName(), suiteName);
584        testName = defaultIfStringEmpty(test.getTestName(), testName);
585      } else {
586        if (m_isMixed && JUnitTestFinder.isJUnitTest(c)) {
587          isJUnit = true;
588          testName = c.getName();
589        }
590      }
591      XmlSuite xmlSuite = suites.get(suiteName);
592      if (xmlSuite == null) {
593        xmlSuite = new XmlSuite();
594        xmlSuite.setName(suiteName);
595        suites.put(suiteName, xmlSuite);
596      }
597
598      if (m_dataProviderThreadCount != null) {
599        xmlSuite.setDataProviderThreadCount(m_dataProviderThreadCount);
600      }
601      XmlTest xmlTest = null;
602      for (XmlTest xt  : xmlSuite.getTests()) {
603        if (xt.getName().equals(testName)) {
604          xmlTest = xt;
605          break;
606        }
607      }
608
609      if (xmlTest == null) {
610        xmlTest = new XmlTest(xmlSuite);
611        xmlTest.setName(testName);
612        xmlTest.setJUnit(isJUnit);
613      }
614
615      xmlTest.getXmlClasses().add(xmlClasses[i]);
616    }
617
618    return new ArrayList<>(suites.values());
619  }
620
621  public void addMethodSelector(String className, int priority) {
622    m_methodDescriptors.put(className, priority);
623  }
624
625  /**
626   * Set the suites file names to be run by this TestNG object. This method tries to load and
627   * parse the specified TestNG suite xml files. If a file is missing, it is ignored.
628   *
629   * @param suites A list of paths to one more XML files defining the tests.  For example:
630   *
631   * <pre>
632   * TestNG tng = new TestNG();
633   * List<String> suites = Lists.newArrayList();
634   * suites.add("c:/tests/testng1.xml");
635   * suites.add("c:/tests/testng2.xml");
636   * tng.setTestSuites(suites);
637   * tng.run();
638   * </pre>
639   */
640  public void setTestSuites(List<String> suites) {
641    m_stringSuites = suites;
642  }
643
644  /**
645   * Specifies the XmlSuite objects to run.
646   * @param suites
647   * @see org.testng.xml.XmlSuite
648   */
649  public void setXmlSuites(List<XmlSuite> suites) {
650    m_suites = suites;
651  }
652
653  /**
654   * Define which groups will be excluded from this run.
655   *
656   * @param groups A list of group names separated by a comma.
657   */
658  public void setExcludedGroups(String groups) {
659    m_excludedGroups = Utils.split(groups, ",");
660  }
661
662
663  /**
664   * Define which groups will be included from this run.
665   *
666   * @param groups A list of group names separated by a comma.
667   */
668  public void setGroups(String groups) {
669    m_includedGroups = Utils.split(groups, ",");
670  }
671
672
673  private void setTestRunnerFactoryClass(Class testRunnerFactoryClass) {
674    setTestRunnerFactory((ITestRunnerFactory) ClassHelper.newInstance(testRunnerFactoryClass));
675  }
676
677
678  protected void setTestRunnerFactory(ITestRunnerFactory itrf) {
679    m_testRunnerFactory= itrf;
680  }
681
682  public void setObjectFactory(Class c) {
683    m_objectFactory = (ITestObjectFactory) ClassHelper.newInstance(c);
684  }
685
686  public void setObjectFactory(ITestObjectFactory factory) {
687    m_objectFactory = factory;
688  }
689
690  /**
691   * Define which listeners to user for this run.
692   *
693   * @param classes A list of classes, which must be either ISuiteListener,
694   * ITestListener or IReporter
695   */
696  public void setListenerClasses(List<Class> classes) {
697    for (Class cls: classes) {
698      addListener(ClassHelper.newInstance(cls));
699    }
700  }
701
702  public void addListener(Object listener) {
703    if (! (listener instanceof ITestNGListener))
704    {
705      exitWithError("Listener " + listener
706          + " must be one of ITestListener, ISuiteListener, IReporter, "
707          + " IAnnotationTransformer, IMethodInterceptor or IInvokedMethodListener");
708    }
709    else {
710      if (listener instanceof ISuiteListener) {
711        addListener((ISuiteListener) listener);
712      }
713      if (listener instanceof ITestListener) {
714        addListener((ITestListener) listener);
715      }
716      if (listener instanceof IClassListener) {
717        addListener((IClassListener) listener);
718      }
719      if (listener instanceof IReporter) {
720        addListener((IReporter) listener);
721      }
722      if (listener instanceof IAnnotationTransformer) {
723        setAnnotationTransformer((IAnnotationTransformer) listener);
724      }
725      if (listener instanceof IMethodInterceptor) {
726        m_methodInterceptors.add((IMethodInterceptor) listener);
727      }
728      if (listener instanceof IInvokedMethodListener) {
729        addInvokedMethodListener((IInvokedMethodListener) listener);
730      }
731      if (listener instanceof IHookable) {
732        setHookable((IHookable) listener);
733      }
734      if (listener instanceof IConfigurable) {
735        setConfigurable((IConfigurable) listener);
736      }
737      if (listener instanceof IExecutionListener) {
738        addExecutionListener((IExecutionListener) listener);
739      }
740      if (listener instanceof IConfigurationListener) {
741        getConfiguration().addConfigurationListener((IConfigurationListener) listener);
742      }
743      if (listener instanceof IAlterSuiteListener) {
744        addAlterSuiteListener((IAlterSuiteListener) listener);
745      }
746    }
747  }
748
749  public void addListener(IInvokedMethodListener listener) {
750    m_invokedMethodListeners.add(listener);
751  }
752
753  public void addListener(ISuiteListener listener) {
754    if (null != listener) {
755      m_suiteListeners.add(listener);
756    }
757  }
758
759  public void addListener(ITestListener listener) {
760    if (null != listener) {
761      m_testListeners.add(listener);
762    }
763  }
764
765  public void addListener(IClassListener listener) {
766    if (null != listener) {
767      m_classListeners.add(listener);
768    }
769  }
770
771  public void addListener(IReporter listener) {
772    if (null != listener) {
773      m_reporters.add(listener);
774    }
775  }
776
777  public void addInvokedMethodListener(IInvokedMethodListener listener) {
778    m_invokedMethodListeners.add(listener);
779  }
780
781  public Set<IReporter> getReporters() {
782    return m_reporters;
783  }
784
785  public List<ITestListener> getTestListeners() {
786    return m_testListeners;
787  }
788
789  public List<ISuiteListener> getSuiteListeners() {
790    return m_suiteListeners;
791  }
792
793  /** If m_verbose gets set, it will override the verbose setting in testng.xml */
794  private Integer m_verbose = null;
795
796  private final IAnnotationTransformer m_defaultAnnoProcessor = new DefaultAnnotationTransformer();
797  private IAnnotationTransformer m_annotationTransformer = m_defaultAnnoProcessor;
798
799  private Boolean m_skipFailedInvocationCounts = false;
800
801  private List<IMethodInterceptor> m_methodInterceptors = new ArrayList<IMethodInterceptor>();
802
803  /** The list of test names to run from the given suite */
804  private List<String> m_testNames;
805
806  private Integer m_suiteThreadPoolSize = CommandLineArgs.SUITE_THREAD_POOL_SIZE_DEFAULT;
807
808  private boolean m_randomizeSuites = Boolean.FALSE;
809
810  private boolean m_preserveOrder = false;
811  private Boolean m_groupByInstances;
812
813  private IConfiguration m_configuration;
814
815  /**
816   * Sets the level of verbosity. This value will override the value specified
817   * in the test suites.
818   *
819   * @param verbose the verbosity level (0 to 10 where 10 is most detailed)
820   * Actually, this is a lie:  you can specify -1 and this will put TestNG
821   * in debug mode (no longer slicing off stack traces and all).
822   */
823  public void setVerbose(int verbose) {
824    m_verbose = verbose;
825  }
826
827  private void initializeCommandLineSuites() {
828    if (m_commandLineTestClasses != null || m_commandLineMethods != null) {
829      if (null != m_commandLineMethods) {
830        m_cmdlineSuites = createCommandLineSuitesForMethods(m_commandLineMethods);
831      }
832      else {
833        m_cmdlineSuites = createCommandLineSuitesForClasses(m_commandLineTestClasses);
834      }
835
836      for (XmlSuite s : m_cmdlineSuites) {
837        for (XmlTest t : s.getTests()) {
838          t.setPreserveOrder(String.valueOf(m_preserveOrder));
839        }
840        m_suites.add(s);
841        if (m_groupByInstances != null) {
842          s.setGroupByInstances(m_groupByInstances);
843        }
844      }
845    }
846  }
847
848  private void initializeCommandLineSuitesParams() {
849    if(null == m_cmdlineSuites) {
850      return;
851    }
852
853    for (XmlSuite s : m_cmdlineSuites) {
854      if(m_useThreadCount) {
855        s.setThreadCount(m_threadCount);
856      }
857      s.setParallel(m_parallelMode);
858      if(m_configFailurePolicy != null) {
859        s.setConfigFailurePolicy(m_configFailurePolicy.toString());
860      }
861    }
862
863  }
864
865  private void initializeCommandLineSuitesGroups() {
866    // If groups were specified on the command line, they should override groups
867    // specified in the XML file
868    boolean hasIncludedGroups = null != m_includedGroups && m_includedGroups.length > 0;
869    boolean hasExcludedGroups = null != m_excludedGroups && m_excludedGroups.length > 0;
870    List<XmlSuite> suites = m_cmdlineSuites != null ? m_cmdlineSuites : m_suites;
871    if (hasIncludedGroups || hasExcludedGroups) {
872      for (XmlSuite s : suites) {
873        //set on each test, instead of just the first one of the suite
874        for (XmlTest t : s.getTests()) {
875          if(hasIncludedGroups) {
876            t.setIncludedGroups(Arrays.asList(m_includedGroups));
877          }
878          if(hasExcludedGroups) {
879            t.setExcludedGroups(Arrays.asList(m_excludedGroups));
880          }
881        }
882      }
883    }
884  }
885  private void addReporter(Class<? extends IReporter> r) {
886    m_reporters.add(ClassHelper.newInstance(r));
887  }
888
889  private void initializeDefaultListeners() {
890    m_testListeners.add(new ExitCodeListener(this));
891
892    if (m_useDefaultListeners) {
893      addReporter(SuiteHTMLReporter.class);
894      addReporter(Main.class);
895      addReporter(FailedReporter.class);
896      addReporter(XMLReporter.class);
897      if (System.getProperty("oldTestngEmailableReporter") != null) {
898        addReporter(EmailableReporter.class);
899      } else if (System.getProperty("noEmailableReporter") == null) {
900        addReporter(EmailableReporter2.class);
901      }
902      addReporter(JUnitReportReporter.class);
903      if (m_verbose != null && m_verbose > 4) {
904        addListener(new VerboseReporter("[TestNG] "));
905      }
906    }
907  }
908
909  private void initializeConfiguration() {
910    ITestObjectFactory factory = m_objectFactory;
911    //
912    // Install the listeners found in ServiceLoader (or use the class
913    // loader for tests, if specified).
914    //
915    addServiceLoaderListeners();
916
917    //
918    // Install the listeners found in the suites
919    //
920    for (XmlSuite s : m_suites) {
921      for (String listenerName : s.getListeners()) {
922        Class<?> listenerClass = ClassHelper.forName(listenerName);
923
924        // If specified listener does not exist, a TestNGException will be thrown
925        if(listenerClass == null) {
926          throw new TestNGException("Listener " + listenerName
927              + " was not found in project's classpath");
928        }
929
930        Object listener = ClassHelper.newInstance(listenerClass);
931        addListener(listener);
932      }
933
934      //
935      // Install the method selectors
936      //
937      for (XmlMethodSelector methodSelector : s.getMethodSelectors() ) {
938        addMethodSelector(methodSelector.getClassName(), methodSelector.getPriority());
939      }
940
941      //
942      // Find if we have an object factory
943      //
944      if (s.getObjectFactory() != null) {
945        if (factory == null) {
946          factory = s.getObjectFactory();
947        } else {
948          throw new TestNGException("Found more than one object-factory tag in your suites");
949        }
950      }
951    }
952
953    m_configuration.setAnnotationFinder(new JDK15AnnotationFinder(getAnnotationTransformer()));
954    m_configuration.setHookable(m_hookable);
955    m_configuration.setConfigurable(m_configurable);
956    m_configuration.setObjectFactory(factory);
957  }
958
959  /**
960   * Using reflection to remain Java 5 compliant.
961   */
962  private void addServiceLoaderListeners() {
963      Iterable<ITestNGListener> loader = m_serviceLoaderClassLoader != null ?
964          ServiceLoader.load(ITestNGListener.class, m_serviceLoaderClassLoader)
965          : ServiceLoader.load(ITestNGListener.class);
966      for (ITestNGListener l : loader) {
967        Utils.log("[TestNG]", 2, "Adding ServiceLoader listener:" + l);
968        addListener(l);
969        addServiceLoaderListener(l);
970      }
971  }
972
973  /**
974   * Before suites are executed, do a sanity check to ensure all required
975   * conditions are met. If not, throw an exception to stop test execution
976   *
977   * @throws TestNGException if the sanity check fails
978   */
979  private void sanityCheck() {
980    checkTestNames(m_suites);
981    checkSuiteNames(m_suites);
982  }
983
984  /**
985   * Ensure that two XmlTest within the same XmlSuite don't have the same name
986   */
987  private void checkTestNames(List<XmlSuite> suites) {
988    for (XmlSuite suite : suites) {
989      Set<String> testNames = Sets.newHashSet();
990      for (XmlTest test : suite.getTests()) {
991        if (testNames.contains(test.getName())) {
992          throw new TestNGException("Two tests in the same suite "
993              + "cannot have the same name: " + test.getName());
994        } else {
995          testNames.add(test.getName());
996        }
997      }
998      checkTestNames(suite.getChildSuites());
999    }
1000  }
1001
1002  /**
1003   * Ensure that two XmlSuite don't have the same name
1004   * Otherwise will be clash in SuiteRunnerMap
1005   * See issue #302
1006   */
1007  private void checkSuiteNames(List<XmlSuite> suites) {
1008    checkSuiteNamesInternal(suites, Sets.<String>newHashSet());
1009  }
1010
1011  private void checkSuiteNamesInternal(List<XmlSuite> suites, Set<String> names) {
1012    for (XmlSuite suite : suites) {
1013      final String name = suite.getName();
1014
1015      int count = 0;
1016      String tmpName = name;
1017      while (names.contains(tmpName)) {
1018        tmpName = name + " (" + count++ + ")";
1019      }
1020
1021      if (count > 0) {
1022        suite.setName(tmpName);
1023        names.add(tmpName);
1024      } else {
1025        names.add(name);
1026      }
1027
1028      names.add(name);
1029      checkSuiteNamesInternal(suite.getChildSuites(), names);
1030    }
1031  }
1032
1033  /**
1034   * Run TestNG.
1035   */
1036  public void run() {
1037    initializeSuitesAndJarFile();
1038    initializeConfiguration();
1039    initializeDefaultListeners();
1040    initializeCommandLineSuites();
1041    initializeCommandLineSuitesParams();
1042    initializeCommandLineSuitesGroups();
1043
1044    sanityCheck();
1045
1046    List<ISuite> suiteRunners = null;
1047
1048    runSuiteAlterationListeners();
1049    runExecutionListeners(true /* start */);
1050
1051    m_start = System.currentTimeMillis();
1052
1053    //
1054    // Slave mode
1055    //
1056    if (m_slavefileName != null) {
1057       SuiteSlave slave = new SuiteSlave( m_slavefileName, this );
1058       slave.waitForSuites();
1059    }
1060
1061    //
1062    // Regular mode
1063    //
1064    else if (m_masterfileName == null) {
1065      suiteRunners = runSuitesLocally();
1066    }
1067
1068    //
1069    // Master mode
1070    //
1071    else {
1072       SuiteDispatcher dispatcher = new SuiteDispatcher(m_masterfileName);
1073       suiteRunners = dispatcher.dispatch(getConfiguration(),
1074           m_suites, getOutputDirectory(),
1075           getTestListeners());
1076    }
1077
1078    m_end = System.currentTimeMillis();
1079    runExecutionListeners(false /* finish */);
1080
1081    if(null != suiteRunners) {
1082      generateReports(suiteRunners);
1083    }
1084
1085    if(!m_hasTests) {
1086      setStatus(HAS_NO_TEST);
1087      if (TestRunner.getVerbose() > 1) {
1088        System.err.println("[TestNG] No tests found. Nothing was run");
1089        usage();
1090      }
1091    }
1092  }
1093
1094  private void p(String string) {
1095    System.out.println("[TestNG] " + string);
1096  }
1097
1098  private void runSuiteAlterationListeners() {
1099    for (List<IAlterSuiteListener> listeners
1100        : Arrays.asList(m_alterSuiteListeners, m_configuration.getAlterSuiteListeners())) {
1101      for (IAlterSuiteListener l : listeners) {
1102        l.alter(m_suites);
1103      }
1104    }
1105  }
1106
1107  private void runExecutionListeners(boolean start) {
1108    for (List<IExecutionListener> listeners
1109        : Arrays.asList(m_executionListeners, m_configuration.getExecutionListeners())) {
1110      for (IExecutionListener l : listeners) {
1111        if (start) l.onExecutionStart();
1112        else l.onExecutionFinish();
1113      }
1114    }
1115  }
1116
1117  public void addAlterSuiteListener(IAlterSuiteListener l) {
1118    m_alterSuiteListeners.add(l);
1119  }
1120
1121  public void addExecutionListener(IExecutionListener l) {
1122    m_executionListeners.add(l);
1123  }
1124
1125  private static void usage() {
1126    if (m_jCommander == null) {
1127      m_jCommander = new JCommander(new CommandLineArgs());
1128    }
1129    m_jCommander.usage();
1130  }
1131
1132  private void generateReports(List<ISuite> suiteRunners) {
1133    for (IReporter reporter : m_reporters) {
1134      try {
1135        long start = System.currentTimeMillis();
1136        reporter.generateReport(m_suites, suiteRunners, m_outputDir);
1137        Utils.log("TestNG", 2, "Time taken by " + reporter + ": "
1138            + (System.currentTimeMillis() - start) + " ms");
1139      }
1140      catch(Exception ex) {
1141        System.err.println("[TestNG] Reporter " + reporter + " failed");
1142        ex.printStackTrace(System.err);
1143      }
1144    }
1145  }
1146
1147  /**
1148   * This needs to be public for maven2, for now..At least
1149   * until an alternative mechanism is found.
1150   */
1151  public List<ISuite> runSuitesLocally() {
1152    SuiteRunnerMap suiteRunnerMap = new SuiteRunnerMap();
1153    if (m_suites.size() > 0) {
1154      if (m_suites.get(0).getVerbose() >= 2) {
1155        Version.displayBanner();
1156      }
1157
1158      // First initialize the suite runners to ensure there are no configuration issues.
1159      // Create a map with XmlSuite as key and corresponding SuiteRunner as value
1160      for (XmlSuite xmlSuite : m_suites) {
1161        createSuiteRunners(suiteRunnerMap, xmlSuite);
1162      }
1163
1164      //
1165      // Run suites
1166      //
1167      if (m_suiteThreadPoolSize == 1 && !m_randomizeSuites) {
1168        // Single threaded and not randomized: run the suites in order
1169        for (XmlSuite xmlSuite : m_suites) {
1170          runSuitesSequentially(xmlSuite, suiteRunnerMap, getVerbose(xmlSuite),
1171              getDefaultSuiteName());
1172        }
1173      } else {
1174        // Multithreaded: generate a dynamic graph that stores the suite hierarchy. This is then
1175        // used to run related suites in specific order. Parent suites are run only
1176        // once all the child suites have completed execution
1177        DynamicGraph<ISuite> suiteGraph = new DynamicGraph<>();
1178        for (XmlSuite xmlSuite : m_suites) {
1179          populateSuiteGraph(suiteGraph, suiteRunnerMap, xmlSuite);
1180        }
1181
1182        IThreadWorkerFactory<ISuite> factory = new SuiteWorkerFactory(suiteRunnerMap,
1183          0 /* verbose hasn't been set yet */, getDefaultSuiteName());
1184        GraphThreadPoolExecutor<ISuite> pooledExecutor =
1185                new GraphThreadPoolExecutor<>(suiteGraph, factory, m_suiteThreadPoolSize,
1186                        m_suiteThreadPoolSize, Integer.MAX_VALUE, TimeUnit.MILLISECONDS,
1187                        new LinkedBlockingQueue<Runnable>());
1188
1189        Utils.log("TestNG", 2, "Starting executor for all suites");
1190        // Run all suites in parallel
1191        pooledExecutor.run();
1192        try {
1193          pooledExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
1194          pooledExecutor.shutdownNow();
1195        }
1196        catch (InterruptedException handled) {
1197          Thread.currentThread().interrupt();
1198          error("Error waiting for concurrent executors to finish " + handled.getMessage());
1199        }
1200      }
1201    }
1202    else {
1203      setStatus(HAS_NO_TEST);
1204      error("No test suite found. Nothing to run");
1205      usage();
1206    }
1207
1208    //
1209    // Generate the suites report
1210    //
1211    return Lists.newArrayList(suiteRunnerMap.values());
1212  }
1213
1214  private static void error(String s) {
1215    LOGGER.error(s);
1216  }
1217
1218  /**
1219   * @return the verbose level, checking in order: the verbose level on
1220   * the suite, the verbose level on the TestNG object, or 1.
1221   */
1222  private int getVerbose(XmlSuite xmlSuite) {
1223    int result = xmlSuite.getVerbose() != null ? xmlSuite.getVerbose()
1224        : (m_verbose != null ? m_verbose : DEFAULT_VERBOSE);
1225    return result;
1226  }
1227
1228  /**
1229   * Recursively runs suites. Runs the children suites before running the parent
1230   * suite. This is done so that the results for parent suite can reflect the
1231   * combined results of the children suites.
1232   *
1233   * @param xmlSuite XML Suite to be executed
1234   * @param suiteRunnerMap Maps {@code XmlSuite}s to respective {@code ISuite}
1235   * @param verbose verbose level
1236   * @param defaultSuiteName default suite name
1237   */
1238  private void runSuitesSequentially(XmlSuite xmlSuite,
1239      SuiteRunnerMap suiteRunnerMap, int verbose, String defaultSuiteName) {
1240    for (XmlSuite childSuite : xmlSuite.getChildSuites()) {
1241      runSuitesSequentially(childSuite, suiteRunnerMap, verbose, defaultSuiteName);
1242    }
1243    SuiteRunnerWorker srw = new SuiteRunnerWorker(suiteRunnerMap.get(xmlSuite), suiteRunnerMap,
1244      verbose, defaultSuiteName);
1245    srw.run();
1246  }
1247
1248  /**
1249   * Populates the dynamic graph with the reverse hierarchy of suites. Edges are
1250   * added pointing from child suite runners to parent suite runners, hence making
1251   * parent suite runners dependent on all the child suite runners
1252   *
1253   * @param suiteGraph dynamic graph representing the reverse hierarchy of SuiteRunners
1254   * @param suiteRunnerMap Map with XMLSuite as key and its respective SuiteRunner as value
1255   * @param xmlSuite XML Suite
1256   */
1257  private void populateSuiteGraph(DynamicGraph<ISuite> suiteGraph /* OUT */,
1258      SuiteRunnerMap suiteRunnerMap, XmlSuite xmlSuite) {
1259    ISuite parentSuiteRunner = suiteRunnerMap.get(xmlSuite);
1260    if (xmlSuite.getChildSuites().isEmpty()) {
1261      suiteGraph.addNode(parentSuiteRunner);
1262    }
1263    else {
1264      for (XmlSuite childSuite : xmlSuite.getChildSuites()) {
1265        suiteGraph.addEdge(parentSuiteRunner, suiteRunnerMap.get(childSuite));
1266        populateSuiteGraph(suiteGraph, suiteRunnerMap, childSuite);
1267      }
1268    }
1269  }
1270
1271  /**
1272   * Creates the {@code SuiteRunner}s and populates the suite runner map with
1273   * this information
1274   * @param suiteRunnerMap Map with XMLSuite as key and it's respective
1275   *   SuiteRunner as value. This is updated as part of this method call
1276   * @param xmlSuite Xml Suite (and its children) for which {@code SuiteRunner}s are created
1277   */
1278  private void createSuiteRunners(SuiteRunnerMap suiteRunnerMap /* OUT */, XmlSuite xmlSuite) {
1279    if (null != m_isJUnit && ! m_isJUnit.equals(XmlSuite.DEFAULT_JUNIT)) {
1280      xmlSuite.setJUnit(m_isJUnit);
1281    }
1282
1283    // If the skip flag was invoked on the command line, it
1284    // takes precedence
1285    if (null != m_skipFailedInvocationCounts) {
1286      xmlSuite.setSkipFailedInvocationCounts(m_skipFailedInvocationCounts);
1287    }
1288
1289    // Override the XmlSuite verbose value with the one from TestNG
1290    if (m_verbose != null) {
1291      xmlSuite.setVerbose(m_verbose);
1292    }
1293
1294    if (null != m_configFailurePolicy) {
1295      xmlSuite.setConfigFailurePolicy(m_configFailurePolicy);
1296    }
1297
1298    for (XmlTest t : xmlSuite.getTests()) {
1299      for (Map.Entry<String, Integer> ms : m_methodDescriptors.entrySet()) {
1300        XmlMethodSelector xms = new XmlMethodSelector();
1301        xms.setName(ms.getKey());
1302        xms.setPriority(ms.getValue());
1303        t.getMethodSelectors().add(xms);
1304      }
1305    }
1306
1307    suiteRunnerMap.put(xmlSuite, createSuiteRunner(xmlSuite));
1308
1309    for (XmlSuite childSuite : xmlSuite.getChildSuites()) {
1310      createSuiteRunners(suiteRunnerMap, childSuite);
1311    }
1312  }
1313
1314  /**
1315   * Creates a suite runner and configures its initial state
1316   * @param xmlSuite
1317   * @return returns the newly created suite runner
1318   */
1319  private SuiteRunner createSuiteRunner(XmlSuite xmlSuite) {
1320    SuiteRunner result = new SuiteRunner(getConfiguration(), xmlSuite,
1321        m_outputDir,
1322        m_testRunnerFactory,
1323        m_useDefaultListeners,
1324        m_methodInterceptors,
1325        m_invokedMethodListeners,
1326        m_testListeners,
1327        m_classListeners);
1328
1329    for (ISuiteListener isl : m_suiteListeners) {
1330      result.addListener(isl);
1331    }
1332
1333    for (IReporter r : result.getReporters()) {
1334      addListener(r);
1335    }
1336
1337    for (IConfigurationListener cl : m_configuration.getConfigurationListeners()) {
1338      result.addConfigurationListener(cl);
1339    }
1340
1341    return result;
1342  }
1343
1344  protected IConfiguration getConfiguration() {
1345    return m_configuration;
1346  }
1347
1348  /**
1349   * The TestNG entry point for command line execution.
1350   *
1351   * @param argv the TestNG command line parameters.
1352   * @throws FileNotFoundException
1353   */
1354  public static void main(String[] argv) {
1355    TestNG testng = privateMain(argv, null);
1356    System.exit(testng.getStatus());
1357  }
1358
1359  /**
1360   * <B>Note</B>: this method is not part of the public API and is meant for internal usage only.
1361   */
1362  public static TestNG privateMain(String[] argv, ITestListener listener) {
1363    TestNG result = new TestNG();
1364
1365    if (null != listener) {
1366      result.addListener(listener);
1367    }
1368
1369    //
1370    // Parse the arguments
1371    //
1372    try {
1373      CommandLineArgs cla = new CommandLineArgs();
1374      m_jCommander = new JCommander(cla, argv);
1375      validateCommandLineParameters(cla);
1376      result.configure(cla);
1377    }
1378    catch(ParameterException ex) {
1379      exitWithError(ex.getMessage());
1380    }
1381
1382    //
1383    // Run
1384    //
1385    try {
1386      result.run();
1387    }
1388    catch(TestNGException ex) {
1389      if (TestRunner.getVerbose() > 1) {
1390        ex.printStackTrace(System.out);
1391      }
1392      else {
1393        error(ex.getMessage());
1394      }
1395      result.setStatus(HAS_FAILURE);
1396    }
1397
1398    return result;
1399  }
1400
1401  /**
1402   * Configure the TestNG instance based on the command line parameters.
1403   */
1404  protected void configure(CommandLineArgs cla) {
1405    if (cla.verbose != null) {
1406      setVerbose(cla.verbose);
1407    }
1408    setOutputDirectory(cla.outputDirectory);
1409
1410    String testClasses = cla.testClass;
1411    if (null != testClasses) {
1412      String[] strClasses = testClasses.split(",");
1413      List<Class> classes = Lists.newArrayList();
1414      for (String c : strClasses) {
1415        classes.add(ClassHelper.fileToClass(c));
1416      }
1417
1418      setTestClasses(classes.toArray(new Class[classes.size()]));
1419    }
1420
1421    setOutputDirectory(cla.outputDirectory);
1422
1423    if (cla.testNames != null) {
1424      setTestNames(Arrays.asList(cla.testNames.split(",")));
1425    }
1426
1427//    List<String> testNgXml = (List<String>) cmdLineArgs.get(CommandLineArgs.SUITE_DEF);
1428//    if (null != testNgXml) {
1429//      setTestSuites(testNgXml);
1430//    }
1431
1432    // Note: can't use a Boolean field here because we are allowing a boolean
1433    // parameter with an arity of 1 ("-usedefaultlisteners false")
1434    if (cla.useDefaultListeners != null) {
1435      setUseDefaultListeners("true".equalsIgnoreCase(cla.useDefaultListeners));
1436    }
1437
1438    setGroups(cla.groups);
1439    setExcludedGroups(cla.excludedGroups);
1440    setTestJar(cla.testJar);
1441    setXmlPathInJar(cla.xmlPathInJar);
1442    setJUnit(cla.junit);
1443    setMixed(cla.mixed);
1444    setMaster(cla.master);
1445    setSlave(cla.slave);
1446    setSkipFailedInvocationCounts(cla.skipFailedInvocationCounts);
1447    if (cla.parallelMode != null) {
1448      setParallel(cla.parallelMode);
1449    }
1450    if (cla.configFailurePolicy != null) {
1451      setConfigFailurePolicy(cla.configFailurePolicy);
1452    }
1453    if (cla.threadCount != null) {
1454      setThreadCount(cla.threadCount);
1455    }
1456    if (cla.dataProviderThreadCount != null) {
1457      setDataProviderThreadCount(cla.dataProviderThreadCount);
1458    }
1459    if (cla.suiteName != null) {
1460      setDefaultSuiteName(cla.suiteName);
1461    }
1462    if (cla.testName != null) {
1463      setDefaultTestName(cla.testName);
1464    }
1465    if (cla.listener != null) {
1466      String sep = ";";
1467      if (cla.listener.contains(",")) {
1468        sep = ",";
1469      }
1470      String[] strs = Utils.split(cla.listener, sep);
1471      List<Class> classes = Lists.newArrayList();
1472
1473      for (String cls : strs) {
1474        classes.add(ClassHelper.fileToClass(cls));
1475      }
1476
1477      setListenerClasses(classes);
1478    }
1479
1480    if (null != cla.methodSelectors) {
1481      String[] strs = Utils.split(cla.methodSelectors, ",");
1482      for (String cls : strs) {
1483        String[] sel = Utils.split(cls, ":");
1484        try {
1485          if (sel.length == 2) {
1486            addMethodSelector(sel[0], Integer.parseInt(sel[1]));
1487          } else {
1488            error("Method selector value was not in the format org.example.Selector:4");
1489          }
1490        }
1491        catch (NumberFormatException nfe) {
1492          error("Method selector value was not in the format org.example.Selector:4");
1493        }
1494      }
1495    }
1496
1497    if (cla.objectFactory != null) {
1498      setObjectFactory(ClassHelper.fileToClass(cla.objectFactory));
1499    }
1500    if (cla.testRunnerFactory != null) {
1501      setTestRunnerFactoryClass(
1502          ClassHelper.fileToClass(cla.testRunnerFactory));
1503    }
1504
1505    if (cla.reporter != null) {
1506      ReporterConfig reporterConfig = ReporterConfig.deserialize(cla.reporter);
1507      addReporter(reporterConfig);
1508    }
1509
1510    if (cla.commandLineMethods.size() > 0) {
1511      m_commandLineMethods = cla.commandLineMethods;
1512    }
1513
1514    if (cla.suiteFiles != null) {
1515      setTestSuites(cla.suiteFiles);
1516    }
1517
1518    setSuiteThreadPoolSize(cla.suiteThreadPoolSize);
1519    setRandomizeSuites(cla.randomizeSuites);
1520  }
1521
1522  public void setSuiteThreadPoolSize(Integer suiteThreadPoolSize) {
1523    m_suiteThreadPoolSize = suiteThreadPoolSize;
1524  }
1525
1526  public Integer getSuiteThreadPoolSize() {
1527    return m_suiteThreadPoolSize;
1528  }
1529
1530   public void setRandomizeSuites(boolean randomizeSuites) {
1531     m_randomizeSuites = randomizeSuites;
1532   }
1533
1534  /**
1535   * This method is invoked by Maven's Surefire, only remove it once
1536   * Surefire has been modified to no longer call it.
1537   */
1538  public void setSourcePath(String path) {
1539    // nop
1540  }
1541
1542  /**
1543   * This method is invoked by Maven's Surefire to configure the runner,
1544   * do not remove unless you know for sure that Surefire has been updated
1545   * to use the new configure(CommandLineArgs) method.
1546   *
1547   * @deprecated use new configure(CommandLineArgs) method
1548   */
1549  @SuppressWarnings({"unchecked"})
1550  @Deprecated
1551  public void configure(Map cmdLineArgs) {
1552    CommandLineArgs result = new CommandLineArgs();
1553
1554    Integer verbose = (Integer) cmdLineArgs.get(CommandLineArgs.LOG);
1555    if (null != verbose) {
1556      result.verbose = verbose;
1557    }
1558    result.outputDirectory = (String) cmdLineArgs.get(CommandLineArgs.OUTPUT_DIRECTORY);
1559
1560    String testClasses = (String) cmdLineArgs.get(CommandLineArgs.TEST_CLASS);
1561    if (null != testClasses) {
1562      result.testClass = testClasses;
1563    }
1564
1565    String testNames = (String) cmdLineArgs.get(CommandLineArgs.TEST_NAMES);
1566    if (testNames != null) {
1567      result.testNames = testNames;
1568    }
1569
1570    String useDefaultListeners = (String) cmdLineArgs.get(CommandLineArgs.USE_DEFAULT_LISTENERS);
1571    if (null != useDefaultListeners) {
1572      result.useDefaultListeners = useDefaultListeners;
1573    }
1574
1575    result.groups = (String) cmdLineArgs.get(CommandLineArgs.GROUPS);
1576    result.excludedGroups = (String) cmdLineArgs.get(CommandLineArgs.EXCLUDED_GROUPS);
1577    result.testJar = (String) cmdLineArgs.get(CommandLineArgs.TEST_JAR);
1578    result.xmlPathInJar = (String) cmdLineArgs.get(CommandLineArgs.XML_PATH_IN_JAR);
1579    result.junit = (Boolean) cmdLineArgs.get(CommandLineArgs.JUNIT);
1580    result.mixed = (Boolean) cmdLineArgs.get(CommandLineArgs.MIXED);
1581    result.master = (String) cmdLineArgs.get(CommandLineArgs.MASTER);
1582    result.slave = (String) cmdLineArgs.get(CommandLineArgs.SLAVE);
1583    result.skipFailedInvocationCounts = (Boolean) cmdLineArgs.get(
1584        CommandLineArgs.SKIP_FAILED_INVOCATION_COUNTS);
1585    String parallelMode = (String) cmdLineArgs.get(CommandLineArgs.PARALLEL);
1586    if (parallelMode != null) {
1587      result.parallelMode = XmlSuite.ParallelMode.getValidParallel(parallelMode);
1588    }
1589
1590    String threadCount = (String) cmdLineArgs.get(CommandLineArgs.THREAD_COUNT);
1591    if (threadCount != null) {
1592      result.threadCount = Integer.parseInt(threadCount);
1593    }
1594
1595    // Not supported by Surefire yet
1596    Integer dptc = (Integer) cmdLineArgs.get(CommandLineArgs.DATA_PROVIDER_THREAD_COUNT);
1597    if (dptc != null) {
1598      result.dataProviderThreadCount = dptc;
1599    }
1600    String defaultSuiteName = (String) cmdLineArgs.get(CommandLineArgs.SUITE_NAME);
1601    if (defaultSuiteName != null) {
1602      result.suiteName = defaultSuiteName;
1603    }
1604
1605    String defaultTestName = (String) cmdLineArgs.get(CommandLineArgs.TEST_NAME);
1606    if (defaultTestName != null) {
1607      result.testName = defaultTestName;
1608    }
1609
1610    Object listeners = cmdLineArgs.get(CommandLineArgs.LISTENER);
1611    if (listeners instanceof List) {
1612      result.listener = Utils.join((List<?>) listeners, ",");
1613    } else {
1614      result.listener = (String) listeners;
1615    }
1616
1617    String ms = (String) cmdLineArgs.get(CommandLineArgs.METHOD_SELECTORS);
1618    if (null != ms) {
1619      result.methodSelectors = ms;
1620    }
1621
1622    String objectFactory = (String) cmdLineArgs.get(CommandLineArgs.OBJECT_FACTORY);
1623    if(null != objectFactory) {
1624      result.objectFactory = objectFactory;
1625    }
1626
1627    String runnerFactory = (String) cmdLineArgs.get(CommandLineArgs.TEST_RUNNER_FACTORY);
1628    if (null != runnerFactory) {
1629      result.testRunnerFactory = runnerFactory;
1630    }
1631
1632    String reporterConfigs = (String) cmdLineArgs.get(CommandLineArgs.REPORTER);
1633    if (reporterConfigs != null) {
1634      result.reporter = reporterConfigs;
1635    }
1636
1637    String failurePolicy = (String)cmdLineArgs.get(CommandLineArgs.CONFIG_FAILURE_POLICY);
1638    if (failurePolicy != null) {
1639      result.configFailurePolicy = failurePolicy;
1640    }
1641
1642    Object  suiteThreadPoolSize = cmdLineArgs.get(CommandLineArgs.SUITE_THREAD_POOL_SIZE);
1643    if (null != suiteThreadPoolSize) {
1644        if (suiteThreadPoolSize instanceof String){
1645            result.suiteThreadPoolSize=Integer.parseInt((String) suiteThreadPoolSize);
1646        }
1647        if (suiteThreadPoolSize instanceof Integer){
1648            result.suiteThreadPoolSize=(Integer) suiteThreadPoolSize;
1649        }
1650    }
1651
1652    configure(result);
1653  }
1654
1655  /**
1656   * Only run the specified tests from the suite.
1657   */
1658  public void setTestNames(List<String> testNames) {
1659    m_testNames = testNames;
1660  }
1661
1662  public void setSkipFailedInvocationCounts(Boolean skip) {
1663    m_skipFailedInvocationCounts = skip;
1664  }
1665
1666  private void addReporter(ReporterConfig reporterConfig) {
1667    Object instance = reporterConfig.newReporterInstance();
1668    if (instance != null) {
1669      addListener(instance);
1670    } else {
1671      LOGGER.warn("Could not find reporte class : " + reporterConfig.getClassName());
1672    }
1673  }
1674
1675  /**
1676   * Specify if this run should be in Master-Slave mode as Master
1677   *
1678   * @param fileName remote.properties path
1679   */
1680  public void setMaster(String fileName) {
1681     m_masterfileName = fileName;
1682  }
1683
1684  /**
1685   * Specify if this run should be in Master-Slave mode as slave
1686   *
1687   * @param fileName remote.properties path
1688   */
1689  public void setSlave(String fileName) {
1690     m_slavefileName = fileName;
1691  }
1692
1693  /**
1694   * Specify if this run should be made in JUnit mode
1695   *
1696   * @param isJUnit
1697   */
1698  public void setJUnit(Boolean isJUnit) {
1699    m_isJUnit = isJUnit;
1700  }
1701
1702  /**
1703   * Specify if this run should be made in mixed mode
1704   */
1705  public void setMixed(Boolean isMixed) {
1706      if(isMixed==null){
1707          return;
1708      }
1709    m_isMixed = isMixed;
1710  }
1711
1712  /**
1713   * @deprecated The TestNG version is now established at load time. This
1714   * method is not required anymore and is now a no-op.
1715   */
1716  @Deprecated
1717  public static void setTestNGVersion() {
1718    LOGGER.info("setTestNGVersion has been deprecated.");
1719  }
1720
1721  /**
1722   * Returns true if this is the JDK 1.4 JAR version of TestNG, false otherwise.
1723   *
1724   * @return true if this is the JDK 1.4 JAR version of TestNG, false otherwise.
1725   */
1726  @Deprecated
1727  public static boolean isJdk14() {
1728    return false;
1729  }
1730
1731  /**
1732   * Double check that the command line parameters are valid.
1733   */
1734  protected static void validateCommandLineParameters(CommandLineArgs args) {
1735    String testClasses = args.testClass;
1736    List<String> testNgXml = args.suiteFiles;
1737    String testJar = args.testJar;
1738    String slave = args.slave;
1739    List<String> methods = args.commandLineMethods;
1740
1741    if (testClasses == null && slave == null && testJar == null
1742        && (testNgXml == null || testNgXml.isEmpty())
1743        && (methods == null || methods.isEmpty())) {
1744      throw new ParameterException("You need to specify at least one testng.xml, one class"
1745          + " or one method");
1746    }
1747
1748    String groups = args.groups;
1749    String excludedGroups = args.excludedGroups;
1750
1751    if (testJar == null &&
1752        (null != groups || null != excludedGroups) && testClasses == null
1753        && (testNgXml == null || testNgXml.isEmpty())) {
1754      throw new ParameterException("Groups option should be used with testclass option");
1755    }
1756
1757    if (args.slave != null && args.master != null) {
1758     throw new ParameterException(CommandLineArgs.SLAVE + " can't be combined with "
1759         + CommandLineArgs.MASTER);
1760    }
1761
1762    Boolean junit = args.junit;
1763    Boolean mixed = args.mixed;
1764    if (junit && mixed) {
1765     throw new ParameterException(CommandLineArgs.MIXED + " can't be combined with "
1766         + CommandLineArgs.JUNIT);
1767    }
1768  }
1769
1770  /**
1771   * @return true if at least one test failed.
1772   */
1773  public boolean hasFailure() {
1774    return (getStatus() & HAS_FAILURE) == HAS_FAILURE;
1775  }
1776
1777  /**
1778   * @return true if at least one test failed within success percentage.
1779   */
1780  public boolean hasFailureWithinSuccessPercentage() {
1781    return (getStatus() & HAS_FSP) == HAS_FSP;
1782  }
1783
1784  /**
1785   * @return true if at least one test was skipped.
1786   */
1787  public boolean hasSkip() {
1788    return (getStatus() & HAS_SKIPPED) == HAS_SKIPPED;
1789  }
1790
1791  static void exitWithError(String msg) {
1792    System.err.println(msg);
1793    usage();
1794    System.exit(1);
1795  }
1796
1797  public String getOutputDirectory() {
1798    return m_outputDir;
1799  }
1800
1801  public IAnnotationTransformer getAnnotationTransformer() {
1802    return m_annotationTransformer;
1803  }
1804
1805  public void setAnnotationTransformer(IAnnotationTransformer t) {
1806	// compare by reference!
1807    if (m_annotationTransformer != m_defaultAnnoProcessor && m_annotationTransformer != t) {
1808    	LOGGER.warn("AnnotationTransformer already set");
1809    }
1810    m_annotationTransformer = t;
1811  }
1812
1813  /**
1814   * @return the defaultSuiteName
1815   */
1816  public String getDefaultSuiteName() {
1817    return m_defaultSuiteName;
1818  }
1819
1820  /**
1821   * @param defaultSuiteName the defaultSuiteName to set
1822   */
1823  public void setDefaultSuiteName(String defaultSuiteName) {
1824    m_defaultSuiteName = defaultSuiteName;
1825  }
1826
1827  /**
1828   * @return the defaultTestName
1829   */
1830  public String getDefaultTestName() {
1831    return m_defaultTestName;
1832  }
1833
1834  /**
1835   * @param defaultTestName the defaultTestName to set
1836   */
1837  public void setDefaultTestName(String defaultTestName) {
1838    m_defaultTestName = defaultTestName;
1839  }
1840
1841  /**
1842   * Sets the policy for whether or not to ever invoke a configuration method again after
1843   * it has failed once. Possible values are defined in {@link XmlSuite}.  The default
1844   * value is {@link XmlSuite#SKIP}.
1845   * @param failurePolicy the configuration failure policy
1846   */
1847  public void setConfigFailurePolicy(String failurePolicy) {
1848    m_configFailurePolicy = failurePolicy;
1849  }
1850
1851  /**
1852   * Returns the configuration failure policy.
1853   * @return config failure policy
1854   */
1855  public String getConfigFailurePolicy() {
1856    return m_configFailurePolicy;
1857  }
1858
1859  // DEPRECATED: to be removed after a major version change
1860  /**
1861   * @deprecated since 5.1
1862   */
1863  @Deprecated
1864  public static TestNG getDefault() {
1865    return m_instance;
1866  }
1867
1868  /**
1869   * @deprecated since 5.1
1870   */
1871  @Deprecated
1872  public void setHasFailure(boolean hasFailure) {
1873    m_status |= HAS_FAILURE;
1874  }
1875
1876  /**
1877   * @deprecated since 5.1
1878   */
1879  @Deprecated
1880  public void setHasFailureWithinSuccessPercentage(boolean hasFailureWithinSuccessPercentage) {
1881    m_status |= HAS_FSP;
1882  }
1883
1884  /**
1885   * @deprecated since 5.1
1886   */
1887  @Deprecated
1888  public void setHasSkip(boolean hasSkip) {
1889    m_status |= HAS_SKIPPED;
1890  }
1891
1892  public static class ExitCodeListener implements IResultListener2 {
1893    private TestNG m_mainRunner;
1894
1895    public ExitCodeListener() {
1896      m_mainRunner = TestNG.m_instance;
1897    }
1898
1899    public ExitCodeListener(TestNG runner) {
1900      m_mainRunner = runner;
1901    }
1902
1903    @Override
1904    public void beforeConfiguration(ITestResult tr) {
1905    }
1906
1907    @Override
1908    public void onTestFailure(ITestResult result) {
1909      setHasRunTests();
1910      m_mainRunner.setStatus(HAS_FAILURE);
1911    }
1912
1913    @Override
1914    public void onTestSkipped(ITestResult result) {
1915      setHasRunTests();
1916      if ((m_mainRunner.getStatus() & HAS_FAILURE) != 0) {
1917        m_mainRunner.setStatus(HAS_SKIPPED);
1918      }
1919    }
1920
1921    @Override
1922    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
1923      setHasRunTests();
1924      m_mainRunner.setStatus(HAS_FSP);
1925    }
1926
1927    @Override
1928    public void onTestSuccess(ITestResult result) {
1929      setHasRunTests();
1930    }
1931
1932    @Override
1933    public void onStart(ITestContext context) {
1934      setHasRunTests();
1935    }
1936
1937    @Override
1938    public void onFinish(ITestContext context) {
1939    }
1940
1941    @Override
1942    public void onTestStart(ITestResult result) {
1943      setHasRunTests();
1944    }
1945
1946    private void setHasRunTests() {
1947      m_mainRunner.m_hasTests= true;
1948    }
1949
1950    /**
1951     * @see org.testng.IConfigurationListener#onConfigurationFailure(org.testng.ITestResult)
1952     */
1953    @Override
1954    public void onConfigurationFailure(ITestResult itr) {
1955      m_mainRunner.setStatus(HAS_FAILURE);
1956    }
1957
1958    /**
1959     * @see org.testng.IConfigurationListener#onConfigurationSkip(org.testng.ITestResult)
1960     */
1961    @Override
1962    public void onConfigurationSkip(ITestResult itr) {
1963      m_mainRunner.setStatus(HAS_SKIPPED);
1964    }
1965
1966    /**
1967     * @see org.testng.IConfigurationListener#onConfigurationSuccess(org.testng.ITestResult)
1968     */
1969    @Override
1970    public void onConfigurationSuccess(ITestResult itr) {
1971    }
1972  }
1973
1974  private void setConfigurable(IConfigurable c) {
1975	// compare by reference!
1976    if (m_configurable != null && m_configurable != c) {
1977    	LOGGER.warn("Configurable already set");
1978	}
1979    m_configurable = c;
1980  }
1981
1982  private void setHookable(IHookable h) {
1983	// compare by reference!
1984    if (m_hookable != null && m_hookable != h) {
1985    	LOGGER.warn("Hookable already set");
1986    }
1987    m_hookable = h;
1988  }
1989
1990  public void setMethodInterceptor(IMethodInterceptor methodInterceptor) {
1991    m_methodInterceptors.add(methodInterceptor);
1992  }
1993
1994  public void setDataProviderThreadCount(int count) {
1995    m_dataProviderThreadCount = count;
1996  }
1997
1998  /** Add a class loader to the searchable loaders. */
1999  public void addClassLoader(final ClassLoader loader) {
2000    if (loader != null) {
2001      ClassHelper.addClassLoader(loader);
2002    }
2003  }
2004
2005  public void setPreserveOrder(boolean b) {
2006    m_preserveOrder = b;
2007  }
2008
2009  protected long getStart() {
2010    return m_start;
2011  }
2012
2013  protected long getEnd() {
2014    return m_end;
2015  }
2016
2017  public void setGroupByInstances(boolean b) {
2018    m_groupByInstances = b;
2019  }
2020
2021  /////
2022  // ServiceLoader testing
2023  //
2024
2025  private URLClassLoader m_serviceLoaderClassLoader;
2026  private List<ITestNGListener> m_serviceLoaderListeners = Lists.newArrayList();
2027
2028  /*
2029   * Used to test ServiceClassLoader
2030   */
2031  public void setServiceLoaderClassLoader(URLClassLoader ucl) {
2032    m_serviceLoaderClassLoader = ucl;
2033  }
2034
2035  /*
2036   * Used to test ServiceClassLoader
2037   */
2038  private void addServiceLoaderListener(ITestNGListener l) {
2039    m_serviceLoaderListeners.add(l);
2040  }
2041
2042  /*
2043   * Used to test ServiceClassLoader
2044   */
2045  public List<ITestNGListener> getServiceLoaderListeners() {
2046    return m_serviceLoaderListeners;
2047  }
2048
2049  //
2050  // ServiceLoader testing
2051  /////
2052}
2053