2 * Sonar, open source software quality management tool.
3 * Copyright (C) 2008-2011 SonarSource
4 * mailto:contact AT sonarsource DOT com
6 * Sonar is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * Sonar is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with Sonar; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
20 package org.sonar.plugins.surefire.data;
22 import java.text.ParseException;
23 import java.util.Locale;
25 import javax.xml.stream.XMLStreamException;
27 import org.apache.commons.lang.StringUtils;
28 import org.codehaus.staxmate.in.ElementFilter;
29 import org.codehaus.staxmate.in.SMEvent;
30 import org.codehaus.staxmate.in.SMHierarchicCursor;
31 import org.codehaus.staxmate.in.SMInputCursor;
32 import org.sonar.api.utils.ParsingUtils;
33 import org.sonar.api.utils.StaxParser.XmlStreamHandler;
35 public class SurefireStaxHandler implements XmlStreamHandler {
37 private UnitTestIndex index;
39 public SurefireStaxHandler(UnitTestIndex index) {
43 public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException {
44 SMInputCursor testSuite = rootCursor.constructDescendantCursor(new ElementFilter("testsuite"));
45 SMEvent testSuiteEvent;
46 while ((testSuiteEvent = testSuite.getNext()) != null) {
47 if (testSuiteEvent.compareTo(SMEvent.START_ELEMENT) == 0) {
48 String testSuiteClassName = testSuite.getAttrValue("name");
49 if (StringUtils.contains(testSuiteClassName, "$")) {
50 // test suites for inner classes are ignored
53 SMInputCursor testCase = testSuite.childCursor(new ElementFilter("testcase"));
55 while ((event = testCase.getNext()) != null) {
56 if (event.compareTo(SMEvent.START_ELEMENT) == 0) {
57 String testClassName = getClassname(testCase, testSuiteClassName);
58 UnitTestClassReport classReport = index.index(testClassName);
59 parseTestCase(testCase, classReport);
66 private String getClassname(SMInputCursor testCaseCursor, String defaultClassname) throws XMLStreamException {
67 String testClassName = testCaseCursor.getAttrValue("classname");
68 return StringUtils.defaultIfBlank(testClassName, defaultClassname);
71 private void parseTestCase(SMInputCursor testCaseCursor, UnitTestClassReport report) throws XMLStreamException {
72 report.add(parseTestResult(testCaseCursor));
75 private void setStackAndMessage(UnitTestResult result, SMInputCursor stackAndMessageCursor) throws XMLStreamException {
76 result.setMessage(stackAndMessageCursor.getAttrValue("message"));
77 String stack = stackAndMessageCursor.collectDescendantText();
78 result.setStackTrace(stack);
81 private UnitTestResult parseTestResult(SMInputCursor testCaseCursor) throws XMLStreamException {
82 UnitTestResult detail = new UnitTestResult();
83 String name = getTestCaseName(testCaseCursor);
86 String status = UnitTestResult.STATUS_OK;
87 long duration = getTimeAttributeInMS(testCaseCursor);
89 SMInputCursor childNode = testCaseCursor.descendantElementCursor();
90 if (childNode.getNext() != null) {
91 String elementName = childNode.getLocalName();
92 if ("skipped".equals(elementName)) {
93 status = UnitTestResult.STATUS_SKIPPED;
94 // bug with surefire reporting wrong time for skipped tests
97 } else if ("failure".equals(elementName)) {
98 status = UnitTestResult.STATUS_FAILURE;
99 setStackAndMessage(detail, childNode);
101 } else if ("error".equals(elementName)) {
102 status = UnitTestResult.STATUS_ERROR;
103 setStackAndMessage(detail, childNode);
106 while (childNode.getNext() != null) {
107 // make sure we loop till the end of the elements cursor
109 detail.setDurationMilliseconds(duration);
110 detail.setStatus(status);
114 private long getTimeAttributeInMS(SMInputCursor testCaseCursor) throws XMLStreamException {
115 // hardcoded to Locale.ENGLISH see http://jira.codehaus.org/browse/SONAR-602
117 Double time = ParsingUtils.parseNumber(testCaseCursor.getAttrValue("time"), Locale.ENGLISH);
118 return !Double.isNaN(time) ? new Double(ParsingUtils.scaleValue(time * 1000, 3)).longValue() : 0L;
119 } catch (ParseException e) {
120 throw new XMLStreamException(e);
124 private String getTestCaseName(SMInputCursor testCaseCursor) throws XMLStreamException {
125 String classname = testCaseCursor.getAttrValue("classname");
126 String name = testCaseCursor.getAttrValue("name");
127 if (StringUtils.contains(classname, "$")) {
128 return StringUtils.substringAfterLast(classname, "$") + "/" + name;