diff options
author | Evgeny Mandrikov <mandrikov@gmail.com> | 2011-10-14 01:05:25 +0400 |
---|---|---|
committer | Evgeny Mandrikov <mandrikov@gmail.com> | 2011-10-14 01:48:01 +0400 |
commit | a887c65cf1b03d210cea3cf98dc81e6a509ef572 (patch) | |
tree | f33b8087c740069c0743694f4c928777ac7b85c1 | |
parent | cbccf1ebdf3ac39233f3e75ad8bd78968c240a81 (diff) | |
download | sonarqube-a887c65cf1b03d210cea3cf98dc81e6a509ef572.tar.gz sonarqube-a887c65cf1b03d210cea3cf98dc81e6a509ef572.zip |
SONAR-2792 Import code of JaCoCo plugin from Plugins Forge
Revision 4564.
39 files changed, 2551 insertions, 0 deletions
diff --git a/plugins/sonar-jacoco-plugin/pom.xml b/plugins/sonar-jacoco-plugin/pom.xml new file mode 100644 index 00000000000..ba44b839d41 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/pom.xml @@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar</artifactId> + <version>2.12-SNAPSHOT</version> + <relativePath>../..</relativePath> + </parent> + + <groupId>org.codehaus.sonar.plugins</groupId> + <artifactId>sonar-jacoco-plugin</artifactId> + <packaging>sonar-plugin</packaging> + + <name>Sonar :: Plugins :: JaCoCo</name> + <description>JaCoCo is an alternative to Clover and Cobertura to measure coverage by unit tests.</description> + <url>http://docs.codehaus.org/display/SONAR/JaCoCo+Plugin</url> + <inceptionYear>2010</inceptionYear> + <organization> + <name>SonarSource</name> + <url>http://www.sonarsource.com</url> + </organization> + <licenses> + <license> + <name>GNU LGPL 3</name> + <url>http://www.gnu.org/licenses/lgpl.txt</url> + <distribution>repo</distribution> + </license> + </licenses> + + <developers> + <developer> + <id>godin</id> + <name>Evgeny Mandrikov</name> + </developer> + </developers> + + <properties> + <jacoco.version>0.5.3.201107060350</jacoco.version> + + <sonar.pluginName>JaCoCo</sonar.pluginName> + <sonar.pluginClass>org.sonar.plugins.jacoco.JaCoCoPlugin</sonar.pluginClass> + </properties> + + <dependencies> + <dependency> + <groupId>org.jacoco</groupId> + <artifactId>org.jacoco.core</artifactId> + <version>${jacoco.version}</version> + </dependency> + <dependency> + <groupId>org.jacoco</groupId> + <artifactId>org.jacoco.agent</artifactId> + <version>${jacoco.version}</version> + </dependency> + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-plugin-api</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-gwt-api</artifactId> + <scope>provided</scope> + </dependency> + <!-- Would be provided by environment --> + <dependency> + <groupId>org.apache.ant</groupId> + <artifactId>ant</artifactId> + <version>1.7.0</version> + <scope>provided</scope> + </dependency> + <!-- unit tests --> + <dependency> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-testing-harness</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-project</artifactId> + <version>2.0.7</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- TODO Godin: investigate why doesn't work without declaration of this plugin here --> + <plugin> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-packaging-maven-plugin</artifactId> + <extensions>true</extensions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>gwt-maven-plugin</artifactId> + <executions> + <execution> + <configuration> + <modules> + <module>org.sonar.plugins.jacoco.itcoverage.viewer.CoverageViewer</module> + </modules> + <skip>${skipGwt}</skip> + <webappDirectory>${project.build.directory}/classes</webappDirectory> + + <!-- do not break on two lines --> + <extraJvmArgs>${extraJvmArgs}</extraJvmArgs> + </configuration> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.sonar</groupId> + <artifactId>sonar-dev-maven-plugin</artifactId> + <executions> + <execution> + <id>trim</id> + <phase>process-resources</phase> + <goals> + <goal>trim</goal> + </goals> + <configuration> + <directory>${project.build.outputDirectory}</directory> + <includes> + <include>**/*.erb</include> + </includes> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/AbstractAnalyzer.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/AbstractAnalyzer.java new file mode 100644 index 00000000000..2b226f2e69c --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/AbstractAnalyzer.java @@ -0,0 +1,150 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import org.apache.commons.lang.StringUtils; +import org.jacoco.core.analysis.*; +import org.jacoco.core.data.ExecutionDataReader; +import org.jacoco.core.data.ExecutionDataStore; +import org.jacoco.core.data.SessionInfoStore; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.measures.CoverageMeasuresBuilder; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.JavaFile; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.SonarException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Collection; + +/** + * @author Evgeny Mandrikov + */ +public abstract class AbstractAnalyzer { + + public final void analyse(Project project, SensorContext context) { + final File buildOutputDir = project.getFileSystem().getBuildOutputDir(); + if (!buildOutputDir.exists()) { + JaCoCoUtils.LOG.info("Can't find build output directory: {}. Skipping JaCoCo analysis.", buildOutputDir); + return; + } + String path = getReportPath(project); + File jacocoExecutionData = project.getFileSystem().resolvePath(path); + try { + readExecutionData(jacocoExecutionData, buildOutputDir, context); + } catch (IOException e) { + throw new SonarException(e); + } + } + + public final void readExecutionData(File jacocoExecutionData, File buildOutputDir, SensorContext context) throws IOException { + SessionInfoStore sessionInfoStore = new SessionInfoStore(); + ExecutionDataStore executionDataStore = new ExecutionDataStore(); + + if (jacocoExecutionData == null || !jacocoExecutionData.exists() || !jacocoExecutionData.isFile()) { + JaCoCoUtils.LOG.info("Can't find JaCoCo execution data : {}. Project coverage is set to 0%.", jacocoExecutionData); + } else { + JaCoCoUtils.LOG.info("Analysing {}", jacocoExecutionData); + ExecutionDataReader reader = new ExecutionDataReader(new FileInputStream(jacocoExecutionData)); + reader.setSessionInfoVisitor(sessionInfoStore); + reader.setExecutionDataVisitor(executionDataStore); + reader.read(); + } + + CoverageBuilder coverageBuilder = new CoverageBuilder(); + Analyzer analyzer = new Analyzer(executionDataStore, coverageBuilder); + analyzeAll(analyzer, buildOutputDir); + + int analyzedResources = 0; + for (ISourceFileCoverage coverage : coverageBuilder.getSourceFiles()) { + JavaFile resource = getResource(coverage); + // Do not save measures on resource which doesn't exist in the context + if (context.getResource(resource) != null) { + analyzeFile(resource, coverage, context); + analyzedResources++; + } + } + if (analyzedResources == 0) { + JaCoCoUtils.LOG.warn("Coverage information was not collected. Perhaps you forget to include debug information into compiled classes?"); + } + } + + static JavaFile getResource(ISourceFileCoverage coverage) { + String packageName = StringUtils.replaceChars(coverage.getPackageName(), '/', '.'); + String fileName = StringUtils.substringBeforeLast(coverage.getName(), "."); + return new JavaFile(packageName, fileName); + } + + /** + * Copied from {@link Analyzer#analyzeAll(File)} in order to add logging. + */ + private void analyzeAll(Analyzer analyzer, File file) { + if (file.isDirectory()) { + for (File f : file.listFiles()) { + analyzeAll(analyzer, f); + } + } else { + try { + analyzer.analyzeAll(file); + } catch (Exception e) { + JaCoCoUtils.LOG.warn("Exception during analysis of file " + file.getAbsolutePath(), e); + } + } + } + + private void analyzeFile(JavaFile resource, ISourceFileCoverage coverage, SensorContext context) { + CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create(); + for (int lineId = coverage.getFirstLine(); lineId <= coverage.getLastLine(); lineId++) { + final int hits; + ILine line = coverage.getLine(lineId); + switch (line.getInstructionCounter().getStatus()) { + case ICounter.FULLY_COVERED: + case ICounter.PARTLY_COVERED: + hits = 1; + break; + case ICounter.NOT_COVERED: + hits = 0; + break; + case ICounter.EMPTY: + continue; + default: + JaCoCoUtils.LOG.warn("Unknown status for line {} in {}", lineId, resource); + continue; + } + builder.setHits(lineId, hits); + + ICounter branchCounter = line.getBranchCounter(); + int conditions = branchCounter.getTotalCount(); + if (conditions > 0) { + int coveredConditions = branchCounter.getCoveredCount(); + builder.setConditions(lineId, conditions, coveredConditions); + } + } + + saveMeasures(context, resource, builder.createMeasures()); + } + + protected abstract void saveMeasures(SensorContext context, JavaFile resource, Collection<Measure> measures); + + protected abstract String getReportPath(Project project); + +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoAgentDownloader.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoAgentDownloader.java new file mode 100644 index 00000000000..30589df32bb --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoAgentDownloader.java @@ -0,0 +1,62 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import org.apache.commons.io.FileUtils; +import org.jacoco.agent.AgentJar; +import org.jacoco.core.JaCoCo; +import org.sonar.api.BatchExtension; +import org.sonar.api.utils.SonarException; + +import java.io.File; +import java.io.IOException; + +/** + * @author Evgeny Mandrikov + */ +public class JaCoCoAgentDownloader implements BatchExtension { + + /** + * Dirty hack, but it allows to extract agent only once during Sonar analyzes for multi-module project. + */ + private static File agentJarFile; + + public JaCoCoAgentDownloader() { + } + + public synchronized File getAgentJarFile() { + if (agentJarFile == null) { + agentJarFile = extractAgent(); + } + return agentJarFile; + } + + private File extractAgent() { + try { + File agent = File.createTempFile("jacocoagent", ".jar"); + AgentJar.extractTo(agent); + FileUtils.forceDeleteOnExit(agent); // TODO evil method + JaCoCoUtils.LOG.info("JaCoCo agent (version " + JaCoCo.VERSION + ") extracted: {}", agent); + return agent; + } catch (IOException e) { + throw new SonarException(e); + } + } +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandler.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandler.java new file mode 100644 index 00000000000..522dd14539a --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandler.java @@ -0,0 +1,93 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import java.io.File; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.batch.maven.MavenPlugin; +import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.batch.maven.MavenSurefireUtils; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.SonarException; + +/** + * @author Evgeny Mandrikov + */ +public class JaCoCoMavenPluginHandler implements MavenPluginHandler { + + private static final String ARG_LINE_PARAMETER = "argLine"; + private static final String TEST_FAILURE_IGNORE_PARAMETER = "testFailureIgnore"; + + private final String groupId; + private final String artifactId; + private final String version; + + private JacocoConfiguration configuration; + + public JaCoCoMavenPluginHandler(JacocoConfiguration configuration) { + this.configuration = configuration; + groupId = MavenSurefireUtils.GROUP_ID; + artifactId = MavenSurefireUtils.ARTIFACT_ID; + version = MavenSurefireUtils.VERSION; + } + + public String getGroupId() { + return groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public String getVersion() { + return version; + } + + public boolean isFixedVersion() { + return false; + } + + public String[] getGoals() { + return new String[] { "test" }; + } + + public void configure(Project project, MavenPlugin plugin) { + // See SONARPLUGINS-600 + String destfilePath = configuration.getReportPath(); + File destfile = project.getFileSystem().resolvePath(destfilePath); + if (destfile.exists() && destfile.isFile()) { + JaCoCoUtils.LOG.info("Deleting {}", destfile); + if (!destfile.delete()) { + throw new SonarException("Unable to delete " + destfile); + } + } + + String argument = configuration.getJvmArgument(); + + String argLine = plugin.getParameter(ARG_LINE_PARAMETER); + argLine = StringUtils.isBlank(argLine) ? argument : argument + " " + argLine; + JaCoCoUtils.LOG.info("JVM options: {}", argLine); + plugin.setParameter(ARG_LINE_PARAMETER, argLine); + + plugin.setParameter(TEST_FAILURE_IGNORE_PARAMETER, "true"); + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoPlugin.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoPlugin.java new file mode 100644 index 00000000000..811efa13f8a --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoPlugin.java @@ -0,0 +1,132 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import java.util.Arrays; +import java.util.List; + +import org.sonar.api.Plugin; +import org.sonar.api.Properties; +import org.sonar.api.Property; +import org.sonar.plugins.jacoco.itcoverage.*; +import org.sonar.plugins.jacoco.itcoverage.viewer.CoverageViewerDefinition; + +@Properties({ + @Property( + key = JacocoConfiguration.REPORT_PATH_PROPERTY, + name = "File with execution data", + defaultValue = JacocoConfiguration.REPORT_PATH_DEFAULT_VALUE, + description = "Path (absolute or relative) to the file with execution data.", + global = false, + module = true, + project = true + ), + @Property( + key = JacocoConfiguration.INCLUDES_PROPERTY, + name = "Includes", + description = "A list of class names that should be included in execution analysis." + + " The list entries are separated by a colon (:) and may use wildcard characters (* and ?)." + + " Except for performance optimization or technical corner cases this option is normally not required.", + global = true, + project = true, + module = true + ), + @Property( + key = JacocoConfiguration.EXCLUDES_PROPERTY, + name = "Excludes", + description = "A list of class names that should be excluded from execution analysis." + + " The list entries are separated by a colon (:) and may use wildcard characters (* and ?)." + + " Except for performance optimization or technical corner cases this option is normally not required.", + global = true, + project = true, + module = true + ), + @Property( + key = JacocoConfiguration.EXCLCLASSLOADER_PROPERTY, + name = "Excluded class loaders", + description = "A list of class loader names that should be excluded from execution analysis." + + " The list entries are separated by a colon (:) and may use wildcard characters (* and ?)." + + " This option might be required in case of special frameworks that conflict with JaCoCo code" + + " instrumentation, in particular class loaders that do not have access to the Java runtime classes.", + global = true, + project = true, + module = true + ), + @Property( + key = JacocoConfiguration.IT_REPORT_PATH_PROPERTY, + name = "File with execution data for integration tests", + defaultValue = JacocoConfiguration.IT_REPORT_PATH_DEFAULT_VALUE, + description = "Path (absolute or relative) to the file with execution data.", + global = false, + module = true, + project = true + ), + @Property( + key = JacocoConfiguration.ANT_TARGETS_PROPERTY, + name = "Ant targets", + defaultValue = JacocoConfiguration.ANT_TARGETS_DEFAULT_VALUE, + description = "Comma separated list of Ant targets for execution of tests.", + global = true, + module = true, + project = true + ) }) +public class JaCoCoPlugin implements Plugin { + + public String getKey() { + return "jacoco"; + } + + public String getName() { + return "JaCoCo"; + } + + public String getDescription() { + return "<a href='http://www.eclemma.org/jacoco/'>JaCoCo</a> calculates coverage of unit tests." + + " Set the parameter 'Code coverage plugin' to <code>jacoco</code> in the General plugin."; + } + + public List getExtensions() { + return Arrays.asList( + JacocoConfiguration.class, + JaCoCoAgentDownloader.class, + // Ant + JacocoAntInitializer.class, + // Maven + JacocoMavenInitializer.class, + JaCoCoMavenPluginHandler.class, + // Unit tests + JaCoCoSensor.class, + + // Integration tests + JaCoCoItMetrics.class, + JaCoCoItSensor.class, + ItCoverageWidget.class, + ItCoverageDecorator.class, + ItLineCoverageDecorator.class, + ItBranchCoverageDecorator.class, + CoverageViewerDefinition.class); + } + + @Override + public String toString() { + return getKey(); + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoSensor.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoSensor.java new file mode 100644 index 00000000000..6c2f703e3b0 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoSensor.java @@ -0,0 +1,70 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import org.sonar.api.batch.CoverageExtension; +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.JavaFile; +import org.sonar.api.resources.Project; + +import java.util.Collection; + +/** + * @author Evgeny Mandrikov + */ +public class JaCoCoSensor implements Sensor, CoverageExtension { + + private JacocoConfiguration configuration; + + public JaCoCoSensor(JacocoConfiguration configuration) { + this.configuration = configuration; + } + + public void analyse(Project project, SensorContext context) { + new UnitTestsAnalyzer().analyse(project, context); + } + + public boolean shouldExecuteOnProject(Project project) { + return Java.KEY.equals(project.getLanguageKey()); + } + + class UnitTestsAnalyzer extends AbstractAnalyzer { + @Override + protected String getReportPath(Project project) { + return configuration.getReportPath(); + } + + @Override + protected void saveMeasures(SensorContext context, JavaFile resource, Collection<Measure> measures) { + for (Measure measure : measures) { + context.saveMeasure(resource, measure); + } + } + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoUtils.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoUtils.java new file mode 100644 index 00000000000..54433ca2bf4 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JaCoCoUtils.java @@ -0,0 +1,38 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Evgeny Mandrikov + */ +public final class JaCoCoUtils { + + /** + * Utility class constructor. + */ + private JaCoCoUtils() { + } + + public static final Logger LOG = LoggerFactory.getLogger(JaCoCoPlugin.class.getName()); + +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JacocoAntInitializer.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JacocoAntInitializer.java new file mode 100644 index 00000000000..e74e2a7a3c8 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JacocoAntInitializer.java @@ -0,0 +1,144 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import org.apache.tools.ant.*; +import org.sonar.api.batch.CoverageExtension; +import org.sonar.api.batch.Initializer; +import org.sonar.api.batch.SupportedEnvironment; +import org.sonar.api.resources.Project; + +import java.util.Hashtable; + +@SupportedEnvironment("ant") +public class JacocoAntInitializer extends Initializer implements CoverageExtension { + + private final TaskEnhancer[] taskEnhancers = new TaskEnhancer[] { new JavaLikeTaskEnhancer("java"), new JavaLikeTaskEnhancer("junit"), new TestngTaskEnhancer() }; + + private org.apache.tools.ant.Project antProject; + private JacocoConfiguration configuration; + + public JacocoAntInitializer(org.apache.tools.ant.Project antProject, JacocoConfiguration configuration) { + this.antProject = antProject; + this.configuration = configuration; + } + + @Override + public boolean shouldExecuteOnProject(org.sonar.api.resources.Project project) { + return project.getAnalysisType().equals(Project.AnalysisType.DYNAMIC); + // TODO return project.getAnalysisType().equals(Project.AnalysisType.DYNAMIC) && project.getFileSystem().hasTestFiles(Java.INSTANCE); + } + + @Override + public void execute(org.sonar.api.resources.Project project) { + Hashtable<String, Target> hastable = antProject.getTargets(); + String jvmArg = configuration.getJvmArgument(); + String[] names = configuration.getAntTargets(); + for (String name : names) { + Target target = hastable.get(name); + if (target == null) { + JaCoCoUtils.LOG.warn("Target '{}' not found", name); + } else { + // Enhance target + for (Task task : target.getTasks()) { + for (TaskEnhancer enhancer : taskEnhancers) { + if (enhancer.supportsTask(task)) { + enhancer.enhanceTask(task, jvmArg); + } + } + } + // Execute target + // TODO antProject.getExecutor().executeTargets(antProject, new String[] { "test" }); + target.performTasks(); + } + } + } + + private static class TestngTaskEnhancer extends TaskEnhancer { + @Override + public boolean supportsTask(Task task) { + return "testng".equals(task.getTaskName()); + } + } + + /** + * Basic task enhancer that can handle all 'java like' tasks. That is, tasks + * that have a top level fork attribute and nested jvmargs elements + */ + private static class JavaLikeTaskEnhancer extends TaskEnhancer { + private String taskName; + + public JavaLikeTaskEnhancer(String taskName) { + this.taskName = taskName; + } + + public boolean supportsTask(final Task task) { + return taskName.equals(task.getTaskName()); + } + + @Override + public void enhanceTask(final Task task, final String jvmArg) { + final RuntimeConfigurable configurableWrapper = task.getRuntimeConfigurableWrapper(); + + final String forkValue = (String) configurableWrapper.getAttributeMap().get("fork"); + + if (forkValue == null || !org.apache.tools.ant.Project.toBoolean(forkValue)) { + throw new BuildException("Coverage can only be applied on a forked VM"); + } + + super.enhanceTask(task, jvmArg); + } + + } + + private static abstract class TaskEnhancer { + /** + * @param task Task instance to enhance + * @return <code>true</code> if this enhancer is capable of enhancing the requested task + */ + public abstract boolean supportsTask(Task task); + + /** + * Attempt to enhance the supplied task with coverage information. This + * operation may fail if the task is being executed in the current VM + * + * @param task Task instance to enhance (usually an {@link UnknownElement}) + * @param jvmArg + * @throws BuildException Thrown if this enhancer can handle this type of task, but this instance can not be enhanced for some reason. + */ + public void enhanceTask(Task task, String jvmArg) { + addJvmArg((UnknownElement) task, jvmArg); + } + + public void addJvmArg(final UnknownElement task, final String jvmArg) { + final UnknownElement el = new UnknownElement("jvmarg"); + el.setTaskName("jvmarg"); + el.setQName("jvmarg"); + + final RuntimeConfigurable runtimeConfigurableWrapper = el.getRuntimeConfigurableWrapper(); + runtimeConfigurableWrapper.setAttribute("value", jvmArg); + + task.getRuntimeConfigurableWrapper().addChild(runtimeConfigurableWrapper); + + task.addChild(el); + } + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JacocoConfiguration.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JacocoConfiguration.java new file mode 100644 index 00000000000..373c7e9b853 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JacocoConfiguration.java @@ -0,0 +1,76 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.jacoco.core.runtime.AgentOptions; +import org.sonar.api.BatchExtension; + +public class JacocoConfiguration implements BatchExtension { + + public static final String REPORT_PATH_PROPERTY = "sonar.jacoco.reportPath"; + public static final String REPORT_PATH_DEFAULT_VALUE = "target/jacoco.exec"; + public static final String IT_REPORT_PATH_PROPERTY = "sonar.jacoco.itReportPath"; + public static final String IT_REPORT_PATH_DEFAULT_VALUE = ""; + public static final String INCLUDES_PROPERTY = "sonar.jacoco.includes"; + public static final String EXCLUDES_PROPERTY = "sonar.jacoco.excludes"; + public static final String EXCLCLASSLOADER_PROPERTY = "sonar.jacoco.exclclassloader"; + public static final String ANT_TARGETS_PROPERTY = "sonar.jacoco.antTargets"; + public static final String ANT_TARGETS_DEFAULT_VALUE = ""; + + private Configuration configuration; + private JaCoCoAgentDownloader downloader; + + public JacocoConfiguration(Configuration configuration, JaCoCoAgentDownloader downloader) { + this.configuration = configuration; + this.downloader = downloader; + } + + public String getReportPath() { + return configuration.getString(REPORT_PATH_PROPERTY, REPORT_PATH_DEFAULT_VALUE); + } + + public String getItReportPath() { + return configuration.getString(IT_REPORT_PATH_PROPERTY, IT_REPORT_PATH_DEFAULT_VALUE); + } + + public String getJvmArgument() { + AgentOptions options = new AgentOptions(); + options.setDestfile(getReportPath()); + String includes = configuration.getString(INCLUDES_PROPERTY); + if (StringUtils.isNotBlank(includes)) { + options.setIncludes(includes); + } + String excludes = configuration.getString(EXCLUDES_PROPERTY); + if (StringUtils.isNotBlank(excludes)) { + options.setExcludes(excludes); + } + String exclclassloader = configuration.getString(EXCLCLASSLOADER_PROPERTY); + if (StringUtils.isNotBlank(exclclassloader)) { + options.setExclClassloader(exclclassloader); + } + return options.getVMArgument(downloader.getAgentJarFile()); + } + + public String[] getAntTargets() { + return configuration.getStringArray(ANT_TARGETS_PROPERTY); + } +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JacocoMavenInitializer.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JacocoMavenInitializer.java new file mode 100644 index 00000000000..07ed54e05fc --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/JacocoMavenInitializer.java @@ -0,0 +1,53 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import org.sonar.api.batch.CoverageExtension; +import org.sonar.api.batch.Initializer; +import org.sonar.api.batch.SupportedEnvironment; +import org.sonar.api.batch.maven.DependsUponMavenPlugin; +import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Project; + +@SupportedEnvironment("maven") +public class JacocoMavenInitializer extends Initializer implements CoverageExtension, DependsUponMavenPlugin { + + private JaCoCoMavenPluginHandler handler; + + public JacocoMavenInitializer(JaCoCoMavenPluginHandler handler) { + this.handler = handler; + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return project.getAnalysisType().equals(Project.AnalysisType.DYNAMIC) && project.getFileSystem().hasTestFiles(Java.INSTANCE); + } + + @Override + public void execute(Project project) { + // nothing to do + } + + public MavenPluginHandler getMavenPluginHandler(Project project) { + return handler; + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/AbstractCoverageDecorator.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/AbstractCoverageDecorator.java new file mode 100644 index 00000000000..71f5364dab1 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/AbstractCoverageDecorator.java @@ -0,0 +1,72 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage; + +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; + +/** + * Copied from org.sonar.plugins.core.sensors.AbstractCoverageDecorator + */ +public abstract class AbstractCoverageDecorator implements Decorator { + + public boolean shouldExecuteOnProject(Project project) { + return project.getAnalysisType().isDynamic(true); + } + + @DependedUpon + public Metric generatesCoverage() { + return getTargetMetric(); + } + + public void decorate(final Resource resource, final DecoratorContext context) { + if (shouldDecorate(resource, context)) { + saveCoverage(context); + } + } + + protected boolean shouldDecorate(final Resource resource, final DecoratorContext context) { + return context.getMeasure(getTargetMetric()) == null && !ResourceUtils.isUnitTestClass(resource); + } + + private void saveCoverage(DecoratorContext context) { + Double elements = countElements(context); + Double coveredElements = countCoveredElements(context); + + if (elements != null && elements > 0.0 && coveredElements != null) { + context.saveMeasure(getTargetMetric(), calculateCoverage(coveredElements, elements)); + } + } + + private double calculateCoverage(final Double coveredElements, final Double elements) { + return (100.0 * coveredElements) / elements; + } + + protected abstract Metric getTargetMetric(); + + protected abstract Double countCoveredElements(DecoratorContext context); + + protected abstract Double countElements(DecoratorContext context); +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItBranchCoverageDecorator.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItBranchCoverageDecorator.java new file mode 100644 index 00000000000..d3a4147806a --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItBranchCoverageDecorator.java @@ -0,0 +1,55 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage; + +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; + +import java.util.Arrays; +import java.util.List; + +/** + * Copied from org.sonar.plugins.core.sensors.BranchCoverageDecorator + */ +public final class ItBranchCoverageDecorator extends AbstractCoverageDecorator { + @Override + protected Metric getTargetMetric() { + return JaCoCoItMetrics.IT_BRANCH_COVERAGE; + } + + @DependsUpon + public List<Metric> dependsUponMetrics() { + return Arrays.asList(JaCoCoItMetrics.IT_UNCOVERED_CONDITIONS, JaCoCoItMetrics.IT_CONDITIONS_TO_COVER); + } + + @Override + protected Double countCoveredElements(DecoratorContext context) { + double uncoveredConditions = MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_UNCOVERED_CONDITIONS), 0.0); + double conditions = MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_CONDITIONS_TO_COVER), 0.0); + return conditions - uncoveredConditions; + } + + @Override + protected Double countElements(DecoratorContext context) { + return MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_CONDITIONS_TO_COVER), 0.0); + } +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItCoverageDecorator.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItCoverageDecorator.java new file mode 100644 index 00000000000..49e02905895 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItCoverageDecorator.java @@ -0,0 +1,61 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage; + +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; + +import java.util.Arrays; +import java.util.List; + +/** + * Copied from org.sonar.plugins.core.sensors.CoverageDecorator + */ +public class ItCoverageDecorator extends AbstractCoverageDecorator { + + @Override + protected Metric getTargetMetric() { + return JaCoCoItMetrics.IT_COVERAGE; + } + + @DependsUpon + public List<Metric> dependsUponMetrics() { + return Arrays.asList(JaCoCoItMetrics.IT_LINES_TO_COVER, JaCoCoItMetrics.IT_UNCOVERED_LINES, JaCoCoItMetrics.IT_CONDITIONS_TO_COVER, JaCoCoItMetrics.IT_UNCOVERED_CONDITIONS); + } + + @Override + protected Double countCoveredElements(DecoratorContext context) { + double uncoveredLines = MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_UNCOVERED_LINES), 0.0); + double lines = MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_LINES_TO_COVER), 0.0); + double uncoveredConditions = MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_UNCOVERED_CONDITIONS), 0.0); + double conditions = MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_CONDITIONS_TO_COVER), 0.0); + return lines + conditions - uncoveredConditions - uncoveredLines; + } + + @Override + protected Double countElements(DecoratorContext context) { + double lines = MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_LINES_TO_COVER), 0.0); + double conditions = MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_CONDITIONS_TO_COVER), 0.0); + return lines + conditions; + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItCoverageWidget.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItCoverageWidget.java new file mode 100644 index 00000000000..65ef6ef25bb --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItCoverageWidget.java @@ -0,0 +1,44 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage; + +import org.sonar.api.web.AbstractRubyTemplate; +import org.sonar.api.web.NavigationSection; +import org.sonar.api.web.RubyRailsWidget; +import org.sonar.api.web.UserRole; + +@NavigationSection(NavigationSection.RESOURCE) +@UserRole(UserRole.USER) +public class ItCoverageWidget extends AbstractRubyTemplate implements RubyRailsWidget { + + @Override + protected String getTemplatePath() { + return "/org/sonar/plugins/jacoco/itcoverage/widget.html.erb"; + } + + public String getId() { + return "it-coverage"; + } + + public String getTitle() { + return "IT Coverage widget"; + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItLineCoverageDecorator.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItLineCoverageDecorator.java new file mode 100644 index 00000000000..797c3f36bd7 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/ItLineCoverageDecorator.java @@ -0,0 +1,55 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage; + +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.Metric; + +import java.util.Arrays; +import java.util.List; + +/** + * Copied from org.sonar.plugins.core.sensors.LineCoverageDecorator + */ +public class ItLineCoverageDecorator extends AbstractCoverageDecorator { + @Override + protected Metric getTargetMetric() { + return JaCoCoItMetrics.IT_LINE_COVERAGE; + } + + @DependsUpon + public List<Metric> dependsUponMetrics() { + return Arrays.asList(JaCoCoItMetrics.IT_UNCOVERED_LINES, JaCoCoItMetrics.IT_LINES_TO_COVER); + } + + @Override + protected Double countCoveredElements(DecoratorContext context) { + double uncoveredLines = MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_UNCOVERED_LINES), 0.0); + double lines = MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_LINES_TO_COVER), 0.0); + return lines - uncoveredLines; + } + + @Override + protected Double countElements(DecoratorContext context) { + return MeasureUtils.getValue(context.getMeasure(JaCoCoItMetrics.IT_LINES_TO_COVER), 0.0); + } +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItMetrics.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItMetrics.java new file mode 100644 index 00000000000..591d737ff81 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItMetrics.java @@ -0,0 +1,130 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage; + +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.Metrics; +import org.sonar.api.measures.SumChildValuesFormula; + +import java.util.Arrays; +import java.util.List; + +/** + * Should be in {@link org.sonar.api.measures.CoreMetrics} + * + * @author Evgeny Mandrikov + */ +public final class JaCoCoItMetrics implements Metrics { + + public static final String DOMAIN_IT_TESTS = "Integration Tests"; + + public static final String IT_COVERAGE_KEY = "it_coverage"; + public static final Metric IT_COVERAGE = new Metric.Builder(IT_COVERAGE_KEY, "IT Coverage", Metric.ValueType.PERCENT) + .setDescription("Coverage by integration tests") + .setDirection(Metric.DIRECTION_BETTER) + .setQualitative(true) + .setDomain(DOMAIN_IT_TESTS) + .setWorstValue(0.0) + .setBestValue(100.0) + .create(); + + public static final String IT_LINES_TO_COVER_KEY = "it_lines_to_cover"; + public static final Metric IT_LINES_TO_COVER = new Metric.Builder(IT_LINES_TO_COVER_KEY, "IT lines to cover", Metric.ValueType.INT) + .setDescription("IT lines to cover") + .setDirection(Metric.DIRECTION_BETTER) + .setDomain(DOMAIN_IT_TESTS) + .setQualitative(false) + .setFormula(new SumChildValuesFormula(false)) + .setHidden(true) + .create(); + + public static final String IT_UNCOVERED_LINES_KEY = "it_uncovered_lines"; + public static final Metric IT_UNCOVERED_LINES = new Metric.Builder(IT_UNCOVERED_LINES_KEY, "IT uncovered lines", Metric.ValueType.INT) + .setDescription("IT uncovered lines") + .setDirection(Metric.DIRECTION_WORST) + .setQualitative(false) + .setDomain(DOMAIN_IT_TESTS) + .setFormula(new SumChildValuesFormula(false)) + .create(); + + public static final String IT_LINE_COVERAGE_KEY = "it_line_coverage"; + public static final Metric IT_LINE_COVERAGE = new Metric.Builder(IT_LINE_COVERAGE_KEY, "IT line coverage", Metric.ValueType.PERCENT) + .setDescription("IT line coverage") + .setDirection(Metric.DIRECTION_BETTER) + .setQualitative(true) + .setDomain(DOMAIN_IT_TESTS) + .create(); + + public static final String IT_COVERAGE_LINE_HITS_DATA_KEY = "it_coverage_line_hits_data"; + public static final Metric IT_COVERAGE_LINE_HITS_DATA = new Metric.Builder(IT_COVERAGE_LINE_HITS_DATA_KEY, "IT Coverage hits data", Metric.ValueType.DATA) + .setDescription("IT Code coverage line hits data") + .setDirection(Metric.DIRECTION_NONE) + .setQualitative(false) + .setDomain(DOMAIN_IT_TESTS) + .create(); + + public static final String IT_CONDITIONS_TO_COVER_KEY = "it_conditions_to_cover"; + public static final Metric IT_CONDITIONS_TO_COVER = new Metric.Builder(IT_CONDITIONS_TO_COVER_KEY, "IT Conditions to cover", Metric.ValueType.INT) + .setDescription("IT Conditions to cover") + .setDirection(Metric.DIRECTION_BETTER) + .setQualitative(false) + .setDomain(DOMAIN_IT_TESTS) + .setFormula(new SumChildValuesFormula(false)) + .setHidden(true) + .create(); + + public static final String IT_UNCOVERED_CONDITIONS_KEY = "it_uncovered_conditions"; + public static final Metric IT_UNCOVERED_CONDITIONS = new Metric.Builder(IT_UNCOVERED_CONDITIONS_KEY, "IT Uncovered conditions", Metric.ValueType.INT) + .setDescription("IT Uncovered conditions") + .setDirection(Metric.DIRECTION_WORST) + .setDomain(DOMAIN_IT_TESTS) + .setFormula(new SumChildValuesFormula(false)) + .create(); + + public static final String IT_BRANCH_COVERAGE_KEY = "it_branch_coverage"; + public static final Metric IT_BRANCH_COVERAGE = new Metric.Builder(IT_BRANCH_COVERAGE_KEY, "IT Branch coverage", Metric.ValueType.PERCENT) + .setDescription("IT Branch coverage") + .setDirection(Metric.DIRECTION_BETTER) + .setQualitative(true) + .setDomain(DOMAIN_IT_TESTS) + .setWorstValue(0.0) + .setBestValue(100.0) + .create(); + + public static final String IT_CONDITIONS_BY_LINE_KEY = "it_conditions_by_line"; + + public static final Metric IT_CONDITIONS_BY_LINE = new Metric.Builder(IT_CONDITIONS_BY_LINE_KEY, "IT Conditions by line", Metric.ValueType.DATA) + .setDomain(DOMAIN_IT_TESTS) + .create(); + + public static final String IT_COVERED_CONDITIONS_BY_LINE_KEY = "it_covered_conditions_by_line"; + + public static final Metric IT_COVERED_CONDITIONS_BY_LINE = new Metric.Builder(IT_COVERED_CONDITIONS_BY_LINE_KEY, "IT Covered conditions by line", Metric.ValueType.DATA) + .setDomain(DOMAIN_IT_TESTS) + .create(); + + + public List<Metric> getMetrics() { + return Arrays.asList(IT_COVERAGE, IT_LINES_TO_COVER, IT_UNCOVERED_LINES, IT_LINE_COVERAGE, IT_COVERAGE_LINE_HITS_DATA, + IT_CONDITIONS_TO_COVER, IT_UNCOVERED_CONDITIONS, IT_BRANCH_COVERAGE, + IT_CONDITIONS_BY_LINE, IT_COVERED_CONDITIONS_BY_LINE); + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItSensor.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItSensor.java new file mode 100644 index 00000000000..f043d16dddf --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItSensor.java @@ -0,0 +1,103 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.JavaFile; +import org.sonar.api.resources.Project; +import org.sonar.plugins.jacoco.AbstractAnalyzer; +import org.sonar.plugins.jacoco.JacocoConfiguration; + +import java.util.Collection; + +/** + * Note that this class can't extend {@link org.sonar.api.batch.AbstractCoverageExtension}, because in this case this extension will be + * disabled under Sonar 2.3, if JaCoCo is not defined as the default code coverage plugin. + * + * @author Evgeny Mandrikov + */ +public class JaCoCoItSensor implements Sensor { + private JacocoConfiguration configuration; + + public JaCoCoItSensor(JacocoConfiguration configuration) { + this.configuration = configuration; + } + + public boolean shouldExecuteOnProject(Project project) { + return StringUtils.isNotBlank(configuration.getItReportPath()) + && project.getAnalysisType().isDynamic(true); + } + + public void analyse(Project project, SensorContext context) { + new ITAnalyzer().analyse(project, context); + } + + class ITAnalyzer extends AbstractAnalyzer { + @Override + protected String getReportPath(Project project) { + return configuration.getItReportPath(); + } + + @Override + protected void saveMeasures(SensorContext context, JavaFile resource, Collection<Measure> measures) { + for (Measure measure : measures) { + Measure itMeasure = convertForIT(measure); + if (itMeasure != null) { + context.saveMeasure(resource, itMeasure); + } + } + } + + private Measure convertForIT(Measure measure) { + Measure itMeasure = null; + if (CoreMetrics.LINES_TO_COVER.equals(measure.getMetric())) { + itMeasure = new Measure(JaCoCoItMetrics.IT_LINES_TO_COVER, measure.getValue()); + + } else if (CoreMetrics.UNCOVERED_LINES.equals(measure.getMetric())) { + itMeasure = new Measure(JaCoCoItMetrics.IT_UNCOVERED_LINES, measure.getValue()); + + } else if (CoreMetrics.COVERAGE_LINE_HITS_DATA.equals(measure.getMetric())) { + itMeasure = new Measure(JaCoCoItMetrics.IT_COVERAGE_LINE_HITS_DATA, measure.getData()); + + } else if (CoreMetrics.CONDITIONS_TO_COVER.equals(measure.getMetric())) { + itMeasure = new Measure(JaCoCoItMetrics.IT_CONDITIONS_TO_COVER, measure.getValue()); + + } else if (CoreMetrics.UNCOVERED_CONDITIONS.equals(measure.getMetric())) { + itMeasure = new Measure(JaCoCoItMetrics.IT_UNCOVERED_CONDITIONS, measure.getValue()); + + } else if (CoreMetrics.COVERED_CONDITIONS_BY_LINE.equals(measure.getMetric())) { + itMeasure = new Measure(JaCoCoItMetrics.IT_COVERED_CONDITIONS_BY_LINE, measure.getData()); + + } else if (CoreMetrics.CONDITIONS_BY_LINE.equals(measure.getMetric())) { + itMeasure = new Measure(JaCoCoItMetrics.IT_CONDITIONS_BY_LINE, measure.getData()); + } + return itMeasure; + } + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/CoverageViewerDefinition.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/CoverageViewerDefinition.java new file mode 100644 index 00000000000..995353ca3c7 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/CoverageViewerDefinition.java @@ -0,0 +1,39 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage.viewer; + +import org.sonar.api.resources.Resource; +import org.sonar.api.web.*; +import org.sonar.plugins.jacoco.itcoverage.JaCoCoItMetrics; + +@ResourceQualifier(Resource.QUALIFIER_CLASS) +@NavigationSection(NavigationSection.RESOURCE_TAB) +@DefaultTab(metrics = { JaCoCoItMetrics.IT_COVERAGE_KEY, JaCoCoItMetrics.IT_LINES_TO_COVER_KEY, JaCoCoItMetrics.IT_UNCOVERED_LINES_KEY, JaCoCoItMetrics.IT_LINE_COVERAGE_KEY, JaCoCoItMetrics.IT_CONDITIONS_TO_COVER_KEY, JaCoCoItMetrics.IT_UNCOVERED_CONDITIONS_KEY, JaCoCoItMetrics.IT_BRANCH_COVERAGE_KEY }) +@UserRole(UserRole.CODEVIEWER) +public class CoverageViewerDefinition extends GwtPage { + + public String getTitle() { + return "IT Coverage"; + } + + public String getGwtId() { + return "org.sonar.plugins.jacoco.itcoverage.viewer.CoverageViewer"; + } +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/client/CoveragePanel.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/client/CoveragePanel.java new file mode 100644 index 00000000000..80651a99c61 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/client/CoveragePanel.java @@ -0,0 +1,150 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage.viewer.client; + +import org.sonar.gwt.ui.SourcePanel; +import org.sonar.wsclient.gwt.AbstractCallback; +import org.sonar.wsclient.gwt.Sonar; +import org.sonar.wsclient.services.Resource; +import org.sonar.wsclient.services.ResourceQuery; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Copied from org.sonar.plugins.core.coverageviewer.client.CoveragePanel + */ +public class CoveragePanel extends SourcePanel { + + private Map<Integer, Integer> hitsByLine = new HashMap<Integer, Integer>(); + private Map<Integer, Integer> conditionsByLine = new HashMap<Integer, Integer>(); + private Map<Integer, Integer> coveredConditionsByLine = new HashMap<Integer, Integer>(); + private Map<Integer, String> branchCoverageByLine = new HashMap<Integer, String>(); + + public CoveragePanel(Resource resource) { + super(resource); + loadCoverageHits(resource); + } + + private void loadCoverageHits(Resource resource) { + ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.IT_COVERAGE_LINE_HITS_DATA, Metrics.IT_BRANCH_COVERAGE_HITS_DATA, Metrics.IT_CONDITIONS_BY_LINE, Metrics.IT_COVERED_CONDITIONS_BY_LINE); + Sonar.getInstance().find(query, new AbstractCallback<Resource>() { + + @Override + protected void doOnResponse(Resource resource) { + handleLineHits(resource); + handleLineConditions(resource); + handleDeprecatedBranchCoverage(resource); + setStarted(); + } + }); + } + + private void handleLineHits(Resource resource) { + parseDataMap(resource, Metrics.IT_COVERAGE_LINE_HITS_DATA, hitsByLine); + } + + private void handleLineConditions(Resource resource) { + parseDataMap(resource, Metrics.IT_CONDITIONS_BY_LINE, conditionsByLine); + parseDataMap(resource, Metrics.IT_COVERED_CONDITIONS_BY_LINE, coveredConditionsByLine); + } + + private void parseDataMap(Resource resource, String metric, Map<Integer, Integer> map) { + if (resource == null || resource.getMeasure(metric) == null) { + return; + } + + map.clear(); + String data = resource.getMeasure(metric).getData(); + for (String lineWithValue : data.split(";")) { + String[] elt = lineWithValue.split("="); + if (elt != null && elt.length == 2) { + map.put(Integer.parseInt(elt[0]), Integer.parseInt(elt[1])); + } + } + } + + private void handleDeprecatedBranchCoverage(Resource resource) { + if (resource == null || resource.getMeasure(Metrics.IT_BRANCH_COVERAGE_HITS_DATA) == null) { + return; + } + + branchCoverageByLine.clear(); + String data = resource.getMeasure(Metrics.IT_BRANCH_COVERAGE_HITS_DATA).getData(); + for (String lineWithValue : data.split(";")) { + String[] elt = lineWithValue.split("="); + if (elt != null && elt.length == 2) { + branchCoverageByLine.put(Integer.parseInt(elt[0]), elt[1]); + } + } + } + + @Override + protected boolean shouldDecorateLine(int index) { + return index > 0; + } + + @Override + protected List<Row> decorateLine(int index, String source) { + Row row = new Row().setLineIndex(index, ""); + + Integer hits = hitsByLine.get(index); + Integer conditions = conditionsByLine.get(index); + Integer coveredConditions = coveredConditionsByLine.get(index); + String branchCoverage = branchCoverageByLine.get(index); + if (branchCoverage == null && conditions != null && coveredConditions != null) { + branchCoverage = String.valueOf(conditions - coveredConditions) + "/" + String.valueOf(conditions); + } + + boolean hasLineCoverage = (hits != null); + boolean hasBranchCoverage = (branchCoverage != null); + boolean lineIsCovered = (hasLineCoverage && hits > 0); + boolean branchIsCovered = ("100%".equals(branchCoverage) || (conditions != null && coveredConditions != null && coveredConditions == conditions)); + + row.setSource(source, ""); + row.setValue(" ", ""); + row.setValue2(" ", ""); + + if (lineIsCovered) { + if (branchIsCovered) { + row.setValue(String.valueOf(hits), "green"); + row.setValue2(branchCoverage, "green"); + + } else if (hasBranchCoverage) { + row.setValue(String.valueOf(hits), "orange"); + row.setValue2(branchCoverage, "orange"); + row.setSource(source, "orange"); + } else { + row.setValue(String.valueOf(hits), "green"); + } + } else if (hasLineCoverage) { + row.setValue(String.valueOf(hits), "red"); + row.setSource(source, "red"); + if (hasBranchCoverage) { + row.setValue2(branchCoverage, "red"); + } else { + row.setValue2(" ", "red"); + } + } + return Arrays.asList(row); + } +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/client/CoverageViewer.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/client/CoverageViewer.java new file mode 100644 index 00000000000..1d9b3ae8c6b --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/client/CoverageViewer.java @@ -0,0 +1,69 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage.viewer.client; + +import org.sonar.gwt.ui.Page; +import org.sonar.gwt.ui.ViewerHeader; +import org.sonar.wsclient.services.Measure; +import org.sonar.wsclient.services.Resource; + +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Widget; + +/** + * Copied from org.sonar.plugins.core.coverageviewer.client.CoverageViewer + */ +public class CoverageViewer extends Page { + @Override + protected Widget doOnResourceLoad(Resource resource) { + FlowPanel panel = new FlowPanel(); + panel.setWidth("100%"); + panel.add(new CoverageHeader(resource)); + panel.add(new CoveragePanel(resource)); + return panel; + } + + private static class CoverageHeader extends ViewerHeader { + public CoverageHeader(Resource resource) { + super(resource, new String[] { Metrics.IT_COVERAGE, Metrics.IT_LINE_COVERAGE, Metrics.IT_UNCOVERED_LINES, Metrics.IT_BRANCH_COVERAGE, Metrics.IT_UNCOVERED_CONDITIONS }); + } + + @Override + protected void display(FlowPanel header, Resource resource) { + HorizontalPanel panel = new HorizontalPanel(); + header.add(panel); + + Measure measure = resource.getMeasure(Metrics.IT_COVERAGE); + if (measure == null) { + addBigCell(panel, "-"); + } else { + addBigCell(panel, measure.getFormattedValue()); + } + + addCell(panel, resource.getMeasure(Metrics.IT_LINE_COVERAGE)); + addCell(panel, resource.getMeasure(Metrics.IT_UNCOVERED_LINES)); + addCell(panel, resource.getMeasure(Metrics.IT_LINES_TO_COVER)); + addCell(panel, resource.getMeasure(Metrics.IT_BRANCH_COVERAGE)); + addCell(panel, resource.getMeasure(Metrics.IT_UNCOVERED_CONDITIONS)); + addCell(panel, resource.getMeasure(Metrics.IT_CONDITIONS_TO_COVER)); + } + } +} diff --git a/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/client/Metrics.java b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/client/Metrics.java new file mode 100644 index 00000000000..3e3190f6418 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/java/org/sonar/plugins/jacoco/itcoverage/viewer/client/Metrics.java @@ -0,0 +1,42 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage.viewer.client; + +/** + * Should be in {@link org.sonar.gwt.Metrics} + */ +public interface Metrics { + String IT_COVERAGE = "it_coverage"; + String IT_LINES_TO_COVER = "it_lines_to_cover"; + String IT_UNCOVERED_LINES = "it_uncovered_lines"; + String IT_LINE_COVERAGE = "it_line_coverage"; + String IT_COVERAGE_LINE_HITS_DATA = "it_coverage_line_hits_data"; + String IT_CONDITIONS_TO_COVER = "it_conditions_to_cover"; + String IT_UNCOVERED_CONDITIONS = "it_uncovered_conditions"; + String IT_BRANCH_COVERAGE = "it_branch_coverage"; + String IT_CONDITIONS_BY_LINE = "it_conditions_by_line"; + String IT_COVERED_CONDITIONS_BY_LINE = "it_covered_conditions_by_line"; + + /** + * @deprecated use IT_CONDITIONS_BY_LINE and IT_COVERED_CONDITIONS_BY_LINE + */ + @Deprecated + String IT_BRANCH_COVERAGE_HITS_DATA = "it_branch_coverage_hits_data"; +} diff --git a/plugins/sonar-jacoco-plugin/src/main/resources/org/sonar/plugins/jacoco/itcoverage/viewer/CoverageViewer.gwt.xml b/plugins/sonar-jacoco-plugin/src/main/resources/org/sonar/plugins/jacoco/itcoverage/viewer/CoverageViewer.gwt.xml new file mode 100644 index 00000000000..38176710583 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/resources/org/sonar/plugins/jacoco/itcoverage/viewer/CoverageViewer.gwt.xml @@ -0,0 +1,10 @@ +<module> + <inherits name="com.google.gwt.user.User"/> + <inherits name="com.google.gwt.json.JSON"/> + <inherits name="com.google.gwt.http.HTTP"/> + <inherits name="org.sonar.Sonar"/> + <inherits name="com.google.gwt.gen2.table.Table"/> + + <entry-point class="org.sonar.plugins.jacoco.itcoverage.viewer.client.CoverageViewer"/> + +</module> diff --git a/plugins/sonar-jacoco-plugin/src/main/resources/org/sonar/plugins/jacoco/itcoverage/widget.html.erb b/plugins/sonar-jacoco-plugin/src/main/resources/org/sonar/plugins/jacoco/itcoverage/widget.html.erb new file mode 100644 index 00000000000..00f91c0bb2b --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/main/resources/org/sonar/plugins/jacoco/itcoverage/widget.html.erb @@ -0,0 +1,32 @@ +<% + it_coverage_measure=measure('it_coverage') + if it_coverage_measure %> + <table width="100%"> + <tr> + <td valign="top" width="100%"> + <div class="dashbox"> + <p class="title">IT Code coverage</p> + + <p> + <span class="big"><%= format_measure(it_coverage_measure, :suffix => '', :url => url_for_drilldown('it_coverage'), :default => '-') %></span> + <%= dashboard_configuration.selected_period? ? format_variation(it_coverage_measure) : trend_icon(it_coverage_measure) -%> + </p> + <% it_line_coverage=measure('it_line_coverage') + if it_line_coverage %> + <p> + <%= format_measure(it_line_coverage, :suffix => ' IT line coverage', :url => url_for_drilldown('it_uncovered_lines', :highlight => 'it_line_coverage')) %> + <%= dashboard_configuration.selected_period? ? format_variation(it_line_coverage) : trend_icon(it_line_coverage) -%> + </p> + <% end %> + <% it_branch_coverage=measure('it_branch_coverage') + if it_branch_coverage %> + <p> + <%= format_measure(it_branch_coverage, :suffix => ' IT branch coverage', :url => url_for_drilldown('it_uncovered_conditions', :highlight => 'it_branch_coverage')) %> + <%= dashboard_configuration.selected_period? ? format_variation(it_branch_coverage) : trend_icon(it_branch_coverage) -%> + </p> + <% end %> + </div> + </td> + </tr> + </table> +<% end %> diff --git a/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/AbstractAnalyzerTest.java b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/AbstractAnalyzerTest.java new file mode 100644 index 00000000000..49ad852ddeb --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/AbstractAnalyzerTest.java @@ -0,0 +1,40 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.jacoco.core.analysis.ISourceFileCoverage; +import org.junit.Test; +import org.sonar.api.resources.JavaFile; + +public class AbstractAnalyzerTest { + @Test + public void defaultPackage() { + ISourceFileCoverage coverage = mock(ISourceFileCoverage.class); + when(coverage.getPackageName()).thenReturn("").thenReturn("org/example"); + when(coverage.getName()).thenReturn("Hello.java"); + assertThat(AbstractAnalyzer.getResource(coverage), is(new JavaFile("[default].Hello"))); + assertThat(AbstractAnalyzer.getResource(coverage), is(new JavaFile("org.example.Hello"))); + } +} diff --git a/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandlerTest.java b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandlerTest.java new file mode 100644 index 00000000000..81d102babd5 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandlerTest.java @@ -0,0 +1,91 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; + +import org.apache.commons.configuration.BaseConfiguration; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.maven.MavenPlugin; +import org.sonar.api.batch.maven.MavenSurefireUtils; +import org.sonar.api.resources.Project; +import org.sonar.api.test.MavenTestUtils; + +/** + * @author Evgeny Mandrikov + */ +public class JaCoCoMavenPluginHandlerTest { + + private JacocoConfiguration configuration; + private JaCoCoMavenPluginHandler handler; + + @Before + public void setUp() throws Exception { + JaCoCoAgentDownloader downloader = mock(JaCoCoAgentDownloader.class); + when(downloader.getAgentJarFile()).thenReturn(new File("jacocoagent.jar")); + Project project = mock(Project.class); + when(project.getConfiguration()).thenReturn(new BaseConfiguration()); + configuration = spy(new JacocoConfiguration(project.getConfiguration(), downloader)); + + handler = new JaCoCoMavenPluginHandler(configuration); + } + + @Test + public void testMavenPluginDefinition() { + assertThat(handler.getGroupId(), is(MavenSurefireUtils.GROUP_ID)); + assertThat(handler.getArtifactId(), is(MavenSurefireUtils.ARTIFACT_ID)); + assertThat(handler.getVersion(), is(MavenSurefireUtils.VERSION)); + assertThat(handler.getGoals(), is(new String[] { "test" })); + assertThat(handler.isFixedVersion(), is(false)); + } + + @Test + public void testConfigureMavenPlugin() { + Project project = MavenTestUtils.loadProjectFromPom(getClass(), "pom.xml"); + MavenPlugin plugin = new MavenPlugin(handler.getGroupId(), handler.getArtifactId(), handler.getVersion()); + + handler.configure(project, plugin); + + verify(configuration).getJvmArgument(); + assertThat(plugin.getParameter("argLine"), is("-javaagent:jacocoagent.jar=destfile=target/jacoco.exec")); + assertThat(plugin.getParameter("testFailureIgnore"), is("true")); + } + + @Test + public void testReconfigureMavenPlugin() { + Project project = MavenTestUtils.loadProjectFromPom(getClass(), "pom2.xml"); + MavenPlugin plugin = MavenPlugin.getPlugin(project.getPom(), handler.getGroupId(), handler.getArtifactId()); + + handler.configure(project, plugin); + + verify(configuration).getJvmArgument(); + assertThat(plugin.getParameter("argLine"), is("-javaagent:jacocoagent.jar=destfile=target/jacoco.exec -esa")); + assertThat(plugin.getParameter("testFailureIgnore"), is("true")); + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JaCoCoPluginTest.java b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JaCoCoPluginTest.java new file mode 100644 index 00000000000..33a989813b2 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JaCoCoPluginTest.java @@ -0,0 +1,53 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +import org.junit.Before; +import org.junit.Test; + +/** + * @author Evgeny Mandrikov + */ +public class JaCoCoPluginTest { + private JaCoCoPlugin plugin = new JaCoCoPlugin(); + + @Before + public void setUp() { + plugin = new JaCoCoPlugin(); + } + + @Test + public void testPluginDefition() { + assertThat(plugin.getKey(), is("jacoco")); + assertThat(plugin.getName(), notNullValue()); + assertThat(plugin.getDescription(), notNullValue()); + assertThat(plugin.toString(), is("jacoco")); + } + + @Test + public void testExtensions() { + assertThat(plugin.getExtensions().size(), greaterThan(0)); + } +} diff --git a/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JaCoCoSensorTest.java b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JaCoCoSensorTest.java new file mode 100644 index 00000000000..e3066e5c30a --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JaCoCoSensorTest.java @@ -0,0 +1,120 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.*; +import org.sonar.api.test.IsMeasure; + +import java.io.File; + +/** + * @author Evgeny Mandrikov + */ +public class JaCoCoSensorTest { + + private JacocoConfiguration configuration; + private JaCoCoSensor sensor; + + @Before + public void setUp() { + configuration = mock(JacocoConfiguration.class); + sensor = new JaCoCoSensor(configuration); + } + + @Test + public void testSensorDefinition() { + assertThat(sensor.toString(), is("JaCoCoSensor")); + } + + @Test + public void shouldNotExecuteOnProject() { + Project project = mock(Project.class); + when(project.getLanguageKey()).thenReturn("flex"); + assertThat(sensor.shouldExecuteOnProject(project), is(false)); + } + + @Test + public void shouldExecuteOnProject() { + Project project = mock(Project.class); + when(project.getLanguageKey()).thenReturn(Java.KEY); + assertThat(sensor.shouldExecuteOnProject(project), is(true)); + } + + @Test + public void testReadExecutionData() throws Exception { + File jacocoExecutionData = new File(getClass().getResource("/org/sonar/plugins/jacoco/JaCoCoSensorTest/jacoco.exec").getFile()); + File buildOutputDir = jacocoExecutionData.getParentFile(); + SensorContext context = mock(SensorContext.class); + + final JavaFile resource = new JavaFile("org.sonar.plugins.jacoco.tests.Hello"); + when(context.getResource(any(Resource.class))).thenReturn(resource); + + ProjectFileSystem pfs = mock(ProjectFileSystem.class); + when(pfs.getBuildOutputDir()).thenReturn(buildOutputDir); + when(pfs.resolvePath(anyString())).thenReturn(jacocoExecutionData); + + Project project = mock(Project.class); + when(project.getFileSystem()).thenReturn(pfs); + + sensor.analyse(project, context); + + verify(context).getResource(eq(resource)); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(CoreMetrics.LINES_TO_COVER, 7.0))); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(CoreMetrics.UNCOVERED_LINES, 3.0))); + verify(context).saveMeasure(eq(resource), + argThat(new IsMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "6=1;7=1;8=1;11=1;15=0;16=0;18=0"))); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(CoreMetrics.CONDITIONS_TO_COVER, 2.0))); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(CoreMetrics.UNCOVERED_CONDITIONS, 2.0))); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(CoreMetrics.CONDITIONS_BY_LINE, "15=2"))); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "15=0"))); + verifyNoMoreInteractions(context); + } + + @Test + public void doNotSaveMeasureOnResourceWhichDoesntExistInTheContext() throws Exception { + File jacocoExecutionData = new File(getClass().getResource("/org/sonar/plugins/jacoco/JaCoCoSensorTest/jacoco.exec").getFile()); + File buildOutputDir = jacocoExecutionData.getParentFile(); + SensorContext context = mock(SensorContext.class); + when(context.getResource(any(Resource.class))).thenReturn(null); + + ProjectFileSystem pfs = mock(ProjectFileSystem.class); + when(pfs.getBuildOutputDir()).thenReturn(buildOutputDir); + + Project project = mock(Project.class); + when(project.getFileSystem()).thenReturn(pfs); + + sensor.analyse(project, context); + + verify(context, never()).saveMeasure(any(Resource.class), any(Measure.class)); + } +} diff --git a/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JacocoConfigurationTest.java b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JacocoConfigurationTest.java new file mode 100644 index 00000000000..ef855eb0e1c --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JacocoConfigurationTest.java @@ -0,0 +1,93 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.commons.configuration.BaseConfiguration; +import org.apache.commons.configuration.Configuration; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +public class JacocoConfigurationTest { + + private Configuration configuration; + private JacocoConfiguration jacocoConfiguration; + + @Before + public void setUp() { + JaCoCoAgentDownloader downloader = mock(JaCoCoAgentDownloader.class); + when(downloader.getAgentJarFile()).thenReturn(new File("jacocoagent.jar")); + + configuration = new BaseConfiguration(); + + jacocoConfiguration = new JacocoConfiguration(configuration, downloader); + } + + @Test + public void defaults() { + assertThat(jacocoConfiguration.getReportPath(), is("target/jacoco.exec")); + assertThat(jacocoConfiguration.getJvmArgument(), is("-javaagent:jacocoagent.jar=destfile=target/jacoco.exec")); + + assertThat(jacocoConfiguration.getItReportPath(), is("")); + + assertThat(jacocoConfiguration.getAntTargets(), is(new String[] {})); + } + + @Test + public void shouldReturnAntTargets() { + configuration.setProperty(JacocoConfiguration.ANT_TARGETS_PROPERTY, "test"); + assertThat(jacocoConfiguration.getAntTargets(), is(new String[] { "test" })); + + configuration.setProperty(JacocoConfiguration.ANT_TARGETS_PROPERTY, "test1,test2"); + assertThat(jacocoConfiguration.getAntTargets(), is(new String[] { "test1", "test2" })); + } + + @Test + public void shouldReturnItReportPath() { + configuration.setProperty(JacocoConfiguration.IT_REPORT_PATH_PROPERTY, "target/it-jacoco.exec"); + + assertThat(jacocoConfiguration.getItReportPath(), is("target/it-jacoco.exec")); + } + + @Test + public void shouldSetDestfile() { + configuration.setProperty(JacocoConfiguration.REPORT_PATH_PROPERTY, "jacoco.exec"); + + assertThat(jacocoConfiguration.getReportPath(), is("jacoco.exec")); + assertThat(jacocoConfiguration.getJvmArgument(), is("-javaagent:jacocoagent.jar=destfile=jacoco.exec")); + } + + @Test + public void shouldSetIncludesAndExcludes() { + configuration.setProperty(JacocoConfiguration.INCLUDES_PROPERTY, "org.sonar.*"); + configuration.setProperty(JacocoConfiguration.EXCLUDES_PROPERTY, "org.sonar.api.*"); + configuration.setProperty(JacocoConfiguration.EXCLCLASSLOADER_PROPERTY, "sun.reflect.DelegatingClassLoader"); + + assertThat(jacocoConfiguration.getJvmArgument(), + is("-javaagent:jacocoagent.jar=destfile=target/jacoco.exec,includes=org.sonar.*,excludes=org.sonar.api.*,exclclassloader=sun.reflect.DelegatingClassLoader")); + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JacocoMavenInitializerTest.java b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JacocoMavenInitializerTest.java new file mode 100644 index 00000000000..44e3a50959d --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/JacocoMavenInitializerTest.java @@ -0,0 +1,88 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco; + +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.ProjectFileSystem; + +public class JacocoMavenInitializerTest { + private JaCoCoMavenPluginHandler mavenPluginHandler; + private JacocoMavenInitializer initializer; + + @Before + public void setUp() { + mavenPluginHandler = mock(JaCoCoMavenPluginHandler.class); + initializer = new JacocoMavenInitializer(mavenPluginHandler); + } + + @Test + public void shouldDoNothing() { + Project project = mockProject(); + initializer.execute(project); + verifyNoMoreInteractions(project); + verifyNoMoreInteractions(mavenPluginHandler); + } + + @Test + public void shouldExecuteMaven() { + Project project = mockProject(); + when(project.getFileSystem().hasTestFiles(argThat(is(Java.INSTANCE)))).thenReturn(true); + when(project.getAnalysisType()).thenReturn(Project.AnalysisType.DYNAMIC); + + assertThat(initializer.shouldExecuteOnProject(project), is(true)); + assertThat(initializer.getMavenPluginHandler(project), instanceOf(JaCoCoMavenPluginHandler.class)); + } + + @Test + public void shouldNotExecuteMavenWhenReuseReports() { + Project project = mockProject(); + when(project.getFileSystem().hasTestFiles(argThat(is(Java.INSTANCE)))).thenReturn(true); + when(project.getAnalysisType()).thenReturn(Project.AnalysisType.REUSE_REPORTS); + + assertThat(initializer.shouldExecuteOnProject(project), is(false)); + } + + @Test + public void shouldNotExecuteMavenWhenNoTests() { + Project project = mockProject(); + when(project.getFileSystem().hasTestFiles(argThat(is(Java.INSTANCE)))).thenReturn(false); + when(project.getAnalysisType()).thenReturn(Project.AnalysisType.DYNAMIC); + + assertThat(initializer.shouldExecuteOnProject(project), is(false)); + } + + private Project mockProject() { + Project project = mock(Project.class); + ProjectFileSystem projectFileSystem = mock(ProjectFileSystem.class); + when(project.getFileSystem()).thenReturn(projectFileSystem); + return project; + } +} diff --git a/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/itcoverage/ItCoverageWidgetTest.java b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/itcoverage/ItCoverageWidgetTest.java new file mode 100644 index 00000000000..bb5870e7733 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/itcoverage/ItCoverageWidgetTest.java @@ -0,0 +1,38 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage; + +import org.junit.Test; + +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +/** + * @author Evgeny Mandrikov + */ +public class ItCoverageWidgetTest { + + @Test + public void testGetTemplatePath() { + String path = new ItCoverageWidget().getTemplatePath(); + assertThat(getClass().getResource(path), notNullValue()); + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItMetricsTest.java b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItMetricsTest.java new file mode 100644 index 00000000000..b931fb12268 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItMetricsTest.java @@ -0,0 +1,34 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class JaCoCoItMetricsTest { + + @Test + public void metricsDefinition() { + assertThat(new JaCoCoItMetrics().getMetrics().size(), is(10)); + } + +} diff --git a/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItSensorTest.java b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItSensorTest.java new file mode 100644 index 00000000000..aca9c9bfd9b --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/java/org/sonar/plugins/jacoco/itcoverage/JaCoCoItSensorTest.java @@ -0,0 +1,119 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.jacoco.itcoverage; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.*; +import org.sonar.api.resources.Project.AnalysisType; +import org.sonar.api.test.IsMeasure; +import org.sonar.plugins.jacoco.JacocoConfiguration; + +import java.io.File; + +public class JaCoCoItSensorTest { + private JacocoConfiguration configuration; + private JaCoCoItSensor sensor; + + @Before + public void setUp() { + configuration = mock(JacocoConfiguration.class); + sensor = new JaCoCoItSensor(configuration); + } + + @Test + public void testSensorDefinition() { + assertThat(sensor.toString(), is("JaCoCoItSensor")); + } + + @Test + public void doNotExecuteWhenReportPathNotSpecified() { + when(configuration.getItReportPath()).thenReturn(""); + Project project = mock(Project.class); + assertThat(sensor.shouldExecuteOnProject(project), is(false)); + } + + @Test + public void shouldExecuteOnProject() { + when(configuration.getItReportPath()).thenReturn("target/it-jacoco.exec"); + Project project = mock(Project.class); + when(project.getAnalysisType()).thenReturn(AnalysisType.DYNAMIC).thenReturn(AnalysisType.REUSE_REPORTS); + assertThat(sensor.shouldExecuteOnProject(project), is(true)); + assertThat(sensor.shouldExecuteOnProject(project), is(true)); + } + + @Test + public void testReadExecutionData() throws Exception { + File jacocoExecutionData = new File(getClass().getResource("/org/sonar/plugins/jacoco/JaCoCoSensorTest/jacoco.exec").getFile()); + File buildOutputDir = jacocoExecutionData.getParentFile(); + SensorContext context = mock(SensorContext.class); + + final JavaFile resource = new JavaFile("org.sonar.plugins.jacoco.tests.Hello"); + when(context.getResource(any(Resource.class))).thenReturn(resource); + + ProjectFileSystem pfs = mock(ProjectFileSystem.class); + when(pfs.getBuildOutputDir()).thenReturn(buildOutputDir); + when(pfs.resolvePath(anyString())).thenReturn(jacocoExecutionData); + + Project project = mock(Project.class); + when(project.getFileSystem()).thenReturn(pfs); + + sensor.analyse(project, context); + + verify(context).getResource(eq(resource)); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(JaCoCoItMetrics.IT_LINES_TO_COVER, 7.0))); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(JaCoCoItMetrics.IT_UNCOVERED_LINES, 3.0))); + verify(context).saveMeasure(eq(resource), + argThat(new IsMeasure(JaCoCoItMetrics.IT_COVERAGE_LINE_HITS_DATA, "6=1;7=1;8=1;11=1;15=0;16=0;18=0"))); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(JaCoCoItMetrics.IT_CONDITIONS_TO_COVER, 2.0))); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(JaCoCoItMetrics.IT_UNCOVERED_CONDITIONS, 2.0))); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(JaCoCoItMetrics.IT_CONDITIONS_BY_LINE, "15=2"))); + verify(context).saveMeasure(eq(resource), argThat(new IsMeasure(JaCoCoItMetrics.IT_COVERED_CONDITIONS_BY_LINE, "15=0"))); + verifyNoMoreInteractions(context); + } + + @Test + public void doNotSaveMeasureOnResourceWhichDoesntExistInTheContext() throws Exception { + File jacocoExecutionData = new File(getClass().getResource("/org/sonar/plugins/jacoco/JaCoCoSensorTest/jacoco.exec").getFile()); + File buildOutputDir = jacocoExecutionData.getParentFile(); + SensorContext context = mock(SensorContext.class); + when(context.getResource(any(Resource.class))).thenReturn(null); + + ProjectFileSystem pfs = mock(ProjectFileSystem.class); + when(pfs.getBuildOutputDir()).thenReturn(buildOutputDir); + + Project project = mock(Project.class); + when(project.getFileSystem()).thenReturn(pfs); + + sensor.analyse(project, context); + + verify(context, never()).saveMeasure(any(Resource.class), any(Measure.class)); + } +} diff --git a/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandlerTest/pom.xml b/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandlerTest/pom.xml new file mode 100644 index 00000000000..0cc53221f8d --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandlerTest/pom.xml @@ -0,0 +1,18 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>foo</groupId> + <artifactId>bar</artifactId> + <version>0.2-SNAPSHOT</version> + <packaging>jar</packaging> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <target>1.5</target> + </configuration> + </plugin> + </plugins> + </build> +</project>
\ No newline at end of file diff --git a/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandlerTest/pom2.xml b/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandlerTest/pom2.xml new file mode 100644 index 00000000000..127ec720f95 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoMavenPluginHandlerTest/pom2.xml @@ -0,0 +1,25 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>foo</groupId> + <artifactId>bar</artifactId> + <version>0.2-SNAPSHOT</version> + <packaging>jar</packaging> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <target>1.5</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine>-esa</argLine> + </configuration> + </plugin> + </plugins> + </build> +</project>
\ No newline at end of file diff --git a/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoSensorTest/Hello.class b/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoSensorTest/Hello.class Binary files differnew file mode 100644 index 00000000000..e004f4f6149 --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoSensorTest/Hello.class diff --git a/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoSensorTest/jacoco.exec b/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoSensorTest/jacoco.exec Binary files differnew file mode 100644 index 00000000000..c7cadccb04e --- /dev/null +++ b/plugins/sonar-jacoco-plugin/src/test/resources/org/sonar/plugins/jacoco/JaCoCoSensorTest/jacoco.exec @@ -48,6 +48,7 @@ <module>plugins/sonar-design-plugin</module> <module>plugins/sonar-l10n-en-plugin</module> <module>plugins/sonar-email-notifications-plugin</module> + <module>plugins/sonar-jacoco-plugin</module> </modules> <organization> diff --git a/sonar-application/pom.xml b/sonar-application/pom.xml index f4c91172d7a..4f14a8459ec 100644 --- a/sonar-application/pom.xml +++ b/sonar-application/pom.xml @@ -195,6 +195,12 @@ <scope>runtime</scope> </dependency> <dependency> + <groupId>org.codehaus.sonar.plugins</groupId> + <artifactId>sonar-jacoco-plugin</artifactId> + <version>${project.version}</version> + <scope>runtime</scope> + </dependency> + <dependency> <groupId>org.sonatype.jsw-binaries</groupId> <artifactId>jsw-binaries</artifactId> <version>3.2.3.6</version> diff --git a/sonar-server/pom.xml b/sonar-server/pom.xml index 0142081d64e..3341f21b830 100644 --- a/sonar-server/pom.xml +++ b/sonar-server/pom.xml @@ -467,6 +467,12 @@ <version>${project.version}</version> <scope>provided</scope> </dependency> + <dependency> + <groupId>org.codehaus.sonar.plugins</groupId> + <artifactId>sonar-jacoco-plugin</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> </dependencies> </profile> |