TestNGContentHandler.java revision 8c68a68e17e85e97da8ebce4425fc7287f8fc21e
1package org.testng.xml;
2
3import org.testng.ITestObjectFactory;
4import org.testng.TestNGException;
5import org.testng.collections.Lists;
6import org.testng.collections.Maps;
7import org.testng.internal.Utils;
8import org.testng.internal.version.VersionInfo;
9import org.xml.sax.Attributes;
10import org.xml.sax.InputSource;
11import org.xml.sax.SAXException;
12import org.xml.sax.SAXParseException;
13import org.xml.sax.helpers.DefaultHandler;
14
15import java.io.IOException;
16import java.io.InputStream;
17import java.util.ArrayList;
18import java.util.List;
19import java.util.Map;
20
21/**
22 * Suite definition parser utility.
23 *
24 * @author Cedric Beust
25 * @author <a href='mailto:the_mindstorm@evolva.ro'>Alexandru Popescu</a>
26 */
27public class TestNGContentHandler extends DefaultHandler {
28  private XmlSuite m_currentSuite = null;
29  private XmlTest m_currentTest = null;
30  private List<String> m_currentDefines = null;
31  private List<String> m_currentRuns = null;
32  private List<XmlClass> m_currentClasses = null;
33  private int m_currentTestIndex = 0;
34  private int m_currentClassIndex = 0;
35  private int m_currentIncludeIndex = 0;
36  private List<XmlPackage> m_currentPackages = null;
37  private XmlPackage m_currentPackage = null;
38  private List<XmlSuite> m_suites = Lists.newArrayList();
39  private List<String> m_currentIncludedGroups = null;
40  private List<String> m_currentExcludedGroups = null;
41  private Map<String, String> m_currentTestParameters = null;
42  private Map<String, String> m_currentSuiteParameters = null;
43  private List<String> m_currentMetaGroup = null;
44  private String m_currentMetaGroupName;
45  private boolean m_inTest = false;
46  private XmlClass m_currentClass = null;
47  private ArrayList<XmlInclude> m_currentIncludedMethods = null;
48  private List<String> m_currentExcludedMethods = null;
49  private ArrayList<XmlMethodSelector> m_currentSelectors = null;
50  private XmlMethodSelector m_currentSelector = null;
51  private String m_currentLanguage = null;
52  private String m_currentExpression = null;
53  private List<String> m_suiteFiles = Lists.newArrayList();
54  private boolean m_enabledTest;
55  private List<String> m_listeners;
56
57  private String m_fileName;
58  private boolean m_loadClasses;
59
60  public TestNGContentHandler(String fileName, boolean loadClasses) {
61    m_fileName = fileName;
62    m_loadClasses = loadClasses;
63  }
64
65  static private void ppp(String s) {
66    System.out.println("[TestNGContentHandler] " + s);
67  }
68
69  /*
70   * (non-Javadoc)
71   *
72   * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
73   *      java.lang.String)
74   */
75  @Override
76  public InputSource resolveEntity(String systemId, String publicId)
77      throws IOException, SAXException {
78    InputSource result = null;
79    if (Parser.DEPRECATED_TESTNG_DTD_URL.equals(publicId)
80        || Parser.TESTNG_DTD_URL.equals(publicId)) {
81      InputStream is = getClass().getClassLoader().getResourceAsStream(Parser.TESTNG_DTD);
82      if (null == is) {
83        is = Thread.currentThread().getContextClassLoader().getResourceAsStream(Parser.TESTNG_DTD);
84        if (null == is) {
85          System.out.println("WARNING: couldn't find in classpath " + publicId
86              + "\n" + "Fetching it from the Web site.");
87          result = super.resolveEntity(systemId, publicId);
88        }
89        else {
90          result = new InputSource(is);
91        }
92      }
93      else {
94        result = new InputSource(is);
95      }
96    }
97    else {
98      result = super.resolveEntity(systemId, publicId);
99    }
100
101    return result;
102  }
103
104  /**
105   * Parse <suite-file>
106   */
107  private void xmlSuiteFile(boolean start, Attributes attributes) {
108    if (start) {
109      String path = attributes.getValue("path");
110      m_suiteFiles.add(path);
111    }
112    else {
113      m_currentSuite.setSuiteFiles(m_suiteFiles);
114    }
115  }
116
117  /**
118   * Parse <suite>
119   */
120  private void xmlSuite(boolean start, Attributes attributes) {
121    if (start) {
122      String name = attributes.getValue("name");
123      m_currentSuite = new XmlSuite();
124      m_currentSuite.setFileName(m_fileName);
125      m_currentSuite.setName(name);
126      m_currentSuiteParameters = Maps.newHashMap();
127
128      String verbose = attributes.getValue("verbose");
129      if (null != verbose) {
130        m_currentSuite.setVerbose(new Integer(verbose));
131      }
132      String jUnit = attributes.getValue("junit");
133      if (null != jUnit) {
134        m_currentSuite.setJUnit( Boolean.valueOf(jUnit).booleanValue());
135      }
136      String parallel = attributes.getValue("parallel");
137      if (null != parallel) {
138        if(XmlSuite.PARALLEL_METHODS.equals(parallel)
139            || XmlSuite.PARALLEL_TESTS.equals(parallel)
140            || XmlSuite.PARALLEL_NONE.equals(parallel)
141            || XmlSuite.PARALLEL_CLASSES.equals(parallel)
142            || "true".equals(parallel)
143            || "false".equals(parallel)) {
144          m_currentSuite.setParallel(parallel);
145        }
146        else {
147          Utils.log("Parser", 1, "[WARN] Unknown value of attribute 'parallel' at suite level: '" + parallel + "'.");
148        }
149      }
150      String configFailurePolicy = attributes.getValue("configfailurepolicy");
151      if (null != configFailurePolicy) {
152        if (XmlSuite.SKIP.equals(configFailurePolicy) || XmlSuite.CONTINUE.equals(configFailurePolicy)) {
153          m_currentSuite.setConfigFailurePolicy(configFailurePolicy);
154        }
155      }
156      String groupByInstances = attributes.getValue("group-by-instances");
157      if (groupByInstances!= null) {
158        m_currentSuite.setGroupByInstances(Boolean.valueOf(groupByInstances).booleanValue());
159      }
160      String skip = attributes.getValue("skipfailedinvocationcounts");
161      if (skip != null) {
162        m_currentSuite.setSkipFailedInvocationCounts(Boolean.valueOf(skip));
163      }
164      String threadCount = attributes.getValue("thread-count");
165      if (null != threadCount) {
166        m_currentSuite.setThreadCount(Integer.parseInt(threadCount));
167      }
168      String dataProviderThreadCount = attributes.getValue("data-provider-thread-count");
169      if (null != dataProviderThreadCount) {
170        m_currentSuite.setDataProviderThreadCount(Integer.parseInt(dataProviderThreadCount));
171      }
172      String annotations = attributes.getValue("annotations");
173      if (null != annotations) {
174        m_currentSuite.setAnnotations(annotations);
175      }
176      else if (VersionInfo.IS_JDK14) {
177        m_currentSuite.setAnnotations(XmlSuite.JAVADOC_ANNOTATION_TYPE);
178      }
179      String timeOut = attributes.getValue("time-out");
180      if (null != timeOut) {
181        m_currentSuite.setTimeOut(timeOut);
182      }
183      String objectFactory = attributes.getValue("object-factory");
184      if (null != objectFactory && m_loadClasses) {
185        try {
186          m_currentSuite.setObjectFactory((ITestObjectFactory)Class.forName(objectFactory).newInstance());
187        }
188        catch(Exception e) {
189          Utils.log("Parser", 1, "[ERROR] Unable to create custom object factory '" + objectFactory + "' :" + e);
190        }
191      }
192      String preserveOrder = attributes.getValue("preserve-order");
193      if (preserveOrder != null) {
194        m_currentSuite.setPreserveOrder(preserveOrder);
195      }
196    }
197    else {
198      m_currentSuite.setParameters(m_currentSuiteParameters);
199      m_suites.add(m_currentSuite);
200      m_currentSuiteParameters = null;
201    }
202  }
203
204  /**
205   * Parse <define>
206   */
207  private void xmlDefine(boolean start, Attributes attributes) {
208    if (start) {
209      String name = attributes.getValue("name");
210      m_currentDefines = Lists.newArrayList();
211      m_currentMetaGroup = Lists.newArrayList();
212      m_currentMetaGroupName = name;
213    }
214    else {
215      m_currentTest.addMetaGroup(m_currentMetaGroupName, m_currentMetaGroup);
216      m_currentDefines = null;
217    }
218  }
219
220  /**
221   * Parse <script>
222   */
223  private void xmlScript(boolean start, Attributes attributes) {
224    if (start) {
225//      ppp("OPEN SCRIPT");
226      m_currentLanguage = attributes.getValue("language");
227      m_currentExpression = "";
228    }
229    else {
230//      ppp("CLOSE SCRIPT:@@" + m_currentExpression + "@@");
231      m_currentSelector.setExpression(m_currentExpression);
232      m_currentSelector.setLanguage(m_currentLanguage);
233      if (m_inTest) {
234        m_currentTest.setBeanShellExpression(m_currentExpression);
235      }
236      m_currentLanguage = null;
237      m_currentExpression = null;
238    }
239  }
240
241  /**
242   * Parse <test>
243   */
244  private void xmlTest(boolean start, Attributes attributes) {
245    if (start) {
246      m_currentTest = new XmlTest(m_currentSuite, m_currentTestIndex++);
247      m_currentTestParameters = Maps.newHashMap();
248      final String testName= attributes.getValue("name");
249      if(null == testName || "".equals(testName.trim())) {
250        throw new TestNGException("Test <test> element must define the name attribute");
251      }
252      m_currentTest.setName(attributes.getValue("name"));
253      String verbose = attributes.getValue("verbose");
254      if (null != verbose) {
255        m_currentTest.setVerbose(Integer.parseInt(verbose));
256      }
257      String jUnit = attributes.getValue("junit");
258      if (null != jUnit) {
259        m_currentTest.setJUnit( Boolean.valueOf(jUnit).booleanValue());
260      }
261      String skip = attributes.getValue("skipfailedinvocationcounts");
262      if (skip != null) {
263        m_currentTest.setSkipFailedInvocationCounts(Boolean.valueOf(skip).booleanValue());
264      }
265      String groupByInstances = attributes.getValue("group-by-instances");
266      if (groupByInstances!= null) {
267        m_currentTest.setGroupByInstances(Boolean.valueOf(groupByInstances).booleanValue());
268      }
269      String preserveOrder = attributes.getValue("preserve-order");
270      if (preserveOrder != null) {
271        m_currentTest.setPreserveOrder(preserveOrder);
272      }
273      String parallel = attributes.getValue("parallel");
274      if (null != parallel) {
275        if(XmlSuite.PARALLEL_METHODS.equals(parallel)
276            || XmlSuite.PARALLEL_NONE.equals(parallel)
277            || XmlSuite.PARALLEL_CLASSES.equals(parallel)
278            || "true".equals(parallel)
279            || "false".equals(parallel)) {
280          m_currentTest.setParallel(parallel);
281        }
282        else {
283          Utils.log("Parser", 1, "[WARN] Unknown value of attribute 'parallel' for test '" + m_currentTest.getName() + "': '" + parallel + "'");
284        }
285      }
286      String threadCount = attributes.getValue("thread-count");
287      if(null != threadCount) {
288        m_currentTest.setThreadCount(Integer.parseInt(threadCount));
289      }
290      String timeOut = attributes.getValue("time-out");
291      if (null != timeOut) {
292        m_currentTest.setTimeOut(Long.parseLong(timeOut));
293      }
294      String annotations = attributes.getValue("annotations");
295      if (null != annotations) {
296        m_currentTest.setAnnotations(annotations);
297      }
298      m_inTest = true;
299      m_enabledTest= true;
300      String enabledTestString = attributes.getValue("enabled");
301      if(null != enabledTestString) {
302        m_enabledTest = Boolean.valueOf(enabledTestString);
303      }
304    }
305    else {
306      if (null != m_currentTestParameters && m_currentTestParameters.size() > 0) {
307        m_currentTest.setParameters(m_currentTestParameters);
308      }
309      if (null != m_currentClasses) {
310        m_currentTest.setXmlClasses(m_currentClasses);
311      }
312      m_currentClasses = null;
313      m_currentTest = null;
314      m_currentTestParameters = null;
315      m_inTest = false;
316      if(!m_enabledTest) {
317        List<XmlTest> tests= m_currentSuite.getTests();
318        tests.remove(tests.size() - 1);
319      }
320    }
321  }
322
323  /**
324   * Parse <classes>
325   */
326  public void xmlClasses(boolean start, Attributes attributes) {
327    if (start) {
328      m_currentClasses = Lists.newArrayList();
329      m_currentClassIndex = 0;
330    }
331    else {
332      m_currentTest.setXmlClasses(m_currentClasses);
333      m_currentClasses = null;
334    }
335  }
336
337  /**
338   * Parse <listeners>
339   */
340  public void xmlListeners(boolean start, Attributes attributes) {
341    if (start) {
342      m_listeners = Lists.newArrayList();
343    }
344    else {
345      if (null != m_listeners) {
346        m_currentSuite.setListeners(m_listeners);
347        m_listeners = null;
348      }
349    }
350  }
351
352  /**
353   * Parse <listener>
354   */
355  public void xmlListener(boolean start, Attributes attributes) {
356    if (start) {
357      String listener = attributes.getValue("class-name");
358      m_listeners.add(listener);
359    }
360  }
361
362  /**
363   * Parse <packages>
364   */
365  public void xmlPackages(boolean start, Attributes attributes) {
366    if (start) {
367      m_currentPackages = Lists.newArrayList();
368    }
369    else {
370      if (null != m_currentPackages) {
371        if(m_inTest) {
372          m_currentTest.setXmlPackages(m_currentPackages);
373        }
374        else {
375          m_currentSuite.setXmlPackages(m_currentPackages);
376        }
377      }
378
379      m_currentPackages = null;
380      m_currentPackage = null;
381    }
382  }
383
384  /**
385   * Parse <method-selectors>
386   */
387  public void xmlMethodSelectors(boolean start, Attributes attributes) {
388    if (start) {
389      m_currentSelectors = new ArrayList<XmlMethodSelector>();
390    }
391    else {
392      if (m_inTest) {
393        m_currentTest.setMethodSelectors(m_currentSelectors);
394      }
395      else {
396        m_currentSuite.setMethodSelectors(m_currentSelectors);
397      }
398
399      m_currentSelectors = null;
400    }
401  }
402
403  /**
404   * Parse <selector-class>
405   */
406  public void xmlSelectorClass(boolean start, Attributes attributes) {
407    if (start) {
408      m_currentSelector.setName(attributes.getValue("name"));
409      String priority = attributes.getValue("priority");
410      if (priority == null) {
411        priority = "0";
412      }
413      m_currentSelector.setPriority(new Integer(priority));
414    }
415    else {
416      // do nothing
417    }
418  }
419
420  /**
421   * Parse <method-selector>
422   */
423  public void xmlMethodSelector(boolean start, Attributes attributes) {
424    if (start) {
425      m_currentSelector = new XmlMethodSelector();
426    }
427    else {
428      m_currentSelectors.add(m_currentSelector);
429      m_currentSelector = null;
430    }
431  }
432
433  private void xmlMethod(boolean start, Attributes attributes) {
434    if (start) {
435      m_currentIncludedMethods = new ArrayList<XmlInclude>();
436      m_currentExcludedMethods = Lists.newArrayList();
437      m_currentIncludeIndex = 0;
438    }
439    else {
440      m_currentClass.setIncludedMethods(m_currentIncludedMethods);
441      m_currentClass.setExcludedMethods(m_currentExcludedMethods);
442      m_currentIncludedMethods = null;
443      m_currentExcludedMethods = null;
444    }
445  }
446
447  /**
448   * Parse <run>
449   */
450  public void xmlRun(boolean start, Attributes attributes) throws SAXException {
451    if (start) {
452      m_currentRuns = Lists.newArrayList();
453    }
454    else {
455      if (m_currentTest != null) {
456        m_currentTest.setIncludedGroups(m_currentIncludedGroups);
457        m_currentTest.setExcludedGroups(m_currentExcludedGroups);
458      } else {
459        m_currentSuite.setIncludedGroups(m_currentIncludedGroups);
460        m_currentSuite.setExcludedGroups(m_currentExcludedGroups);
461      }
462      m_currentRuns = null;
463    }
464  }
465
466  /**
467   * NOTE: I only invoke xml*methods (e.g. xmlSuite()) if I am acting on both
468   * the start and the end of the tag. This way I can keep the treatment of
469   * this tag in one place. If I am only doing something when the tag opens,
470   * the code is inlined below in the startElement() method.
471   */
472  @Override
473  public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
474    String name = attributes.getValue("name");
475
476    // ppp("START ELEMENT uri:" + uri + " sName:" + localName + " qName:" + qName +
477    // " " + attributes);
478    if ("suite".equals(qName)) {
479      xmlSuite(true, attributes);
480    }
481    else if ("suite-file".equals(qName)) {
482      xmlSuiteFile(true, attributes);
483    }
484    else if ("test".equals(qName)) {
485      xmlTest(true, attributes);
486    }
487    else if ("script".equals(qName)) {
488      xmlScript(true, attributes);
489    }
490    else if ("method-selector".equals(qName)) {
491      xmlMethodSelector(true, attributes);
492    }
493    else if ("method-selectors".equals(qName)) {
494      xmlMethodSelectors(true, attributes);
495    }
496    else if ("selector-class".equals(qName)) {
497      xmlSelectorClass(true, attributes);
498    }
499    else if ("classes".equals(qName)) {
500      xmlClasses(true, attributes);
501    }
502    else if ("packages".equals(qName)) {
503      xmlPackages(true, attributes);
504    }
505    else if ("listeners".equals(qName)) {
506      xmlListeners(true, attributes);
507    }
508    else if ("listener".equals(qName)) {
509      xmlListener(true, attributes);
510    }
511    else if ("class".equals(qName)) {
512      // If m_currentClasses is null, the XML is invalid and SAX
513      // will complain, but in the meantime, dodge the NPE so SAX
514      // can finish parsing the file.
515      if (null != m_currentClasses) {
516        m_currentClass = new XmlClass(name, m_currentClassIndex++, m_loadClasses);
517        m_currentClasses.add(m_currentClass);
518      }
519    }
520    else if ("package".equals(qName)) {
521      if (null != m_currentPackages) {
522        m_currentPackage = new XmlPackage();
523        m_currentPackage.setName(name);
524        m_currentPackages.add(m_currentPackage);
525      }
526    }
527    else if ("define".equals(qName)) {
528      xmlDefine(true, attributes);
529    }
530    else if ("run".equals(qName)) {
531      xmlRun(true, attributes);
532    }
533    else if ("groups".equals(qName)) {
534      m_currentIncludedGroups = Lists.newArrayList();
535      m_currentExcludedGroups = Lists.newArrayList();
536    }
537    else if ("methods".equals(qName)) {
538      xmlMethod(true, attributes);
539    }
540    else if ("include".equals(qName)) {
541      if (null != m_currentIncludedMethods) {
542        String in = attributes.getValue("invocation-numbers");
543        if (!Utils.isStringEmpty(in)) {
544          m_currentIncludedMethods.add(new XmlInclude(name, stringToList(in),
545              m_currentIncludeIndex++));
546        } else {
547          m_currentIncludedMethods.add(new XmlInclude(name, m_currentIncludeIndex++));
548        }
549      }
550      else if (null != m_currentDefines) {
551        m_currentMetaGroup.add(name);
552      }
553      else if (null != m_currentRuns) {
554        m_currentIncludedGroups.add(name);
555      }
556      else if (null != m_currentPackage) {
557        m_currentPackage.getInclude().add(name);
558      }
559    }
560    else if ("exclude".equals(qName)) {
561      if (null != m_currentExcludedMethods) {
562        m_currentExcludedMethods.add(name);
563      }
564      else if (null != m_currentRuns) {
565        m_currentExcludedGroups.add(name);
566      }
567      else if (null != m_currentPackage) {
568        m_currentPackage.getExclude().add(name);
569      }
570    }
571    else if ("parameter".equals(qName)) {
572      String value = attributes.getValue("value");
573      if (m_inTest) {
574        m_currentTestParameters.put(name, value);
575      }
576      else {
577        m_currentSuiteParameters.put(name, value);
578      }
579    }
580  }
581
582  private List<Integer> stringToList(String in) {
583    String[] numbers = in.split(" ");
584    List<Integer> result = Lists.newArrayList();
585    for (String n : numbers) {
586      result.add(Integer.parseInt(n));
587    }
588    return result;
589  }
590
591  @Override
592  public void endElement(String uri, String localName, String qName) throws SAXException {
593    if ("suite".equals(qName)) {
594      xmlSuite(false, null);
595    }
596    else if ("suite-file".equals(qName)) {
597      xmlSuiteFile(false, null);
598    }
599    else if ("test".equals(qName)) {
600      xmlTest(false, null);
601    }
602    else if ("define".equals(qName)) {
603      xmlDefine(false, null);
604    }
605    else if ("run".equals(qName)) {
606      xmlRun(false, null);
607    }
608    else if ("methods".equals(qName)) {
609      xmlMethod(false, null);
610    }
611    else if ("classes".equals(qName)) {
612      xmlClasses(false, null);
613    }
614    else if ("classes".equals(qName)) {
615      xmlPackages(false, null);
616    }
617    else if ("listeners".equals(qName)) {
618      xmlListeners(false, null);
619    }
620    else if ("method-selector".equals(qName)) {
621      xmlMethodSelector(false, null);
622    }
623    else if ("method-selectors".equals(qName)) {
624      xmlMethodSelectors(false, null);
625    }
626    else if ("selector-class".equals(qName)) {
627      xmlSelectorClass(false, null);
628    }
629    else if ("script".equals(qName)) {
630      xmlScript(false, null);
631    }
632    else if ("packages".equals(qName)) {
633      xmlPackages(false, null);
634    }
635  }
636
637  @Override
638  public void error(SAXParseException e) throws SAXException {
639    throw e;
640  }
641
642  private boolean areWhiteSpaces(char[] ch, int start, int length) {
643    for (int i = start; i < start + length; i++) {
644      char c = ch[i];
645      if (c != '\n' && c != '\t' && c != ' ') {
646        return false;
647      }
648    }
649
650    return true;
651  }
652
653  @Override
654  public void characters(char ch[], int start, int length) {
655    if (null != m_currentLanguage && ! areWhiteSpaces(ch, start, length)) {
656      m_currentExpression += new String(ch, start, length);
657    }
658  }
659
660  public XmlSuite getSuite() {
661    return m_currentSuite;
662  }
663}