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 * 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.apache.maven.plugins.annotations.LifecyclePhase; 23 import org.apache.maven.plugins.annotations.Mojo; 24 import org.apache.maven.plugins.annotations.Parameter; 25 import org.jacoco.core.analysis.ICoverageNode; 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 31 /** 32 * Checks that the code coverage metrics are being met. 33 * 34 * @since 0.6.1 35 */ 36 @Mojo(name = "check", defaultPhase = LifecyclePhase.VERIFY, threadSafe = true) 37 public class CheckMojo extends AbstractJacocoMojo implements IViolationsOutput { 38 39 private static final String MSG_SKIPPING = "Skipping JaCoCo execution due to missing execution data file:"; 40 private static final String CHECK_SUCCESS = "All coverage checks have been met."; 41 private static final String CHECK_FAILED = "Coverage checks have not been met. See log for details."; 42 43 /** 44 * <p> 45 * Check configuration used to specify rules on element types (BUNDLE, 46 * PACKAGE, CLASS, SOURCEFILE or METHOD) with a list of limits. Each limit 47 * applies to a certain counter (INSTRUCTION, LINE, BRANCH, COMPLEXITY, 48 * METHOD, CLASS) and defines a minimum or maximum for the corresponding 49 * value (TOTALCOUNT, COVEREDCOUNT, MISSEDCOUNT, COVEREDRATIO, MISSEDRATIO). 50 * If a limit refers to a ratio the range is from 0.0 to 1.0 where the 51 * number of decimal places will also determine the precision in error 52 * messages. A limit ratio may optionally be declared as a percentage 53 * where 0.80 and 80% represent the same value, the value must end with %. 54 * </p> 55 * 56 * <p> 57 * If not specified the following defaults are assumed: 58 * </p> 59 * 60 * <ul> 61 * <li>rule element: BUNDLE</li> 62 * <li>limit counter: INSTRUCTION</li> 63 * <li>limit value: COVEREDRATIO</li> 64 * </ul> 65 * 66 * <p> 67 * Note that you <b>must</b> use <tt>implementation</tt> hints for 68 * <tt>rule</tt> and <tt>limit</tt> when using Maven 2, with Maven 3 you do 69 * not need to specify the attributes. 70 * </p> 71 * 72 * <p> 73 * This example requires an overall instruction coverage of 80% and no class 74 * must be missed: 75 * </p> 76 * 77 * <pre> 78 * {@code 79 * <rules> 80 * <rule implementation="org.jacoco.maven.RuleConfiguration"> 81 * <element>BUNDLE</element> 82 * <limits> 83 * <limit implementation="org.jacoco.report.check.Limit"> 84 * <counter>INSTRUCTION</counter> 85 * <value>COVEREDRATIO</value> 86 * <minimum>0.80</minimum> 87 * </limit> 88 * <limit implementation="org.jacoco.report.check.Limit"> 89 * <counter>CLASS</counter> 90 * <value>MISSEDCOUNT</value> 91 * <maximum>0</maximum> 92 * </limit> 93 * </limits> 94 * </rule> 95 * </rules>} 96 * </pre> 97 * 98 * <p> 99 * This example requires a line coverage minimum of 50% for every class 100 * except test classes: 101 * </p> 102 * 103 * <pre> 104 * {@code 105 * <rules> 106 * <rule> 107 * <element>CLASS</element> 108 * <excludes> 109 * <exclude>*Test</exclude> 110 * </excludes> 111 * <limits> 112 * <limit> 113 * <counter>LINE</counter> 114 * <value>COVEREDRATIO</value> 115 * <minimum>50%</minimum> 116 * </limit> 117 * </limits> 118 * </rule> 119 * </rules>} 120 * </pre> 121 */ 122 @Parameter(required = true) 123 private List<RuleConfiguration> rules; 124 125 /** 126 * Halt the build if any of the checks fail. 127 */ 128 @Parameter(property = "jacoco.haltOnFailure", defaultValue = "true", required = true) 129 private boolean haltOnFailure; 130 131 /** 132 * File with execution data. 133 */ 134 @Parameter(defaultValue = "${project.build.directory}/jacoco.exec") 135 private File dataFile; 136 137 private boolean violations; 138 canCheckCoverage()139 private boolean canCheckCoverage() { 140 if (!dataFile.exists()) { 141 getLog().info(MSG_SKIPPING + dataFile); 142 return false; 143 } 144 final File classesDirectory = new File(getProject().getBuild() 145 .getOutputDirectory()); 146 if (!classesDirectory.exists()) { 147 getLog().info( 148 "Skipping JaCoCo execution due to missing classes directory:" 149 + classesDirectory); 150 return false; 151 } 152 return true; 153 } 154 155 @Override executeMojo()156 public void executeMojo() throws MojoExecutionException, 157 MojoExecutionException { 158 if (!canCheckCoverage()) { 159 return; 160 } 161 executeCheck(); 162 } 163 executeCheck()164 private void executeCheck() throws MojoExecutionException { 165 violations = false; 166 167 final ReportSupport support = new ReportSupport(getLog()); 168 169 final List<Rule> checkerrules = new ArrayList<Rule>(); 170 for (final RuleConfiguration r : rules) { 171 checkerrules.add(r.rule); 172 } 173 support.addRulesChecker(checkerrules, this); 174 175 try { 176 final IReportVisitor visitor = support.initRootVisitor(); 177 support.loadExecutionData(dataFile); 178 support.processProject(visitor, getProject(), this.getIncludes(), 179 this.getExcludes()); 180 visitor.visitEnd(); 181 } catch (final IOException e) { 182 throw new MojoExecutionException( 183 "Error while checking code coverage: " + e.getMessage(), e); 184 } 185 if (violations) { 186 if (this.haltOnFailure) { 187 throw new MojoExecutionException(CHECK_FAILED); 188 } else { 189 this.getLog().warn(CHECK_FAILED); 190 } 191 } else { 192 this.getLog().info(CHECK_SUCCESS); 193 } 194 } 195 onViolation(final ICoverageNode node, final Rule rule, final Limit limit, final String message)196 public void onViolation(final ICoverageNode node, final Rule rule, 197 final Limit limit, final String message) { 198 this.getLog().warn(message); 199 violations = true; 200 } 201 202 } 203