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}