Home | History | Annotate | Download | only in maven
      1 /*******************************************************************************
      2  * Copyright (c) 2009, 2015 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  *    Evgeny Mandrikov - initial API and implementation
     10  *    Kyle Lieber - implementation of CheckMojo
     11  *    Marc Hoffmann - redesign using report APIs
     12  *
     13  *******************************************************************************/
     14 package org.jacoco.maven;
     15 
     16 import java.io.File;
     17 import java.io.IOException;
     18 import java.util.ArrayList;
     19 import java.util.List;
     20 
     21 import org.apache.maven.plugin.MojoExecutionException;
     22 import org.jacoco.core.analysis.IBundleCoverage;
     23 import org.jacoco.core.analysis.ICoverageNode;
     24 import org.jacoco.core.data.ExecutionDataStore;
     25 import org.jacoco.core.tools.ExecFileLoader;
     26 import org.jacoco.report.IReportVisitor;
     27 import org.jacoco.report.check.IViolationsOutput;
     28 import org.jacoco.report.check.Limit;
     29 import org.jacoco.report.check.Rule;
     30 import org.jacoco.report.check.RulesChecker;
     31 
     32 /**
     33  * Checks that the code coverage metrics are being met.
     34  *
     35  * @goal check
     36  * @phase verify
     37  * @requiresProject true
     38  * @threadSafe
     39  * @since 0.6.1
     40  */
     41 public class CheckMojo extends AbstractJacocoMojo implements IViolationsOutput {
     42 
     43 	private static final String MSG_SKIPPING = "Skipping JaCoCo execution due to missing execution data file:";
     44 	private static final String CHECK_SUCCESS = "All coverage checks have been met.";
     45 	private static final String CHECK_FAILED = "Coverage checks have not been met. See log for details.";
     46 
     47 	/**
     48 	 * <p>
     49 	 * Check configuration used to specify rules on element types (BUNDLE,
     50 	 * PACKAGE, CLASS, SOURCEFILE or METHOD) with a list of limits. Each limit
     51 	 * applies to a certain counter (INSTRUCTION, LINE, BRANCH, COMPLEXITY,
     52 	 * METHOD, CLASS) and defines a minimum or maximum for the corresponding
     53 	 * value (TOTALCOUNT, COVEREDCOUNT, MISSEDCOUNT, COVEREDRATIO, MISSEDRATIO).
     54 	 * If a limit refers to a ratio the range is from 0.0 to 1.0 where the
     55 	 * number of decimal places will also determine the precision in error
     56 	 * messages.
     57 	 *
     58 	 * Note that you <b>must</b> use <tt>implementation</tt> hints for
     59 	 * <tt>rule</tt> and <tt>limit</tt> when using Maven 2, with Maven 3 you do
     60 	 * not need to specify the attributes.
     61 	 * </p>
     62 	 *
     63 	 * <p>
     64 	 * This example requires an overall instruction coverage of 80% and no class
     65 	 * must be missed:
     66 	 * </p>
     67 	 *
     68 	 * <pre>
     69 	 * {@code
     70 	 * <rules>
     71 	 *   <rule implementation="org.jacoco.maven.RuleConfiguration">
     72 	 *     <element>BUNDLE</element>
     73 	 *     <limits>
     74 	 *       <limit implementation="org.jacoco.report.check.Limit">
     75 	 *         <counter>INSTRUCTION</counter>
     76 	 *         <value>COVEREDRATIO</value>
     77 	 *         <minimum>0.80</minimum>
     78 	 *       </limit>
     79 	 *       <limit implementation="org.jacoco.report.check.Limit">
     80 	 *         <counter>CLASS</counter>
     81 	 *         <value>MISSEDCOUNT</value>
     82 	 *         <maximum>0</maximum>
     83 	 *       </limit>
     84 	 *     </limits>
     85 	 *   </rule>
     86 	 * </rules>}
     87 	 * </pre>
     88 	 *
     89 	 * <p>
     90 	 * This example requires a line coverage minimum of 50% for every class
     91 	 * except test classes:
     92 	 * </p>
     93 	 *
     94 	 * <pre>
     95 	 * {@code
     96 	 * <rules>
     97 	 *   <rule>
     98 	 *     <element>CLASS</element>
     99 	 *     <excludes>
    100 	 *       <exclude>*Test</exclude>
    101 	 *     </excludes>
    102 	 *     <limits>
    103 	 *       <limit>
    104 	 *         <counter>LINE</counter>
    105 	 *         <value>COVEREDRATIO</value>
    106 	 *         <minimum>0.50</minimum>
    107 	 *       </limit>
    108 	 *     </limits>
    109 	 *   </rule>
    110 	 * </rules>}
    111 	 * </pre>
    112 	 *
    113 	 * @parameter
    114 	 * @required
    115 	 */
    116 	private List<RuleConfiguration> rules;
    117 
    118 	/**
    119 	 * Halt the build if any of the checks fail.
    120 	 *
    121 	 * @parameter property="jacoco.haltOnFailure" default-value="true"
    122 	 * @required
    123 	 */
    124 	private boolean haltOnFailure;
    125 
    126 	/**
    127 	 * File with execution data.
    128 	 *
    129 	 * @parameter default-value="${project.build.directory}/jacoco.exec"
    130 	 */
    131 	private File dataFile;
    132 
    133 	private boolean violations;
    134 
    135 	private boolean canCheckCoverage() {
    136 		if (!dataFile.exists()) {
    137 			getLog().info(MSG_SKIPPING + dataFile);
    138 			return false;
    139 		}
    140 		final File classesDirectory = new File(getProject().getBuild()
    141 				.getOutputDirectory());
    142 		if (!classesDirectory.exists()) {
    143 			getLog().info(
    144 					"Skipping JaCoCo execution due to missing classes directory:"
    145 							+ classesDirectory);
    146 			return false;
    147 		}
    148 		return true;
    149 	}
    150 
    151 	@Override
    152 	public void executeMojo() throws MojoExecutionException,
    153 			MojoExecutionException {
    154 		if (!canCheckCoverage()) {
    155 			return;
    156 		}
    157 		executeCheck();
    158 	}
    159 
    160 	private void executeCheck() throws MojoExecutionException {
    161 		final IBundleCoverage bundle = loadBundle();
    162 		violations = false;
    163 
    164 		final RulesChecker checker = new RulesChecker();
    165 		final List<Rule> checkerrules = new ArrayList<Rule>();
    166 		for (final RuleConfiguration r : rules) {
    167 			checkerrules.add(r.rule);
    168 		}
    169 		checker.setRules(checkerrules);
    170 
    171 		final IReportVisitor visitor = checker.createVisitor(this);
    172 		try {
    173 			visitor.visitBundle(bundle, null);
    174 		} catch (final IOException e) {
    175 			throw new MojoExecutionException(
    176 					"Error while checking code coverage: " + e.getMessage(), e);
    177 		}
    178 		if (violations) {
    179 			if (this.haltOnFailure) {
    180 				throw new MojoExecutionException(CHECK_FAILED);
    181 			} else {
    182 				this.getLog().warn(CHECK_FAILED);
    183 			}
    184 		} else {
    185 			this.getLog().info(CHECK_SUCCESS);
    186 		}
    187 	}
    188 
    189 	private IBundleCoverage loadBundle() throws MojoExecutionException {
    190 		final FileFilter fileFilter = new FileFilter(this.getIncludes(),
    191 				this.getExcludes());
    192 		final BundleCreator creator = new BundleCreator(getProject(),
    193 				fileFilter, getLog());
    194 		try {
    195 			final ExecutionDataStore executionData = loadExecutionData();
    196 			return creator.createBundle(executionData);
    197 		} catch (final IOException e) {
    198 			throw new MojoExecutionException(
    199 					"Error while reading code coverage: " + e.getMessage(), e);
    200 		}
    201 	}
    202 
    203 	private ExecutionDataStore loadExecutionData() throws IOException {
    204 		final ExecFileLoader loader = new ExecFileLoader();
    205 		loader.load(dataFile);
    206 		return loader.getExecutionDataStore();
    207 	}
    208 
    209 	public void onViolation(final ICoverageNode node, final Rule rule,
    210 			final Limit limit, final String message) {
    211 		this.getLog().warn(message);
    212 		violations = true;
    213 	}
    214 
    215 }
    216