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.api; 021 022 import org.apache.commons.lang.StringUtils; 023 import org.sonar.api.batch.SensorContext; 024 import org.sonar.api.measures.CoreMetrics; 025 import org.sonar.api.measures.Measure; 026 import org.sonar.api.measures.Metric; 027 import org.sonar.api.resources.Project; 028 import org.sonar.api.resources.Resource; 029 import org.sonar.api.utils.ParsingUtils; 030 import org.sonar.api.utils.SonarException; 031 import org.sonar.api.utils.StaxParser; 032 import org.sonar.plugins.surefire.data.SurefireStaxHandler; 033 import org.sonar.plugins.surefire.data.UnitTestClassReport; 034 import org.sonar.plugins.surefire.data.UnitTestIndex; 035 036 import java.io.File; 037 import java.io.FilenameFilter; 038 import java.util.Map; 039 040 import javax.xml.stream.XMLStreamException; 041 042 /** 043 * @since 2.4 044 */ 045 public abstract class AbstractSurefireParser { 046 047 public void collect(Project project, SensorContext context, File reportsDir) { 048 File[] xmlFiles = getReports(reportsDir); 049 050 if (xmlFiles.length == 0) { 051 // See http://jira.codehaus.org/browse/SONAR-2371 052 if (project.getModules().isEmpty()) { 053 context.saveMeasure(CoreMetrics.TESTS, 0.0); 054 } 055 } else { 056 parseFiles(context, xmlFiles); 057 } 058 } 059 060 private File[] getReports(File dir) { 061 if (dir == null || !dir.isDirectory() || !dir.exists()) { 062 return new File[0]; 063 } 064 return dir.listFiles(new FilenameFilter() { 065 public boolean accept(File dir, String name) { 066 return name.startsWith("TEST") && name.endsWith(".xml"); 067 } 068 }); 069 } 070 071 private void parseFiles(SensorContext context, File[] reports) { 072 UnitTestIndex index = new UnitTestIndex(); 073 parseFiles(reports, index); 074 sanitize(index); 075 save(index, context); 076 } 077 078 private void parseFiles(File[] reports, UnitTestIndex index) { 079 SurefireStaxHandler staxParser = new SurefireStaxHandler(index); 080 StaxParser parser = new StaxParser(staxParser, false); 081 for (File report : reports) { 082 try { 083 parser.parse(report); 084 } catch (XMLStreamException e) { 085 throw new SonarException("Fail to parse the Surefire report: " + report, e); 086 } 087 } 088 } 089 090 private void sanitize(UnitTestIndex index) { 091 for (String classname : index.getClassnames()) { 092 if (StringUtils.contains(classname, "$")) { 093 // Surefire reports classes whereas sonar supports files 094 String parentClassName = StringUtils.substringBefore(classname, "$"); 095 index.merge(classname, parentClassName); 096 } 097 } 098 } 099 100 private void save(UnitTestIndex index, SensorContext context) { 101 for (Map.Entry<String, UnitTestClassReport> entry : index.getIndexByClassname().entrySet()) { 102 UnitTestClassReport report = entry.getValue(); 103 if (report.getTests() > 0) { 104 Resource resource = getUnitTestResource(entry.getKey()); 105 double testsCount = report.getTests() - report.getSkipped(); 106 saveMeasure(context, resource, CoreMetrics.SKIPPED_TESTS, report.getSkipped()); 107 saveMeasure(context, resource, CoreMetrics.TESTS, testsCount); 108 saveMeasure(context, resource, CoreMetrics.TEST_ERRORS, report.getErrors()); 109 saveMeasure(context, resource, CoreMetrics.TEST_FAILURES, report.getFailures()); 110 saveMeasure(context, resource, CoreMetrics.TEST_EXECUTION_TIME, report.getDurationMilliseconds()); 111 double passedTests = testsCount - report.getErrors() - report.getFailures(); 112 if (testsCount > 0) { 113 double percentage = passedTests * 100d / testsCount; 114 saveMeasure(context, resource, CoreMetrics.TEST_SUCCESS_DENSITY, ParsingUtils.scaleValue(percentage)); 115 } 116 saveResults(context, resource, report); 117 } 118 } 119 } 120 121 private void saveMeasure(SensorContext context, Resource resource, Metric metric, double value) { 122 if (!Double.isNaN(value)) { 123 context.saveMeasure(resource, metric, value); 124 } 125 } 126 127 private void saveResults(SensorContext context, Resource resource, UnitTestClassReport report) { 128 context.saveMeasure(resource, new Measure(CoreMetrics.TEST_DATA, report.toXml())); 129 } 130 131 protected abstract Resource<?> getUnitTestResource(String classKey); 132 133 }