1 package org.sonar.plugins.surefire.api;
3 import org.apache.commons.lang.StringEscapeUtils;
4 import org.apache.commons.lang.StringUtils;
5 import org.sonar.api.batch.SensorContext;
6 import org.sonar.api.measures.CoreMetrics;
7 import org.sonar.api.measures.Measure;
8 import org.sonar.api.measures.Metric;
9 import org.sonar.api.resources.Project;
10 import org.sonar.api.resources.Resource;
11 import org.sonar.api.utils.ParsingUtils;
12 import org.sonar.api.utils.StaxParser;
13 import org.sonar.api.utils.XmlParserException;
14 import org.sonar.plugins.surefire.TestCaseDetails;
15 import org.sonar.plugins.surefire.TestSuiteParser;
16 import org.sonar.plugins.surefire.TestSuiteReport;
19 import java.io.FilenameFilter;
20 import java.util.HashSet;
21 import java.util.List;
24 import javax.xml.transform.TransformerException;
29 public abstract class AbstractSurefireParser {
31 public void collect(Project project, SensorContext context, File reportsDir) {
32 File[] xmlFiles = getReports(reportsDir);
34 if (xmlFiles.length == 0) {
35 insertZeroWhenNoReports(project, context);
37 parseFiles(context, xmlFiles);
41 private File[] getReports(File dir) {
42 if (dir == null || !dir.isDirectory() || !dir.exists()) {
45 return dir.listFiles(new FilenameFilter() {
46 public boolean accept(File dir, String name) {
47 return name.startsWith("TEST") && name.endsWith(".xml");
52 private void insertZeroWhenNoReports(Project pom, SensorContext context) {
53 if ( !StringUtils.equalsIgnoreCase("pom", pom.getPackaging())) {
54 context.saveMeasure(CoreMetrics.TESTS, 0.0);
58 private void parseFiles(SensorContext context, File[] reports) {
59 Set<TestSuiteReport> analyzedReports = new HashSet<TestSuiteReport>();
61 for (File report : reports) {
62 TestSuiteParser parserHandler = new TestSuiteParser();
63 StaxParser parser = new StaxParser(parserHandler, false);
66 for (TestSuiteReport fileReport : parserHandler.getParsedReports()) {
67 if ( !fileReport.isValid() || analyzedReports.contains(fileReport)) {
70 if (fileReport.getTests() > 0) {
71 double testsCount = fileReport.getTests() - fileReport.getSkipped();
72 saveClassMeasure(context, fileReport, CoreMetrics.SKIPPED_TESTS, fileReport.getSkipped());
73 saveClassMeasure(context, fileReport, CoreMetrics.TESTS, testsCount);
74 saveClassMeasure(context, fileReport, CoreMetrics.TEST_ERRORS, fileReport.getErrors());
75 saveClassMeasure(context, fileReport, CoreMetrics.TEST_FAILURES, fileReport.getFailures());
76 saveClassMeasure(context, fileReport, CoreMetrics.TEST_EXECUTION_TIME, fileReport.getTimeMS());
77 double passedTests = testsCount - fileReport.getErrors() - fileReport.getFailures();
79 double percentage = passedTests * 100d / testsCount;
80 saveClassMeasure(context, fileReport, CoreMetrics.TEST_SUCCESS_DENSITY, ParsingUtils.scaleValue(percentage));
82 saveTestsDetails(context, fileReport);
83 analyzedReports.add(fileReport);
88 } catch (Exception e) {
89 throw new XmlParserException("Can not parse surefire reports", e);
93 private void saveTestsDetails(SensorContext context, TestSuiteReport fileReport) throws TransformerException {
94 StringBuilder testCaseDetails = new StringBuilder(256);
95 testCaseDetails.append("<tests-details>");
96 List<TestCaseDetails> details = fileReport.getDetails();
97 for (TestCaseDetails detail : details) {
98 testCaseDetails.append("<testcase status=\"").append(detail.getStatus())
99 .append("\" time=\"").append(detail.getTimeMS())
100 .append("\" name=\"").append(detail.getName()).append("\"");
101 boolean isError = detail.getStatus().equals(TestCaseDetails.STATUS_ERROR);
102 if (isError || detail.getStatus().equals(TestCaseDetails.STATUS_FAILURE)) {
103 testCaseDetails.append(">")
104 .append(isError ? "<error message=\"" : "<failure message=\"")
105 .append(StringEscapeUtils.escapeXml(detail.getErrorMessage())).append("\">")
106 .append("<![CDATA[").append(StringEscapeUtils.escapeXml(detail.getStackTrace())).append("]]>")
107 .append(isError ? "</error>" : "</failure>").append("</testcase>");
109 testCaseDetails.append("/>");
112 testCaseDetails.append("</tests-details>");
113 context.saveMeasure(getUnitTestResource(fileReport.getClassKey()), new Measure(CoreMetrics.TEST_DATA, testCaseDetails.toString()));
116 private void saveClassMeasure(SensorContext context, TestSuiteReport fileReport, Metric metric, double value) {
117 if ( !Double.isNaN(value)) {
118 context.saveMeasure(getUnitTestResource(fileReport.getClassKey()), metric, value);
122 protected abstract Resource<?> getUnitTestResource(String classKey);