001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2012 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.plugins.surefire.data; 021 022 import java.text.ParseException; 023 import java.util.Locale; 024 025 import javax.xml.stream.XMLStreamException; 026 027 import org.apache.commons.lang.StringUtils; 028 import org.codehaus.staxmate.in.ElementFilter; 029 import org.codehaus.staxmate.in.SMEvent; 030 import org.codehaus.staxmate.in.SMHierarchicCursor; 031 import org.codehaus.staxmate.in.SMInputCursor; 032 import org.sonar.api.utils.ParsingUtils; 033 import org.sonar.api.utils.StaxParser.XmlStreamHandler; 034 035 public class SurefireStaxHandler implements XmlStreamHandler { 036 037 private UnitTestIndex index; 038 039 public SurefireStaxHandler(UnitTestIndex index) { 040 this.index = index; 041 } 042 043 public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException { 044 SMInputCursor testSuite = rootCursor.constructDescendantCursor(new ElementFilter("testsuite")); 045 SMEvent testSuiteEvent; 046 while ((testSuiteEvent = testSuite.getNext()) != null) { 047 if (testSuiteEvent.compareTo(SMEvent.START_ELEMENT) == 0) { 048 String testSuiteClassName = testSuite.getAttrValue("name"); 049 if (StringUtils.contains(testSuiteClassName, "$")) { 050 // test suites for inner classes are ignored 051 return; 052 } 053 SMInputCursor testCase = testSuite.childCursor(new ElementFilter("testcase")); 054 SMEvent event; 055 while ((event = testCase.getNext()) != null) { 056 if (event.compareTo(SMEvent.START_ELEMENT) == 0) { 057 String testClassName = getClassname(testCase, testSuiteClassName); 058 UnitTestClassReport classReport = index.index(testClassName); 059 parseTestCase(testCase, classReport); 060 } 061 } 062 } 063 } 064 } 065 066 private String getClassname(SMInputCursor testCaseCursor, String defaultClassname) throws XMLStreamException { 067 String testClassName = testCaseCursor.getAttrValue("classname"); 068 return StringUtils.defaultIfBlank(testClassName, defaultClassname); 069 } 070 071 private void parseTestCase(SMInputCursor testCaseCursor, UnitTestClassReport report) throws XMLStreamException { 072 report.add(parseTestResult(testCaseCursor)); 073 } 074 075 private void setStackAndMessage(UnitTestResult result, SMInputCursor stackAndMessageCursor) throws XMLStreamException { 076 result.setMessage(stackAndMessageCursor.getAttrValue("message")); 077 String stack = stackAndMessageCursor.collectDescendantText(); 078 result.setStackTrace(stack); 079 } 080 081 private UnitTestResult parseTestResult(SMInputCursor testCaseCursor) throws XMLStreamException { 082 UnitTestResult detail = new UnitTestResult(); 083 String name = getTestCaseName(testCaseCursor); 084 detail.setName(name); 085 086 String status = UnitTestResult.STATUS_OK; 087 long duration = getTimeAttributeInMS(testCaseCursor); 088 089 SMInputCursor childNode = testCaseCursor.descendantElementCursor(); 090 if (childNode.getNext() != null) { 091 String elementName = childNode.getLocalName(); 092 if ("skipped".equals(elementName)) { 093 status = UnitTestResult.STATUS_SKIPPED; 094 // bug with surefire reporting wrong time for skipped tests 095 duration = 0L; 096 097 } else if ("failure".equals(elementName)) { 098 status = UnitTestResult.STATUS_FAILURE; 099 setStackAndMessage(detail, childNode); 100 101 } else if ("error".equals(elementName)) { 102 status = UnitTestResult.STATUS_ERROR; 103 setStackAndMessage(detail, childNode); 104 } 105 } 106 while (childNode.getNext() != null) { 107 // make sure we loop till the end of the elements cursor 108 } 109 detail.setDurationMilliseconds(duration); 110 detail.setStatus(status); 111 return detail; 112 } 113 114 private long getTimeAttributeInMS(SMInputCursor testCaseCursor) throws XMLStreamException { 115 // hardcoded to Locale.ENGLISH see http://jira.codehaus.org/browse/SONAR-602 116 try { 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); 121 } 122 } 123 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.substringAfter(classname, "$") + "/" + name; 129 } 130 return name; 131 } 132 133 }