diff options
author | Mirko Friedenhagen <mfriedenhagen@gmail.com> | 2013-11-17 13:19:17 -0800 |
---|---|---|
committer | Mirko Friedenhagen <mfriedenhagen@gmail.com> | 2013-11-17 13:19:17 -0800 |
commit | 5754253f9bac1596634c1faf0423cf0cd7b12451 (patch) | |
tree | 539f2191a0980a3d53e78f4d546eac16e4068dfa | |
parent | 3e727fdf376c3f1d57edf9b92e6c745f0a8c4237 (diff) | |
parent | ede9c6301f4bcce75cb569bea4cc201469910cc5 (diff) | |
download | jacoco-5754253f9bac1596634c1faf0423cf0cd7b12451.tar.gz |
Merge pull request #152 from mfriedenhagen/multiple-maven-reports-new
Additional report-integration and prepare-agent-integration goals for integration tests.
14 files changed, 883 insertions, 492 deletions
diff --git a/jacoco-maven-plugin.test/it/it-site-failsafe/invoker.properties b/jacoco-maven-plugin.test/it/it-site-failsafe/invoker.properties new file mode 100644 index 00000000..2e64a55e --- /dev/null +++ b/jacoco-maven-plugin.test/it/it-site-failsafe/invoker.properties @@ -0,0 +1,4 @@ +invoker.goals = clean verify site + +# maven-site-plugin 3.3 works with at least Maven 2.2.1+ +invoker.maven.version = 2.2.1+ diff --git a/jacoco-maven-plugin.test/it/it-site-failsafe/pom.xml b/jacoco-maven-plugin.test/it/it-site-failsafe/pom.xml new file mode 100644 index 00000000..d2a66922 --- /dev/null +++ b/jacoco-maven-plugin.test/it/it-site-failsafe/pom.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html + + Contributors: + Evgeny Mandrikov - initial API and implementation +--> +<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>jacoco</groupId> + <artifactId>setup-parent</artifactId> + <version>1.0-SNAPSHOT</version> + </parent> + + <artifactId>it-site-failsafe</artifactId> + + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <version>2.14.1</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.14.1</version> + </plugin> + </plugins> + </pluginManagement> + <plugins> + <plugin> + <groupId>@project.groupId@</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <executions> + <execution> + <id>default-prepare-agent</id> + <goals> + <goal>prepare-agent</goal> + </goals> + </execution> + <execution> + <id>default-prepare-agent-integration</id> + <goals> + <goal>prepare-agent-integration</goal> + </goals> + </execution> + <execution> + <id>default-report-integration</id> + <goals> + <goal>report-integration</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <executions> + <execution> + <id>default-integration-test</id> + <goals> + <goal>integration-test</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-site-plugin</artifactId> + <!-- version 3.3 is needed as 3.2 is not able to produce multiple reports. --> + <version>3.3</version> + </plugin> + </plugins> + </build> + + <reporting> + <excludeDefaults>true</excludeDefaults> + <plugins> + <plugin> + <groupId>@project.groupId@</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>@project.version@</version> + </plugin> + </plugins> + </reporting> +</project> diff --git a/jacoco-maven-plugin.test/it/it-site-failsafe/src/main/java/Example.java b/jacoco-maven-plugin.test/it/it-site-failsafe/src/main/java/Example.java new file mode 100644 index 00000000..654d3bd5 --- /dev/null +++ b/jacoco-maven-plugin.test/it/it-site-failsafe/src/main/java/Example.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Evgeny Mandrikov - initial API and implementation + * + *******************************************************************************/ +public class Example { + + public void sayHello() { + System.out.println("Hello world"); + } + + public void sayHelloIT() { + System.out.println("Hello world"); + } + +} diff --git a/jacoco-maven-plugin.test/it/it-site-failsafe/src/test/java/ExampleIT.java b/jacoco-maven-plugin.test/it/it-site-failsafe/src/test/java/ExampleIT.java new file mode 100644 index 00000000..df6d8b05 --- /dev/null +++ b/jacoco-maven-plugin.test/it/it-site-failsafe/src/test/java/ExampleIT.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Evgeny Mandrikov - initial API and implementation + * + *******************************************************************************/ +import org.junit.Test; + +public class ExampleIT { + + @Test + public void test() { + new Example().sayHelloIT(); + } + +} diff --git a/jacoco-maven-plugin.test/it/it-site-failsafe/src/test/java/ExampleTest.java b/jacoco-maven-plugin.test/it/it-site-failsafe/src/test/java/ExampleTest.java new file mode 100644 index 00000000..d9b78ab9 --- /dev/null +++ b/jacoco-maven-plugin.test/it/it-site-failsafe/src/test/java/ExampleTest.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Evgeny Mandrikov - initial API and implementation + * + *******************************************************************************/ +import org.junit.Test; + +public class ExampleTest { + + @Test + public void test() { + new Example().sayHello(); + } + +} diff --git a/jacoco-maven-plugin.test/it/it-site-failsafe/verify.bsh b/jacoco-maven-plugin.test/it/it-site-failsafe/verify.bsh new file mode 100644 index 00000000..2c47ec24 --- /dev/null +++ b/jacoco-maven-plugin.test/it/it-site-failsafe/verify.bsh @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Evgeny Mandrikov - initial API and implementation + * + *******************************************************************************/ +import java.io.*; +import org.codehaus.plexus.util.*; + +String projectReportsPage = FileUtils.fileRead( new File( basedir, "target/site/project-reports.html" ) ); +if ( projectReportsPage.indexOf( "JaCoCo Test Coverage Report." ) < 0 ) { + throw new RuntimeException( "project-reports.html does not contain link to JaCoCo report" ); +} + +File htmlReportFile = new File( basedir, "target/site/jacoco/index.html" ); +if ( !htmlReportFile.isFile() ) { + throw new RuntimeException( "HTML report was not created" ); +} + +File xmlReportFile = new File( basedir, "target/site/jacoco/jacoco.xml" ); +if ( !xmlReportFile.isFile() ) { + throw new RuntimeException( "XML report was not created" ); +} + +File csvReportFile = new File( basedir, "target/site/jacoco/jacoco.csv" ); +if ( !csvReportFile.isFile() ) { + throw new RuntimeException( "CSV report was not created" ); +} + +if ( projectReportsPage.indexOf( "JaCoCo IT Coverage Report." ) < 0 ) { + throw new RuntimeException( "project-reports.html does not contain link to JaCoCo Integration report" ); +} + +File htmlReportFile = new File( basedir, "target/site/jacoco-it/index.html" ); +if ( !htmlReportFile.isFile() ) { + throw new RuntimeException( "Integration HTML report was not created" ); +} + +File xmlReportFile = new File( basedir, "target/site/jacoco-it/jacoco.xml" ); +if ( !xmlReportFile.isFile() ) { + throw new RuntimeException( "Integration XML report was not created" ); +} + +File csvReportFile = new File( basedir, "target/site/jacoco-it/jacoco.csv" ); +if ( !csvReportFile.isFile() ) { + throw new RuntimeException( "Integration CSV report was not created" ); +} diff --git a/jacoco-maven-plugin/src/org/jacoco/maven/AbstractAgentMojo.java b/jacoco-maven-plugin/src/org/jacoco/maven/AbstractAgentMojo.java new file mode 100644 index 00000000..bcc1de73 --- /dev/null +++ b/jacoco-maven-plugin/src/org/jacoco/maven/AbstractAgentMojo.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Evgeny Mandrikov - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.maven; + +import java.io.File; +import java.util.Map; +import java.util.Properties; +import org.apache.maven.artifact.Artifact; +import org.codehaus.plexus.util.StringUtils; +import org.jacoco.core.runtime.AgentOptions; + +/** + * + * @author Mirko Friedenhagen + */ +public abstract class AbstractAgentMojo extends AbstractJacocoMojo { + + /** + * Name of the JaCoCo Agent artifact. + */ + protected static final String AGENT_ARTIFACT_NAME = "org.jacoco:org.jacoco.agent"; + /** + * Name of the property used in maven-osgi-test-plugin. + */ + protected static final String TYCHO_ARG_LINE = "tycho.testArgLine"; + /** + * Name of the property used in maven-surefire-plugin. + */ + protected static final String SUREFIRE_ARG_LINE = "argLine"; + /** + * Map of plugin artifacts. + * + * @parameter expression="${plugin.artifactMap}" + * @required + * @readonly + */ + protected Map<String, Artifact> pluginArtifactMap; + /** + * Allows to specify property which will contains settings for JaCoCo Agent. + * If not specified, then "argLine" would be used for "jar" packaging and + * "tycho.testArgLine" for "eclipse-test-plugin". + * + * @parameter expression="${jacoco.propertyName}" + */ + protected String propertyName; + /** + * If set to true and the execution data file already exists, coverage data + * is appended to the existing file. If set to false, an existing execution + * data file will be replaced. + * + * @parameter expression="${jacoco.append}" + */ + protected Boolean append; + /** + * 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. + * + * @parameter expression="${jacoco.exclClassLoaders}" + */ + protected String exclClassLoaders; + /** + * A session identifier that is written with the execution data. Without + * this parameter a random identifier is created by the agent. + * + * @parameter expression="${jacoco.sessionId}" + */ + protected String sessionId; + /** + * If set to true coverage data will be written on VM shutdown. + * + * @parameter expression="${jacoco.dumpOnExit}" + */ + protected Boolean dumpOnExit; + /** + * Output method to use for writing coverage data. Valid options are: + * <ul> + * <li>file: At VM termination execution data is written to the file + * specified in the {@link #destfile}.</li> + * <li>tcpserver: The agent listens for incoming connections on the TCP port + * specified by the {@link #address} and {@link #port}. Execution data is + * written to this TCP connection.</li> + * <li>tcpclient: At startup the agent connects to the TCP port specified by + * the {@link #address} and {@link #port}. Execution data is written to this + * TCP connection.</li> + * <li>none: Do not produce any output.</li> + * </ul> + * + * @parameter expression="${jacoco.output}" + */ + protected String output; + /** + * IP address or hostname to bind to when the output method is tcpserver or + * connect to when the output method is tcpclient. In tcpserver mode the + * value "*" causes the agent to accept connections on any local address. + * + * @parameter expression="${jacoco.address}" + */ + protected String address; + /** + * Port to bind to when the output method is tcpserver or connect to when + * the output method is tcpclient. In tcpserver mode the port must be + * available, which means that if multiple JaCoCo agents should run on the + * same machine, different ports have to be specified. + * + * @parameter expression="${jacoco.port}" + */ + protected Integer port; + /** + * If a directory is specified for this parameter the JaCoCo agent dumps all + * class files it processes to the given location. This can be useful for + * debugging purposes or in case of dynamically created classes for example + * when scripting engines are used. + * + * @parameter expression="${jacoco.classDumpDir}" + */ + protected File classDumpDir; + /** + * If set to true the agent exposes functionality via JMX. + * + * @parameter expression="${jacoco.jmx}" + */ + protected Boolean jmx; + + @Override + public void executeMojo() { + final String name = getEffectivePropertyName(); + final Properties projectProperties = getProject().getProperties(); + final String oldValue = projectProperties.getProperty(name); + final String newValue = createAgentOptions().prependVMArguments( + oldValue, getAgentJarFile()); + getLog().info(name + " set to " + newValue); + projectProperties.setProperty(name, newValue); + } + + protected File getAgentJarFile() { + final Artifact jacocoAgentArtifact = pluginArtifactMap.get( + AGENT_ARTIFACT_NAME); + return jacocoAgentArtifact.getFile(); + } + + protected AgentOptions createAgentOptions() { + final AgentOptions agentOptions = new AgentOptions(); + agentOptions.setDestfile(getDestFile().getAbsolutePath()); + if (append != null) { + agentOptions.setAppend(append.booleanValue()); + } + if (getIncludes() != null && !getIncludes().isEmpty()) { + final String agentIncludes = StringUtils.join( + getIncludes().iterator(), ":"); + agentOptions.setIncludes(agentIncludes); + } + if (getExcludes() != null && !getExcludes().isEmpty()) { + final String agentExcludes = StringUtils.join( + getExcludes().iterator(), ":"); + agentOptions.setExcludes(agentExcludes); + } + if (exclClassLoaders != null) { + agentOptions.setExclClassloader(exclClassLoaders); + } + if (sessionId != null) { + agentOptions.setSessionId(sessionId); + } + if (dumpOnExit != null) { + agentOptions.setDumpOnExit(dumpOnExit.booleanValue()); + } + if (output != null) { + agentOptions.setOutput(output); + } + if (address != null) { + agentOptions.setAddress(address); + } + if (port != null) { + agentOptions.setPort(port.intValue()); + } + if (classDumpDir != null) { + agentOptions.setClassDumpDir(classDumpDir.getAbsolutePath()); + } + if (jmx != null) { + agentOptions.setJmx(jmx.booleanValue()); + } + return agentOptions; + } + + protected String getEffectivePropertyName() { + if (isPropertyNameSpecified()) { + return propertyName; + } + if (isEclipseTestPluginPackaging()) { + return TYCHO_ARG_LINE; + } + return SUREFIRE_ARG_LINE; + } + + protected boolean isPropertyNameSpecified() { + return propertyName != null && !"".equals(propertyName); + } + + protected boolean isEclipseTestPluginPackaging() { + return "eclipse-test-plugin".equals(getProject().getPackaging()); + } + + /** + * @return the destFile + */ + protected abstract File getDestFile(); + +} diff --git a/jacoco-maven-plugin/src/org/jacoco/maven/AbstractReportMojo.java b/jacoco-maven-plugin/src/org/jacoco/maven/AbstractReportMojo.java new file mode 100644 index 00000000..19451122 --- /dev/null +++ b/jacoco-maven-plugin/src/org/jacoco/maven/AbstractReportMojo.java @@ -0,0 +1,298 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Evgeny Mandrikov - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.maven; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import org.apache.maven.doxia.siterenderer.Renderer; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.project.MavenProject; +import org.apache.maven.reporting.AbstractMavenReport; +import org.apache.maven.reporting.MavenReportException; +import org.jacoco.core.analysis.IBundleCoverage; +import org.jacoco.core.analysis.ICoverageNode; +import org.jacoco.core.tools.ExecFileLoader; +import org.jacoco.core.data.ExecutionDataStore; +import org.jacoco.core.data.SessionInfoStore; +import org.jacoco.report.FileMultiReportOutput; +import org.jacoco.report.IReportGroupVisitor; +import org.jacoco.report.IReportVisitor; +import org.jacoco.report.ISourceFileLocator; +import org.jacoco.report.MultiReportVisitor; +import org.jacoco.report.csv.CSVFormatter; +import org.jacoco.report.html.HTMLFormatter; +import org.jacoco.report.xml.XMLFormatter; + +/** + * + * @author Mirko Friedenhagen + */ +public abstract class AbstractReportMojo extends AbstractMavenReport { + + /** + * Encoding of the generated reports. + * + * @parameter expression="${project.reporting.outputEncoding}" + * default-value="UTF-8" + */ + protected String outputEncoding; + /** + * Encoding of the source files. + * + * @parameter expression="${project.build.sourceEncoding}" + * default-value="UTF-8" + */ + protected String sourceEncoding; + /** + * A list of class files to include in the report. May use wildcard + * characters (* and ?). When not specified everything will be included. + * + * @parameter + */ + protected List<String> includes; + /** + * A list of class files to exclude from the report. May use wildcard + * characters (* and ?). When not specified nothing will be excluded. + * + * @parameter + */ + protected List<String> excludes; + /** + * Flag used to suppress execution. + * + * @parameter expression="${jacoco.skip}" default-value="false" + */ + protected boolean skip; + /** + * Maven project. + * + * @parameter expression="${project}" + * @readonly + */ + protected MavenProject project; + /** + * Doxia Site Renderer. + * + * @component + */ + protected Renderer siteRenderer; + protected SessionInfoStore sessionInfoStore; + protected ExecutionDataStore executionDataStore; + + public abstract String getOutputName(); + + public abstract String getName(final Locale locale); + + public String getDescription(final Locale locale) { + return getName(locale) + " Coverage Report."; + } + + @Override + public boolean isExternalReport() { + return true; + } + + @Override + protected abstract String getOutputDirectory(); + + @Override + protected MavenProject getProject() { + return project; + } + + @Override + protected Renderer getSiteRenderer() { + return siteRenderer; + } + + /** + * Returns the list of class files to include in the report. + * + * @return class files to include, may contain wildcard characters + */ + protected List<String> getIncludes() { + return includes; + } + + /** + * Returns the list of class files to exclude from the report. + * + * @return class files to exclude, may contain wildcard characters + */ + protected List<String> getExcludes() { + return excludes; + } + + @Override + public abstract void setReportOutputDirectory(final File reportOutputDirectory); + + @Override + public boolean canGenerateReport() { + if ("pom".equals(project.getPackaging())) { + getLog().info("Skipping JaCoCo for project with packaging type 'pom'"); + return false; + } + if (skip) { + getLog().info("Skipping JaCoCo execution"); + return false; + } + if (!getDataFile().exists()) { + getLog().info("Skipping JaCoCo execution due to missing execution data file"); + return false; + } + return true; + } + + /** + * This method is called when the report generation is invoked directly as a + * standalone Mojo. + */ + @Override + public void execute() throws MojoExecutionException { + if (!canGenerateReport()) { + return; + } + try { + executeReport(Locale.getDefault()); + } catch (final MavenReportException e) { + throw new MojoExecutionException("An error has occurred in " + + getName(Locale.ENGLISH) + " report generation.", e); + } + } + + @Override + protected void executeReport(final Locale locale) throws MavenReportException { + loadExecutionData(); + try { + final IReportVisitor visitor = createVisitor(locale); + visitor.visitInfo(sessionInfoStore.getInfos(), + executionDataStore.getContents()); + createReport(visitor); + visitor.visitEnd(); + } catch (final IOException e) { + throw new MavenReportException("Error while creating report: " + + e.getMessage(), e); + } + } + + protected void loadExecutionData() throws MavenReportException { + final ExecFileLoader loader = new ExecFileLoader(); + try { + loader.load(getDataFile()); + } catch (final IOException e) { + throw new MavenReportException("Unable to read execution data file " + + getDataFile() + ": " + e.getMessage(), e); + } + sessionInfoStore = loader.getSessionInfoStore(); + executionDataStore = loader.getExecutionDataStore(); + } + + protected void createReport(final IReportGroupVisitor visitor) throws IOException { + final FileFilter fileFilter = new FileFilter(this.getIncludes(), + this.getExcludes()); + final BundleCreator creator = new BundleCreator(this.getProject(), + fileFilter); + final IBundleCoverage bundle = creator.createBundle(executionDataStore); + final SourceFileCollection locator = new SourceFileCollection( + getCompileSourceRoots(), sourceEncoding); + checkForMissingDebugInformation(bundle); + visitor.visitBundle(bundle, locator); + } + + protected void checkForMissingDebugInformation(final ICoverageNode node) { + if (node.getClassCounter().getTotalCount() > 0 + && node.getLineCounter().getTotalCount() == 0) { + getLog().warn("To enable source code annotation class files have to be compiled with debug information."); + } + } + + protected IReportVisitor createVisitor(final Locale locale) throws IOException { + final List<IReportVisitor> visitors = new ArrayList<IReportVisitor>(); + getOutputDirectoryFile().mkdirs(); + final XMLFormatter xmlFormatter = new XMLFormatter(); + xmlFormatter.setOutputEncoding(outputEncoding); + visitors.add(xmlFormatter.createVisitor(new FileOutputStream( + new File(getOutputDirectoryFile(), "jacoco.xml")))); + final CSVFormatter csvFormatter = new CSVFormatter(); + csvFormatter.setOutputEncoding(outputEncoding); + visitors.add(csvFormatter.createVisitor(new FileOutputStream( + new File(getOutputDirectoryFile(), "jacoco.csv")))); + final HTMLFormatter htmlFormatter = new HTMLFormatter(); + htmlFormatter.setOutputEncoding(outputEncoding); + htmlFormatter.setLocale(locale); + visitors.add(htmlFormatter.createVisitor( + new FileMultiReportOutput(getOutputDirectoryFile()))); + return new MultiReportVisitor(visitors); + } + + protected File resolvePath(final String path) { + File file = new File(path); + if (!file.isAbsolute()) { + file = new File(getProject().getBasedir(), path); + } + return file; + } + + protected List<File> getCompileSourceRoots() { + final List<File> result = new ArrayList<File>(); + for (final Object path : getProject().getCompileSourceRoots()) { + result.add(resolvePath((String) path)); + } + return result; + } + + private static class SourceFileCollection implements ISourceFileLocator { + + private final List<File> sourceRoots; + private final String encoding; + + public SourceFileCollection(final List<File> sourceRoots, + final String encoding) { + this.sourceRoots = sourceRoots; + this.encoding = encoding; + } + + public Reader getSourceFile(final String packageName, + final String fileName) throws IOException { + final String r; + if (packageName.length() > 0) { + r = packageName + '/' + fileName; + } else { + r = fileName; + } + for (final File sourceRoot : sourceRoots) { + final File file = new File(sourceRoot, r); + if (file.exists() && file.isFile()) { + return new InputStreamReader(new FileInputStream(file), + encoding); + } + } + return null; + } + + public int getTabWidth() { + return 4; + } + } + + abstract protected File getDataFile(); + + abstract protected File getOutputDirectoryFile(); + +} diff --git a/jacoco-maven-plugin/src/org/jacoco/maven/AgentITMojo.java b/jacoco-maven-plugin/src/org/jacoco/maven/AgentITMojo.java new file mode 100644 index 00000000..b62e0885 --- /dev/null +++ b/jacoco-maven-plugin/src/org/jacoco/maven/AgentITMojo.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Evgeny Mandrikov - initial API and implementation + * Kyle Lieber - implementation of CheckMojo + * + *******************************************************************************/ +package org.jacoco.maven; + +import java.io.File; + +/** + * Prepares a property pointing to the JaCoCo runtime agent that can be passed as a VM argument to the application under test for + * integration tests. Depending on the project packaging type by default a property with the following name is set: + * <ul> + * <li>tycho.testArgLine for packaging type eclipse-test-plugin and</li> + * <li>argLine otherwise.</li> + * </ul> + * Resulting coverage information is collected during execution and by default written to a file when the process terminates. + * + * @phase pre-integration-test + * @goal prepare-agent-integration + * @requiresProject true + * @requiresDependencyResolution runtime + * @threadSafe + * @since 0.6.4 + */ +public class AgentITMojo extends AbstractAgentMojo { + + /** + * Path to the output file for execution data. + * + * @parameter expression="${jacoco.destFile}" + * default-value="${project.build.directory}/jacoco-it.exec" + */ + private File destFile; + + /** + * @return the destFile + */ + protected File getDestFile() { + return destFile; + } + +} diff --git a/jacoco-maven-plugin/src/org/jacoco/maven/AgentMojo.java b/jacoco-maven-plugin/src/org/jacoco/maven/AgentMojo.java index 2697158a..0ba0428a 100644 --- a/jacoco-maven-plugin/src/org/jacoco/maven/AgentMojo.java +++ b/jacoco-maven-plugin/src/org/jacoco/maven/AgentMojo.java @@ -12,12 +12,6 @@ package org.jacoco.maven; import java.io.File; -import java.util.Map; -import java.util.Properties; - -import org.apache.maven.artifact.Artifact; -import org.codehaus.plexus.util.StringUtils; -import org.jacoco.core.runtime.AgentOptions; /** * <p> @@ -53,40 +47,7 @@ import org.jacoco.core.runtime.AgentOptions; * @threadSafe * @since 0.5.3 */ -public class AgentMojo extends AbstractJacocoMojo { - - /** - * Name of the JaCoCo Agent artifact. - */ - private static final String AGENT_ARTIFACT_NAME = "org.jacoco:org.jacoco.agent"; - - /** - * Name of the property used in maven-osgi-test-plugin. - */ - private static final String TYCHO_ARG_LINE = "tycho.testArgLine"; - - /** - * Name of the property used in maven-surefire-plugin. - */ - private static final String SUREFIRE_ARG_LINE = "argLine"; - - /** - * Map of plugin artifacts. - * - * @parameter expression="${plugin.artifactMap}" - * @required - * @readonly - */ - private Map<String, Artifact> pluginArtifactMap; - - /** - * Allows to specify property which will contains settings for JaCoCo Agent. - * If not specified, then "argLine" would be used for "jar" packaging and - * "tycho.testArgLine" for "eclipse-test-plugin". - * - * @parameter expression="${jacoco.propertyName}" - */ - private String propertyName; +public class AgentMojo extends AbstractAgentMojo { /** * Path to the output file for execution data. @@ -97,171 +58,10 @@ public class AgentMojo extends AbstractJacocoMojo { private File destFile; /** - * If set to true and the execution data file already exists, coverage data - * is appended to the existing file. If set to false, an existing execution - * data file will be replaced. - * - * @parameter expression="${jacoco.append}" - */ - private Boolean append; - - /** - * 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. - * - * @parameter expression="${jacoco.exclClassLoaders}" - */ - private String exclClassLoaders; - - /** - * A session identifier that is written with the execution data. Without - * this parameter a random identifier is created by the agent. - * - * @parameter expression="${jacoco.sessionId}" - */ - private String sessionId; - - /** - * If set to true coverage data will be written on VM shutdown. - * - * @parameter expression="${jacoco.dumpOnExit}" - */ - private Boolean dumpOnExit; - - /** - * Output method to use for writing coverage data. Valid options are: - * <ul> - * <li>file: At VM termination execution data is written to the file - * specified in the {@link #destfile}.</li> - * <li>tcpserver: The agent listens for incoming connections on the TCP port - * specified by the {@link #address} and {@link #port}. Execution data is - * written to this TCP connection.</li> - * <li>tcpclient: At startup the agent connects to the TCP port specified by - * the {@link #address} and {@link #port}. Execution data is written to this - * TCP connection.</li> - * <li>none: Do not produce any output.</li> - * </ul> - * - * @parameter expression="${jacoco.output}" - */ - private String output; - - /** - * IP address or hostname to bind to when the output method is tcpserver or - * connect to when the output method is tcpclient. In tcpserver mode the - * value "*" causes the agent to accept connections on any local address. - * - * @parameter expression="${jacoco.address}" - */ - private String address; - - /** - * Port to bind to when the output method is tcpserver or connect to when - * the output method is tcpclient. In tcpserver mode the port must be - * available, which means that if multiple JaCoCo agents should run on the - * same machine, different ports have to be specified. - * - * @parameter expression="${jacoco.port}" - */ - private Integer port; - - /** - * If a directory is specified for this parameter the JaCoCo agent dumps all - * class files it processes to the given location. This can be useful for - * debugging purposes or in case of dynamically created classes for example - * when scripting engines are used. - * - * @parameter expression="${jacoco.classDumpDir}" + * @return the destFile */ - private File classDumpDir; - - /** - * If set to true the agent exposes functionality via JMX. - * - * @parameter expression="${jacoco.jmx}" - */ - private Boolean jmx; - - @Override - public void executeMojo() { - final String name = getEffectivePropertyName(); - final Properties projectProperties = getProject().getProperties(); - final String oldValue = projectProperties.getProperty(name); - final String newValue = createAgentOptions().prependVMArguments( - oldValue, getAgentJarFile()); - getLog().info(name + " set to " + newValue); - projectProperties.setProperty(name, newValue); - } - - private File getAgentJarFile() { - final Artifact jacocoAgentArtifact = pluginArtifactMap - .get(AGENT_ARTIFACT_NAME); - return jacocoAgentArtifact.getFile(); - } - - private AgentOptions createAgentOptions() { - final AgentOptions agentOptions = new AgentOptions(); - agentOptions.setDestfile(destFile.getAbsolutePath()); - if (append != null) { - agentOptions.setAppend(append.booleanValue()); - } - if (getIncludes() != null && !getIncludes().isEmpty()) { - final String agentIncludes = StringUtils.join(getIncludes() - .iterator(), ":"); - agentOptions.setIncludes(agentIncludes); - } - if (getExcludes() != null && !getExcludes().isEmpty()) { - final String agentExcludes = StringUtils.join(getExcludes() - .iterator(), ":"); - agentOptions.setExcludes(agentExcludes); - } - if (exclClassLoaders != null) { - agentOptions.setExclClassloader(exclClassLoaders); - } - if (sessionId != null) { - agentOptions.setSessionId(sessionId); - } - if (dumpOnExit != null) { - agentOptions.setDumpOnExit(dumpOnExit.booleanValue()); - } - if (output != null) { - agentOptions.setOutput(output); - } - if (address != null) { - agentOptions.setAddress(address); - } - if (port != null) { - agentOptions.setPort(port.intValue()); - } - if (classDumpDir != null) { - agentOptions.setClassDumpDir(classDumpDir.getAbsolutePath()); - } - if (jmx != null) { - agentOptions.setJmx(jmx.booleanValue()); - } - return agentOptions; - } - - private String getEffectivePropertyName() { - if (isPropertyNameSpecified()) { - return propertyName; - } - if (isEclipseTestPluginPackaging()) { - return TYCHO_ARG_LINE; - } - return SUREFIRE_ARG_LINE; - } - - private boolean isPropertyNameSpecified() { - return propertyName != null && !"".equals(propertyName); - } - - private boolean isEclipseTestPluginPackaging() { - return "eclipse-test-plugin".equals(getProject().getPackaging()); + protected File getDestFile() { + return destFile; } } diff --git a/jacoco-maven-plugin/src/org/jacoco/maven/ReportITMojo.java b/jacoco-maven-plugin/src/org/jacoco/maven/ReportITMojo.java new file mode 100644 index 00000000..79d0186c --- /dev/null +++ b/jacoco-maven-plugin/src/org/jacoco/maven/ReportITMojo.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Evgeny Mandrikov - initial API and implementation + * Kyle Lieber - implementation of CheckMojo + * + *******************************************************************************/ +package org.jacoco.maven; + +import java.io.File; +import java.util.Locale; + +/** + * Creates a code coverage report for integration tests of a single project in multiple formats + * (HTML, XML, and CSV). + * + * @phase verify + * @goal report-integration + * @requiresProject true + * @threadSafe + * @since 0.6.4 + */ +public class ReportITMojo extends AbstractReportMojo { + + /** + * Output directory for the reports. Note that this parameter is only + * relevant if the goal is run from the command line or from the default + * build lifecycle. If the goal is run indirectly as part of a site + * generation, the output directory configured in the Maven Site Plugin is + * used instead. + * + * @parameter default-value="${project.reporting.outputDirectory}/jacoco-it" + */ + private File outputDirectory; + + /** + * File with execution data. + * + * @parameter default-value="${project.build.directory}/jacoco-it.exec" + */ + private File dataFile; + + @Override + protected String getOutputDirectory() { + return outputDirectory.getAbsolutePath(); + } + + @Override + public void setReportOutputDirectory(final File reportOutputDirectory) { + if (reportOutputDirectory != null + && !reportOutputDirectory.getAbsolutePath().endsWith("jacoco-it")) { + outputDirectory = new File(reportOutputDirectory, "jacoco-it"); + } else { + outputDirectory = reportOutputDirectory; + } + } + + @Override + protected File getDataFile() { + return dataFile; + } + + @Override + protected File getOutputDirectoryFile() { + return outputDirectory; + } + + public String getOutputName() { + return "jacoco-it/index"; + } + + public String getName(final Locale locale) { + return "JaCoCo IT"; + } +} diff --git a/jacoco-maven-plugin/src/org/jacoco/maven/ReportMojo.java b/jacoco-maven-plugin/src/org/jacoco/maven/ReportMojo.java index db4cf85c..819b1fa6 100644 --- a/jacoco-maven-plugin/src/org/jacoco/maven/ReportMojo.java +++ b/jacoco-maven-plugin/src/org/jacoco/maven/ReportMojo.java @@ -7,51 +7,24 @@ * * Contributors: * Evgeny Mandrikov - initial API and implementation - * Kyle Lieber - implementation of CheckMojo * *******************************************************************************/ package org.jacoco.maven; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.util.ArrayList; -import java.util.List; import java.util.Locale; -import org.apache.maven.doxia.siterenderer.Renderer; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.project.MavenProject; -import org.apache.maven.reporting.AbstractMavenReport; -import org.apache.maven.reporting.MavenReportException; -import org.jacoco.core.analysis.IBundleCoverage; -import org.jacoco.core.analysis.ICoverageNode; -import org.jacoco.core.data.ExecutionDataStore; -import org.jacoco.core.data.SessionInfoStore; -import org.jacoco.core.tools.ExecFileLoader; -import org.jacoco.report.FileMultiReportOutput; -import org.jacoco.report.IReportGroupVisitor; -import org.jacoco.report.IReportVisitor; -import org.jacoco.report.ISourceFileLocator; -import org.jacoco.report.MultiReportVisitor; -import org.jacoco.report.csv.CSVFormatter; -import org.jacoco.report.html.HTMLFormatter; -import org.jacoco.report.xml.XMLFormatter; - /** - * Creates a code coverage report for a single project in multiple formats + * Creates a code coverage report for tests of a single project in multiple formats * (HTML, XML, and CSV). - * + * * @phase verify * @goal report * @requiresProject true * @threadSafe * @since 0.5.3 */ -public class ReportMojo extends AbstractMavenReport { +public class ReportMojo extends AbstractReportMojo { /** * Output directory for the reports. Note that this parameter is only @@ -59,127 +32,24 @@ public class ReportMojo extends AbstractMavenReport { * build lifecycle. If the goal is run indirectly as part of a site * generation, the output directory configured in the Maven Site Plugin is * used instead. - * + * * @parameter default-value="${project.reporting.outputDirectory}/jacoco" */ private File outputDirectory; /** - * Encoding of the generated reports. - * - * @parameter expression="${project.reporting.outputEncoding}" - * default-value="UTF-8" - */ - private String outputEncoding; - - /** - * Encoding of the source files. - * - * @parameter expression="${project.build.sourceEncoding}" - * default-value="UTF-8" - */ - private String sourceEncoding; - - /** * File with execution data. - * + * * @parameter default-value="${project.build.directory}/jacoco.exec" */ private File dataFile; - /** - * A list of class files to include in the report. May use wildcard - * characters (* and ?). When not specified everything will be included. - * - * @parameter - */ - private List<String> includes; - - /** - * A list of class files to exclude from the report. May use wildcard - * characters (* and ?). When not specified nothing will be excluded. - * - * @parameter - */ - private List<String> excludes; - - /** - * Flag used to suppress execution. - * - * @parameter expression="${jacoco.skip}" default-value="false" - */ - private boolean skip; - - /** - * Maven project. - * - * @parameter expression="${project}" - * @readonly - */ - private MavenProject project; - - /** - * Doxia Site Renderer. - * - * @component - */ - private Renderer siteRenderer; - - private SessionInfoStore sessionInfoStore; - - private ExecutionDataStore executionDataStore; - - public String getOutputName() { - return "jacoco/index"; - } - - public String getName(final Locale locale) { - return "JaCoCo"; - } - - public String getDescription(final Locale locale) { - return "JaCoCo Test Coverage Report."; - } - - @Override - public boolean isExternalReport() { - return true; - } - @Override protected String getOutputDirectory() { return outputDirectory.getAbsolutePath(); } @Override - protected MavenProject getProject() { - return project; - } - - @Override - protected Renderer getSiteRenderer() { - return siteRenderer; - } - - /** - * Returns the list of class files to include in the report. - * - * @return class files to include, may contain wildcard characters - */ - protected List<String> getIncludes() { - return includes; - } - - /** - * Returns the list of class files to exclude from the report. - * - * @return class files to exclude, may contain wildcard characters - */ - protected List<String> getExcludes() { - return excludes; - } - - @Override public void setReportOutputDirectory(final File reportOutputDirectory) { if (reportOutputDirectory != null && !reportOutputDirectory.getAbsolutePath().endsWith("jacoco")) { @@ -190,164 +60,20 @@ public class ReportMojo extends AbstractMavenReport { } @Override - public boolean canGenerateReport() { - if ("pom".equals(project.getPackaging())) { - getLog().info( - "Skipping JaCoCo for project with packaging type 'pom'"); - return false; - } - if (skip) { - getLog().info("Skipping JaCoCo execution"); - return false; - } - if (!dataFile.exists()) { - getLog().info( - "Skipping JaCoCo execution due to missing execution data file"); - return false; - } - return true; + protected File getDataFile() { + return dataFile; } - /** - * This method is called when the report generation is invoked directly as a - * standalone Mojo. - */ @Override - public void execute() throws MojoExecutionException { - if (!canGenerateReport()) { - return; - } - try { - executeReport(Locale.getDefault()); - } catch (final MavenReportException e) { - throw new MojoExecutionException("An error has occurred in " - + getName(Locale.ENGLISH) + " report generation.", e); - } - } - - @Override - protected void executeReport(final Locale locale) - throws MavenReportException { - loadExecutionData(); - try { - final IReportVisitor visitor = createVisitor(locale); - visitor.visitInfo(sessionInfoStore.getInfos(), - executionDataStore.getContents()); - createReport(visitor); - visitor.visitEnd(); - } catch (final IOException e) { - throw new MavenReportException("Error while creating report: " - + e.getMessage(), e); - } - } - - private void loadExecutionData() throws MavenReportException { - final ExecFileLoader loader = new ExecFileLoader(); - try { - loader.load(dataFile); - } catch (final IOException e) { - throw new MavenReportException( - "Unable to read execution data file " + dataFile + ": " - + e.getMessage(), e); - } - sessionInfoStore = loader.getSessionInfoStore(); - executionDataStore = loader.getExecutionDataStore(); - } - - private void createReport(final IReportGroupVisitor visitor) - throws IOException { - final FileFilter fileFilter = new FileFilter(this.getIncludes(), - this.getExcludes()); - final BundleCreator creator = new BundleCreator(this.getProject(), - fileFilter); - final IBundleCoverage bundle = creator.createBundle(executionDataStore); - - final SourceFileCollection locator = new SourceFileCollection( - getCompileSourceRoots(), sourceEncoding); - checkForMissingDebugInformation(bundle); - visitor.visitBundle(bundle, locator); - } - - private void checkForMissingDebugInformation(final ICoverageNode node) { - if (node.getClassCounter().getTotalCount() > 0 - && node.getLineCounter().getTotalCount() == 0) { - getLog().warn( - "To enable source code annotation class files have to be compiled with debug information."); - } - } - - private IReportVisitor createVisitor(final Locale locale) - throws IOException { - final List<IReportVisitor> visitors = new ArrayList<IReportVisitor>(); - - outputDirectory.mkdirs(); - - final XMLFormatter xmlFormatter = new XMLFormatter(); - xmlFormatter.setOutputEncoding(outputEncoding); - visitors.add(xmlFormatter.createVisitor(new FileOutputStream(new File( - outputDirectory, "jacoco.xml")))); - - final CSVFormatter csvFormatter = new CSVFormatter(); - csvFormatter.setOutputEncoding(outputEncoding); - visitors.add(csvFormatter.createVisitor(new FileOutputStream(new File( - outputDirectory, "jacoco.csv")))); - - final HTMLFormatter htmlFormatter = new HTMLFormatter(); - htmlFormatter.setOutputEncoding(outputEncoding); - htmlFormatter.setLocale(locale); - visitors.add(htmlFormatter.createVisitor(new FileMultiReportOutput( - outputDirectory))); - - return new MultiReportVisitor(visitors); + protected File getOutputDirectoryFile() { + return outputDirectory; } - private static class SourceFileCollection implements ISourceFileLocator { - - private final List<File> sourceRoots; - private final String encoding; - - public SourceFileCollection(final List<File> sourceRoots, - final String encoding) { - this.sourceRoots = sourceRoots; - this.encoding = encoding; - } - - public Reader getSourceFile(final String packageName, - final String fileName) throws IOException { - final String r; - if (packageName.length() > 0) { - r = packageName + '/' + fileName; - } else { - r = fileName; - } - for (final File sourceRoot : sourceRoots) { - final File file = new File(sourceRoot, r); - if (file.exists() && file.isFile()) { - return new InputStreamReader(new FileInputStream(file), - encoding); - } - } - return null; - } - - public int getTabWidth() { - return 4; - } - } - - private File resolvePath(final String path) { - File file = new File(path); - if (!file.isAbsolute()) { - file = new File(getProject().getBasedir(), path); - } - return file; + public String getOutputName() { + return "jacoco/index"; } - private List<File> getCompileSourceRoots() { - final List<File> result = new ArrayList<File>(); - for (final Object path : getProject().getCompileSourceRoots()) { - result.add(resolvePath((String) path)); - } - return result; + public String getName(final Locale locale) { + return "JaCoCo Test"; } } diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml index 8a11a00c..0c1fdc88 100644 --- a/org.jacoco.build/pom.xml +++ b/org.jacoco.build/pom.xml @@ -510,7 +510,7 @@ </goals> <configuration> <target> - <fileset dir="${basedir}" includes="**/*.java,**/*.xml,**/*.bsh" excludes="target/**" id="missinglicense.fileset"> + <fileset dir="${basedir}" includes="**/*.java,**/*.xml,**/*.bsh" excludes="target/**,nb-configuration.xml" id="missinglicense.fileset"> <not> <and> <contains text="Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors"/> diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html index cc8c65f6..8e1189e9 100644 --- a/org.jacoco.doc/docroot/doc/changes.html +++ b/org.jacoco.doc/docroot/doc/changes.html @@ -28,6 +28,8 @@ (GitHub #126).</li> <li>Additional list of source files for every package in HTML report (GitHub #142).</li> + <li>Additional report-integration and prepare-agent-integration goals for + integration tests. (GitHub #152).</li> </ul> <h3>Fixed Bugs</h3> |