Home | History | Annotate | Download | only in maven
      1 /*******************************************************************************
      2  * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
      3  * All rights reserved. This program and the accompanying materials
      4  * are made available under the terms of the Eclipse Public License v1.0
      5  * which accompanies this distribution, and is available at
      6  * http://www.eclipse.org/legal/epl-v10.html
      7  *
      8  * Contributors:
      9  *    John Oliver, Marc R. Hoffmann, Jan Wloka - initial API and implementation
     10  *
     11  *******************************************************************************/
     12 package org.jacoco.maven;
     13 
     14 import java.io.File;
     15 import java.io.IOException;
     16 import java.util.ArrayList;
     17 import java.util.Arrays;
     18 import java.util.List;
     19 import java.util.Locale;
     20 
     21 import org.apache.maven.artifact.Artifact;
     22 import org.apache.maven.model.Dependency;
     23 import org.apache.maven.plugins.annotations.Mojo;
     24 import org.apache.maven.plugins.annotations.Parameter;
     25 import org.apache.maven.project.MavenProject;
     26 import org.jacoco.report.IReportGroupVisitor;
     27 
     28 /**
     29  * <p>
     30  * Creates a structured code coverage report (HTML, XML, and CSV) from multiple
     31  * projects within reactor. The report is created from all modules this project
     32  * depends on. From those projects class and source files as well as JaCoCo
     33  * execution data files will be collected. In addition execution data is
     34  * collected from the project itself. This also allows to create coverage
     35  * reports when tests are in separate projects than the code under test, for
     36  * example in case of integration tests.
     37  * </p>
     38  *
     39  * <p>
     40  * Using the dependency scope allows to distinguish projects which contribute
     41  * execution data but should not become part of the report:
     42  * </p>
     43  *
     44  * <ul>
     45  * <li><code>compile</code>, <code>runtime</code>, <code>provided</code>:
     46  * Project source and execution data is included in the report.</li>
     47  * <li><code>test</code>: Only execution data is considered for the report.</li>
     48  * </ul>
     49  *
     50  * @since 0.7.7
     51  */
     52 @Mojo(name = "report-aggregate", threadSafe = true)
     53 public class ReportAggregateMojo extends AbstractReportMojo {
     54 
     55 	/**
     56 	 * A list of execution data files to include in the report from each
     57 	 * project. May use wildcard characters (* and ?). When not specified all
     58 	 * *.exec files from the target folder will be included.
     59 	 */
     60 	@Parameter
     61 	List<String> dataFileIncludes;
     62 
     63 	/**
     64 	 * A list of execution data files to exclude from the report. May use
     65 	 * wildcard characters (* and ?). When not specified nothing will be
     66 	 * excluded.
     67 	 */
     68 	@Parameter
     69 	List<String> dataFileExcludes;
     70 
     71 	/**
     72 	 * Output directory for the reports. Note that this parameter is only
     73 	 * relevant if the goal is run from the command line or from the default
     74 	 * build lifecycle. If the goal is run indirectly as part of a site
     75 	 * generation, the output directory configured in the Maven Site Plugin is
     76 	 * used instead.
     77 	 */
     78 	@Parameter(defaultValue = "${project.reporting.outputDirectory}/jacoco-aggregate")
     79 	private File outputDirectory;
     80 
     81 	/**
     82 	 * The projects in the reactor.
     83 	 */
     84 	@Parameter(property = "reactorProjects", readonly = true)
     85 	private List<MavenProject> reactorProjects;
     86 
     87 	@Override
     88 	boolean canGenerateReportRegardingDataFiles() {
     89 		return true;
     90 	}
     91 
     92 	@Override
     93 	boolean canGenerateReportRegardingClassesDirectory() {
     94 		return true;
     95 	}
     96 
     97 	@Override
     98 	void loadExecutionData(final ReportSupport support) throws IOException {
     99 		// https://issues.apache.org/jira/browse/MNG-5440
    100 		if (dataFileIncludes == null) {
    101 			dataFileIncludes = Arrays.asList("target/*.exec");
    102 		}
    103 
    104 		final FileFilter filter = new FileFilter(dataFileIncludes,
    105 				dataFileExcludes);
    106 		loadExecutionData(support, filter, getProject().getBasedir());
    107 		for (final MavenProject dependency : findDependencies(
    108 				Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME,
    109 				Artifact.SCOPE_PROVIDED, Artifact.SCOPE_TEST)) {
    110 			loadExecutionData(support, filter, dependency.getBasedir());
    111 		}
    112 	}
    113 
    114 	private void loadExecutionData(final ReportSupport support,
    115 			final FileFilter filter, final File basedir) throws IOException {
    116 		for (final File execFile : filter.getFiles(basedir)) {
    117 			support.loadExecutionData(execFile);
    118 		}
    119 	}
    120 
    121 	@Override
    122 	void addFormatters(final ReportSupport support, final Locale locale)
    123 			throws IOException {
    124 		support.addAllFormatters(outputDirectory, outputEncoding, footer,
    125 				locale);
    126 	}
    127 
    128 	@Override
    129 	void createReport(final IReportGroupVisitor visitor,
    130 			final ReportSupport support) throws IOException {
    131 		final IReportGroupVisitor group = visitor.visitGroup(title);
    132 		for (final MavenProject dependency : findDependencies(
    133 				Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME,
    134 				Artifact.SCOPE_PROVIDED)) {
    135 			support.processProject(group, dependency.getArtifactId(),
    136 					dependency, getIncludes(), getExcludes(), sourceEncoding);
    137 		}
    138 	}
    139 
    140 	@Override
    141 	protected String getOutputDirectory() {
    142 		return outputDirectory.getAbsolutePath();
    143 	}
    144 
    145 	@Override
    146 	public void setReportOutputDirectory(final File reportOutputDirectory) {
    147 		if (reportOutputDirectory != null
    148 				&& !reportOutputDirectory.getAbsolutePath().endsWith(
    149 						"jacoco-aggregate")) {
    150 			outputDirectory = new File(reportOutputDirectory,
    151 					"jacoco-aggregate");
    152 		} else {
    153 			outputDirectory = reportOutputDirectory;
    154 		}
    155 	}
    156 
    157 	public String getOutputName() {
    158 		return "jacoco-aggregate/index";
    159 	}
    160 
    161 	public String getName(final Locale locale) {
    162 		return "JaCoCo Aggregate";
    163 	}
    164 
    165 	private List<MavenProject> findDependencies(final String... scopes) {
    166 		final List<MavenProject> result = new ArrayList<MavenProject>();
    167 		final List<String> scopeList = Arrays.asList(scopes);
    168 		for (final Object dependencyObject : getProject().getDependencies()) {
    169 			final Dependency dependency = (Dependency) dependencyObject;
    170 			if (scopeList.contains(dependency.getScope())) {
    171 				final MavenProject project = findProjectFromReactor(dependency);
    172 				if (project != null) {
    173 					result.add(project);
    174 				}
    175 			}
    176 		}
    177 		return result;
    178 	}
    179 
    180 	private MavenProject findProjectFromReactor(final Dependency d) {
    181 		for (final MavenProject p : reactorProjects) {
    182 			if (p.getGroupId().equals(d.getGroupId())
    183 					&& p.getArtifactId().equals(d.getArtifactId())
    184 					&& p.getVersion().equals(d.getVersion())) {
    185 				return p;
    186 			}
    187 		}
    188 		return null;
    189 	}
    190 
    191 }
    192