Created
January 10, 2013 08:19
-
-
Save KetothXupack/4500400 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/jacoco-maven-plugin/src/org/jacoco/maven/ReportMojo.java b/jacoco-maven-plugin/src/org/jacoco/maven/ReportMojo.java | |
index 06ef04e..68834c8 100644 | |
--- a/jacoco-maven-plugin/src/org/jacoco/maven/ReportMojo.java | |
+++ b/jacoco-maven-plugin/src/org/jacoco/maven/ReportMojo.java | |
@@ -7,21 +7,11 @@ | |
* | |
* Contributors: | |
* Evgeny Mandrikov - initial API and implementation | |
- * Kyle Lieber - implementation of CheckMojo | |
- * | |
+ * Kyle Lieber - implementation of CheckMojo | |
+ * Ketoth Xupack - data files merging & report aggregation | |
*******************************************************************************/ | |
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; | |
@@ -30,8 +20,6 @@ import org.apache.maven.reporting.MavenReportException; | |
import org.jacoco.core.analysis.IBundleCoverage; | |
import org.jacoco.core.analysis.ICoverageNode; | |
import org.jacoco.core.data.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; | |
@@ -41,10 +29,21 @@ import org.jacoco.report.csv.CSVFormatter; | |
import org.jacoco.report.html.HTMLFormatter; | |
import org.jacoco.report.xml.XMLFormatter; | |
+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; | |
+ | |
/** | |
* Creates a code coverage report for a single project in multiple formats | |
* (HTML, XML, and CSV). | |
- * | |
+ * | |
+ * @requiresDependencyResolution compile | |
* @goal report | |
* @requiresProject true | |
* @threadSafe | |
@@ -57,14 +56,14 @@ 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" | |
*/ | |
@@ -72,7 +71,7 @@ public class ReportMojo extends AbstractMavenReport { | |
/** | |
* Encoding of the source files. | |
- * | |
+ * | |
* @parameter expression="${project.build.sourceEncoding}" | |
* default-value="UTF-8" | |
*/ | |
@@ -80,15 +79,50 @@ public class ReportMojo extends AbstractMavenReport { | |
/** | |
* File with execution data. | |
- * | |
+ * | |
* @parameter default-value="${project.build.directory}/jacoco.exec" | |
*/ | |
private File dataFile; | |
/** | |
+ * File with execution data. | |
+ * | |
+ * @parameter | |
+ */ | |
+ private String[] dataFiles; | |
+ | |
+ /** | |
+ * A list of data files to aggregate. | |
+ * | |
+ * @parameter default-value="" | |
+ */ | |
+ private String postfix; | |
+ | |
+ /** | |
+ * {@code true} to fail on missing data files in {@code dataFiles} list. | |
+ * | |
+ * @parameter | |
+ */ | |
+ private boolean strict = false; | |
+ | |
+ /** | |
+ * Do not produce report for each module in multimodule project. | |
+ * | |
+ * @parameter | |
+ */ | |
+ private boolean skipModule = false; | |
+ | |
+ /** | |
+ * Flg for aggregating each module report in multimodule environment. | |
+ * | |
+ * @parameter | |
+ */ | |
+ private boolean aggregate = true; | |
+ | |
+ /** | |
* 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; | |
@@ -96,49 +130,66 @@ public class ReportMojo extends AbstractMavenReport { | |
/** | |
* 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; | |
/** | |
+ * The projects in the reactor for aggregation report. | |
+ * | |
+ * @parameter expression="${reactorProjects}" | |
+ * @readonly | |
+ */ | |
+ private List<MavenProject> reactorProjects; | |
+ | |
+ /** | |
* Doxia Site Renderer. | |
- * | |
+ * | |
* @component | |
*/ | |
private Renderer siteRenderer; | |
- private SessionInfoStore sessionInfoStore; | |
- | |
- private ExecutionDataStore executionDataStore; | |
- | |
public String getOutputName() { | |
- return "jacoco/index"; | |
+ return getRelative() + "/index"; | |
+ } | |
+ | |
+ private String getRelative() { | |
+ return new File(getProject().getReporting().getOutputDirectory()) | |
+ .toURI().relativize(outputDirectory.toURI()).getPath(); | |
} | |
public String getName(final Locale locale) { | |
- return "JaCoCo"; | |
+ return doPostfix("JaCoCo"); | |
} | |
public String getDescription(final Locale locale) { | |
return "JaCoCo Test Coverage Report."; | |
} | |
+ private String doPostfix(final String string) { | |
+ if (postfix == null || "".equals(postfix)) { | |
+ return string; | |
+ } | |
+ | |
+ return string + " (" + postfix + ")"; | |
+ } | |
+ | |
@Override | |
public boolean isExternalReport() { | |
return true; | |
@@ -161,7 +212,7 @@ public class ReportMojo extends AbstractMavenReport { | |
/** | |
* Returns the list of class files to include in the report. | |
- * | |
+ * | |
* @return class files to include, may contain wildcard characters | |
*/ | |
protected List<String> getIncludes() { | |
@@ -170,7 +221,7 @@ public class ReportMojo extends AbstractMavenReport { | |
/** | |
* Returns the list of class files to exclude from the report. | |
- * | |
+ * | |
* @return class files to exclude, may contain wildcard characters | |
*/ | |
protected List<String> getExcludes() { | |
@@ -179,9 +230,8 @@ public class ReportMojo extends AbstractMavenReport { | |
@Override | |
public void setReportOutputDirectory(final File reportOutputDirectory) { | |
- if (reportOutputDirectory != null | |
- && !reportOutputDirectory.getAbsolutePath().endsWith("jacoco")) { | |
- outputDirectory = new File(reportOutputDirectory, "jacoco"); | |
+ if (reportOutputDirectory != null) { | |
+ outputDirectory = new File(reportOutputDirectory, getRelative()); | |
} else { | |
outputDirectory = reportOutputDirectory; | |
} | |
@@ -189,20 +239,23 @@ 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"); | |
+ | |
+ // fallback to old behavior | |
+ if (dataFiles == null) { | |
+ skipModule = false; | |
+ strict = true; | |
+ aggregate = true; | |
+ } | |
+ | |
+ if (dataFiles == null && dataFile != null && !dataFile.exists()) { | |
+ getLog().warn("Skipping JaCoCo execution due to missing execution data file"); | |
return false; | |
} | |
+ | |
return true; | |
} | |
@@ -223,47 +276,129 @@ public class ReportMojo extends AbstractMavenReport { | |
} | |
} | |
+ private String[] getDataFiles() { | |
+ if (dataFiles == null && dataFile != null) { | |
+ return new String[] {dataFile.getPath()}; | |
+ } | |
+ | |
+ return dataFiles; | |
+ } | |
+ | |
@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(); | |
+ executeReport(locale, false); | |
+ if (getProject() != getLastProject()) { | |
+ executeReport(locale, true); | |
+ } | |
} catch (final IOException e) { | |
- throw new MavenReportException("Error while creating report: " | |
- + e.getMessage(), e); | |
+ throw new MavenReportException("Error while creating report: " + e.getMessage(), e); | |
} | |
} | |
- private void loadExecutionData() throws MavenReportException { | |
+ private void executeReport(final Locale locale, final boolean root) | |
+ throws IOException, MavenReportException { | |
+ | |
+ final boolean exec = root ? aggregate : !skipModule; | |
+ if (!exec) { | |
+ return; | |
+ } | |
+ | |
+ final IReportVisitor mainVisitor = createVisitor(locale, outputDirectory); | |
+ boolean visited = false; | |
+ | |
+ final ExecFileLoader loader = loadExecutionData(root); | |
+ mainVisitor.visitInfo(loader.getSessionInfoStore().getInfos(), | |
+ loader.getExecutionDataStore().getContents()); | |
+ | |
+ if (root) { | |
+ final IReportGroupVisitor subProjectVisitor = mainVisitor.visitGroup( | |
+ getRootProject().getName()); | |
+ | |
+ for (final MavenProject child : reactorProjects) { | |
+ visited |= visitProject(loader, subProjectVisitor, child); | |
+ } | |
+ } else { | |
+ visited = visitProject(loader, mainVisitor, getProject()); | |
+ } | |
+ | |
+ if (visited) { | |
+ mainVisitor.visitEnd(); | |
+ } | |
+ } | |
+ | |
+ private MavenProject getRootProject() { | |
+ final MavenProject lastProject = reactorProjects.get(0); | |
+ if (!lastProject.isExecutionRoot()) { | |
+ throw new IllegalStateException(""); | |
+ } | |
+ | |
+ return lastProject; | |
+ } | |
+ | |
+ private MavenProject getLastProject() { | |
+ final int size = reactorProjects.size(); | |
+ return reactorProjects.get(size - 1); | |
+ } | |
+ | |
+ private void loadExecFile(final ExecFileLoader loader, final MavenProject project) | |
+ throws IOException, MavenReportException { | |
+ for (final String dataFile : getDataFiles()) { | |
+ final File file = resolvePath(project, dataFile); | |
+ if (file.exists()) { | |
+ loader.load(file); | |
+ } else { | |
+ if (strict) { | |
+ throw new MavenReportException("File " + file +" exists"); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ private ExecFileLoader loadExecutionData(final boolean root) | |
+ throws MavenReportException { | |
final ExecFileLoader loader = new ExecFileLoader(); | |
try { | |
- loader.load(dataFile); | |
+ if (root) { | |
+ for (final MavenProject project : reactorProjects) { | |
+ loadExecFile(loader, project); | |
+ } | |
+ } else { | |
+ loadExecFile(loader, getProject()); | |
+ } | |
} catch (final IOException e) { | |
- throw new MavenReportException( | |
- "Unable to read execution data file " + dataFile + ": " | |
- + e.getMessage(), e); | |
+ throw new MavenReportException("Unable to read execution data file: " + e.getMessage(), e); | |
} | |
- sessionInfoStore = loader.getSessionInfoStore(); | |
- executionDataStore = loader.getExecutionDataStore(); | |
+ | |
+ return loader; | |
} | |
- private void createReport(final IReportGroupVisitor visitor) | |
+ /** | |
+ * @return {@code true} if project was actually visited | |
+ * @throws IOException | |
+ */ | |
+ private boolean visitProject(final ExecFileLoader loader, final IReportGroupVisitor visitor, | |
+ final MavenProject project) | |
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); | |
+ | |
+ // skip processing modules with "pom" packaging | |
+ if ("pom".equals(project.getPackaging())) { | |
+ return false; | |
+ } | |
+ | |
+ final FileFilter fileFilter = new FileFilter(this.getIncludes(), this.getExcludes()); | |
+ final BundleCreator creator = new BundleCreator(project, fileFilter); | |
+ final IBundleCoverage bundle = creator.createBundle(loader.getExecutionDataStore()); | |
final SourceFileCollection locator = new SourceFileCollection( | |
- getCompileSourceRoots(), sourceEncoding); | |
+ getCompileSourceRoots(project), | |
+ sourceEncoding); | |
+ | |
checkForMissingDebugInformation(bundle); | |
visitor.visitBundle(bundle, locator); | |
+ | |
+ return true; | |
} | |
private void checkForMissingDebugInformation(final ICoverageNode node) { | |
@@ -274,7 +409,7 @@ public class ReportMojo extends AbstractMavenReport { | |
} | |
} | |
- private IReportVisitor createVisitor(final Locale locale) | |
+ private IReportVisitor createVisitor(final Locale locale, final File outputDirectory) | |
throws IOException { | |
final List<IReportVisitor> visitors = new ArrayList<IReportVisitor>(); | |
@@ -313,7 +448,7 @@ public class ReportMojo extends AbstractMavenReport { | |
public Reader getSourceFile(final String packageName, | |
final String fileName) throws IOException { | |
final String r; | |
- if (packageName.length() > 0) { | |
+ if (!packageName.isEmpty()) { | |
r = packageName + '/' + fileName; | |
} else { | |
r = fileName; | |
@@ -333,18 +468,18 @@ public class ReportMojo extends AbstractMavenReport { | |
} | |
} | |
- private File resolvePath(final String path) { | |
+ private static File resolvePath(final MavenProject project, final String path) { | |
File file = new File(path); | |
if (!file.isAbsolute()) { | |
- file = new File(getProject().getBasedir(), path); | |
+ file = new File(project.getBasedir(), path); | |
} | |
return file; | |
} | |
- private List<File> getCompileSourceRoots() { | |
+ private static List<File> getCompileSourceRoots(final MavenProject project) { | |
final List<File> result = new ArrayList<File>(); | |
- for (final Object path : getProject().getCompileSourceRoots()) { | |
- result.add(resolvePath((String) path)); | |
+ for (final Object path : project.getCompileSourceRoots()) { | |
+ result.add(resolvePath(project, (String) path)); | |
} | |
return result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment