From 63195381a893f6696e492ffa56c1cbdeae6a7e67 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 18 Dec 2014 18:01:57 +0100 Subject: [PATCH] Move Cobertura into the Scala-Plugin --- pom.xml | 7 - .../scala/cobertura/CoberturaSensor.java | 73 +++++---- .../scala/cobertura/ScalaCoberturaParser.java | 43 ------ .../cobertura/ScalaCoberturaReportParser.java | 143 ++++++++++++++++++ 4 files changed, 184 insertions(+), 82 deletions(-) delete mode 100644 src/main/java/org/sonar/plugins/scala/cobertura/ScalaCoberturaParser.java create mode 100644 src/main/java/org/sonar/plugins/scala/cobertura/ScalaCoberturaReportParser.java diff --git a/pom.xml b/pom.xml index 314ca34..d7b0bef 100644 --- a/pom.xml +++ b/pom.xml @@ -45,8 +45,6 @@ 2.10.4 2.10 - - 3.3.2 scala @@ -62,11 +60,6 @@ scala-library ${scala.version} - - org.codehaus.sonar.plugins - sonar-cobertura-plugin - ${cobertura.version} - org.scala-lang scala-compiler diff --git a/src/main/java/org/sonar/plugins/scala/cobertura/CoberturaSensor.java b/src/main/java/org/sonar/plugins/scala/cobertura/CoberturaSensor.java index 58e523a..cf3da99 100644 --- a/src/main/java/org/sonar/plugins/scala/cobertura/CoberturaSensor.java +++ b/src/main/java/org/sonar/plugins/scala/cobertura/CoberturaSensor.java @@ -27,46 +27,55 @@ import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; -import org.sonar.plugins.cobertura.api.AbstractCoberturaParser; -import org.sonar.plugins.cobertura.api.CoberturaUtils; +import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.plugins.scala.language.Scala; public class CoberturaSensor implements Sensor, CoverageExtension { - private static final Logger LOG = LoggerFactory.getLogger(CoberturaSensor.class); - - private final FileSystem fileSystem; - private static final AbstractCoberturaParser COBERTURA_PARSER = new ScalaCoberturaParser(); + private static final Logger LOG = LoggerFactory.getLogger(CoberturaSensor.class); + + public static final String COBERTURA_REPORTS_PATH_PROPERTY ="sonar.cobertura.reportPath"; + private FileSystem fileSystem;; + private PathResolver pathResolver; + private Settings settings; - public CoberturaSensor(FileSystem fileSystem) { - this.fileSystem = fileSystem; - } - - public boolean shouldExecuteOnProject(Project project) { - if(fileSystem.languages().contains(Scala.KEY)){ - LOG.info("CoberturaSensor will be executed"); - return true; - } else { - LOG.info("CoberturaSensor will NOT be executed"); - return false; - } - } - - public void analyse(Project project, SensorContext context) { - File report = CoberturaUtils.getReport(project); - if (report != null) { - parseReport(report, context); - } - } - protected void parseReport(File xmlFile, final SensorContext context) { - LOG.info("parsing {}", xmlFile); - COBERTURA_PARSER.parseReport(xmlFile, context); + public CoberturaSensor(FileSystem fileSystem, PathResolver pathResolver, Settings settings) { + this.pathResolver = pathResolver; + this.settings = settings; + this.fileSystem = fileSystem; + } + + public boolean shouldExecuteOnProject(Project project) { + if(fileSystem.languages().contains(Scala.KEY)){ + LOG.info("CoberturaSensor will be executed"); + return true; + } else { + LOG.info("CoberturaSensor will NOT be executed"); + return false; } + } - @Override - public String toString() { - return "Scala CoberturaSensor"; + public void analyse(Project project, SensorContext context) { + String path = settings.getString(COBERTURA_REPORTS_PATH_PROPERTY); + File report = pathResolver.relativeFile(fileSystem.baseDir(), path); + if (!report.isFile()) { + LOG.warn("Cobertura report not found at {}", report); + return; } + parseReport(report, context); + } + + protected void parseReport(File xmlFile, SensorContext context) { + LOG.info("parsing {}", xmlFile); + ScalaCoberturaReportParser.parseReport(xmlFile, context, fileSystem); + } + + @Override + public String toString() { + return "Scala CoberturaSensor"; + } + } diff --git a/src/main/java/org/sonar/plugins/scala/cobertura/ScalaCoberturaParser.java b/src/main/java/org/sonar/plugins/scala/cobertura/ScalaCoberturaParser.java deleted file mode 100644 index 7f1212d..0000000 --- a/src/main/java/org/sonar/plugins/scala/cobertura/ScalaCoberturaParser.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Sonar Scala Plugin - * Copyright (C) 2011 - 2014 All contributors - * dev@sonar.codehaus.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.plugins.scala.cobertura; - -import org.sonar.api.resources.Resource; -import org.sonar.plugins.cobertura.api.AbstractCoberturaParser; -import org.sonar.plugins.scala.language.ScalaFile; - -public class ScalaCoberturaParser extends AbstractCoberturaParser { - @Override - protected Resource getResource(String fileName) { - // TODO update the sbt scct plugin to provide the correct fully qualified class name. - if (fileName.startsWith("src.main.scala.")) - fileName = fileName.replace("src.main.scala.", ""); - else if (fileName.startsWith("app.")) - fileName = fileName.replace("app.", ""); - int packageTerminator = fileName.lastIndexOf('.'); - if (packageTerminator < 0) { - return new ScalaFile(null, fileName, false); - } else { - String packageName = fileName.substring(0, packageTerminator); - String className = fileName.substring(packageTerminator + 1, fileName.length()); - return new ScalaFile(packageName, className, false); - } - } -} diff --git a/src/main/java/org/sonar/plugins/scala/cobertura/ScalaCoberturaReportParser.java b/src/main/java/org/sonar/plugins/scala/cobertura/ScalaCoberturaReportParser.java new file mode 100644 index 0000000..54d2b01 --- /dev/null +++ b/src/main/java/org/sonar/plugins/scala/cobertura/ScalaCoberturaReportParser.java @@ -0,0 +1,143 @@ +/* + * Sonar Scala Plugin + * Copyright (C) 2011 - 2014 All contributors + * dev@sonar.codehaus.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.scala.cobertura; + +import static java.util.Locale.ENGLISH; +import static org.sonar.api.utils.ParsingUtils.parseNumber; + +import java.io.File; +import java.text.ParseException; +import java.util.Map; + +import javax.xml.stream.XMLStreamException; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.StringUtils; +import org.codehaus.staxmate.in.SMHierarchicCursor; +import org.codehaus.staxmate.in.SMInputCursor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FilePredicates; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.measures.CoverageMeasuresBuilder; +import org.sonar.api.measures.Measure; +import org.sonar.api.utils.StaxParser; +import org.sonar.api.utils.XmlParserException; + +import com.google.common.collect.Maps; + +public class ScalaCoberturaReportParser { + + private static final Logger LOG = LoggerFactory.getLogger(ScalaCoberturaReportParser.class); + private final SensorContext context; + private FileSystem fileSystem; + + private ScalaCoberturaReportParser(SensorContext context, FileSystem fileSystem) { + this.context = context; + this.fileSystem = fileSystem; + } + + /** + * Parse a Cobertura xml report and create measures accordingly + * @param fileSystem + */ + public static void parseReport(File xmlFile, SensorContext context, FileSystem fileSystem) { + new ScalaCoberturaReportParser(context, fileSystem).parse(xmlFile); + } + + private void parse(File xmlFile) { + try { + StaxParser parser = new StaxParser(new StaxParser.XmlStreamHandler() { + + public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException { + rootCursor.advance(); + collectPackageMeasures(rootCursor.descendantElementCursor("package")); + } + }); + parser.parse(xmlFile); + } catch (XMLStreamException e) { + throw new XmlParserException(e); + } + } + + private void collectPackageMeasures(SMInputCursor pack) throws XMLStreamException { + while (pack.getNext() != null) { + Map builderByFilename = Maps.newHashMap(); + collectFileMeasures(pack.descendantElementCursor("class"), builderByFilename); + for (Map.Entry entry : builderByFilename.entrySet()) { + String className = sanitizeFilename(entry.getKey()); + String filename = className.replace('.', '/') + ".scala"; + FilePredicates filePredicates = fileSystem.predicates(); + InputFile resource = fileSystem.inputFile(filePredicates.matchesPathPattern("**/*" + filename)); + if (resource != null){ + for (Measure measure : entry.getValue().createMeasures()) { + context.saveMeasure(resource, measure); + } + }else{ + LOG.warn("Resource not found: {}", entry.getKey()); + } + } + } + } + +// private boolean resourceExists(Resource file) { +// return context.getResource(file) != null; +// } + + private void collectFileMeasures(SMInputCursor clazz, Map builderByFilename) throws XMLStreamException { + while (clazz.getNext() != null) { + String fileName = clazz.getAttrValue("filename"); + CoverageMeasuresBuilder builder = builderByFilename.get(fileName); + if (builder == null) { + builder = CoverageMeasuresBuilder.create(); + builderByFilename.put(fileName, builder); + } + collectFileData(clazz, builder); + } + } + + private void collectFileData(SMInputCursor clazz, CoverageMeasuresBuilder builder) throws XMLStreamException { + SMInputCursor line = clazz.childElementCursor("lines").advance().childElementCursor("line"); + while (line.getNext() != null) { + int lineId = Integer.parseInt(line.getAttrValue("number")); + try { + builder.setHits(lineId, (int) parseNumber(line.getAttrValue("hits"), ENGLISH)); + } catch (ParseException e) { + throw new XmlParserException(e); + } + + String isBranch = line.getAttrValue("branch"); + String text = line.getAttrValue("condition-coverage"); + if (StringUtils.equals(isBranch, "true") && StringUtils.isNotBlank(text)) { + String[] conditions = StringUtils.split(StringUtils.substringBetween(text, "(", ")"), "/"); + builder.setConditions(lineId, Integer.parseInt(conditions[1]), Integer.parseInt(conditions[0])); + } + } + } + + private static String sanitizeFilename(String s) { + String fileName = FilenameUtils.removeExtension(s); + fileName = fileName.replace('/', '.').replace('\\', '.'); + return fileName; + } + +}