1 /******************************************************************************* 2 * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors 3 * This program and the accompanying materials are made available under 4 * the terms of the Eclipse Public License 2.0 which is available at 5 * http://www.eclipse.org/legal/epl-2.0 6 * 7 * SPDX-License-Identifier: EPL-2.0 8 * 9 * Contributors: 10 * Evgeny Mandrikov - initial API and implementation 11 * Kyle Lieber - implementation of CheckMojo 12 * Marc Hoffmann - redesign using report APIs 13 * 14 *******************************************************************************/ 15 package org.jacoco.maven; 16 17 import java.io.File; 18 import java.io.IOException; 19 import java.util.ArrayList; 20 import java.util.List; 21 22 import org.apache.maven.plugin.MojoExecutionException; 23 import org.apache.maven.plugins.annotations.LifecyclePhase; 24 import org.apache.maven.plugins.annotations.Mojo; 25 import org.apache.maven.plugins.annotations.Parameter; 26 import org.jacoco.core.analysis.ICoverageNode; 27 import org.jacoco.report.IReportVisitor; 28 import org.jacoco.report.check.IViolationsOutput; 29 import org.jacoco.report.check.Limit; 30 import org.jacoco.report.check.Rule; 31 32 /** 33 * Checks that the code coverage metrics are being met. 34 * 35 * @since 0.6.1 36 */ 37 @Mojo(name = "check", defaultPhase = LifecyclePhase.VERIFY, threadSafe = true) 38 public class CheckMojo extends AbstractJacocoMojo implements IViolationsOutput { 39 40 private static final String MSG_SKIPPING = "Skipping JaCoCo execution due to missing execution data file:"; 41 private static final String CHECK_SUCCESS = "All coverage checks have been met."; 42 private static final String CHECK_FAILED = "Coverage checks have not been met. See log for details."; 43 44 /** 45 * <p> 46 * Check configuration used to specify rules on element types (BUNDLE, 47 * PACKAGE, CLASS, SOURCEFILE or METHOD) with a list of limits. Each limit 48 * applies to a certain counter (INSTRUCTION, LINE, BRANCH, COMPLEXITY, 49 * METHOD, CLASS) and defines a minimum or maximum for the corresponding 50 * value (TOTALCOUNT, COVEREDCOUNT, MISSEDCOUNT, COVEREDRATIO, MISSEDRATIO). 51 * If a limit refers to a ratio it must be in the range from 0.0 to 1.0 52 * where the number of decimal places will also determine the precision in 53 * error messages. A limit ratio may optionally be declared as a percentage 54 * where 0.80 and 80% represent the same value. 55 * </p> 56 * 57 * <p> 58 * If not specified the following defaults are assumed: 59 * </p> 60 * 61 * <ul> 62 * <li>rule element: BUNDLE</li> 63 * <li>limit counter: INSTRUCTION</li> 64 * <li>limit value: COVEREDRATIO</li> 65 * </ul> 66 * 67 * <p> 68 * This example requires an overall instruction coverage of 80% and no class 69 * must be missed: 70 * </p> 71 * 72 * <pre> 73 * {@code 74 * <rules> 75 * <rule> 76 * <element>BUNDLE</element> 77 * <limits> 78 * <limit> 79 * <counter>INSTRUCTION</counter> 80 * <value>COVEREDRATIO</value> 81 * <minimum>0.80</minimum> 82 * </limit> 83 * <limit> 84 * <counter>CLASS</counter> 85 * <value>MISSEDCOUNT</value> 86 * <maximum>0</maximum> 87 * </limit> 88 * </limits> 89 * </rule> 90 * </rules>} 91 * </pre> 92 * 93 * <p> 94 * This example requires a line coverage minimum of 50% for every class 95 * except test classes: 96 * </p> 97 * 98 * <pre> 99 * {@code 100 * <rules> 101 * <rule> 102 * <element>CLASS</element> 103 * <excludes> 104 * <exclude>*Test</exclude> 105 * </excludes> 106 * <limits> 107 * <limit> 108 * <counter>LINE</counter> 109 * <value>COVEREDRATIO</value> 110 * <minimum>50%</minimum> 111 * </limit> 112 * </limits> 113 * </rule> 114 * </rules>} 115 * </pre> 116 */ 117 @Parameter(required = true) 118 private List<RuleConfiguration> rules; 119 120 /** 121 * Halt the build if any of the checks fail. 122 */ 123 @Parameter(property = "jacoco.haltOnFailure", defaultValue = "true", required = true) 124 private boolean haltOnFailure; 125 126 /** 127 * File with execution data. 128 */ 129 @Parameter(defaultValue = "${project.build.directory}/jacoco.exec") 130 private File dataFile; 131 132 /** 133 * A list of class files to include into analysis. May use wildcard 134 * characters (* and ?). When not specified everything will be included. 135 */ 136 @Parameter 137 private List<String> includes; 138 139 /** 140 * A list of class files to exclude from analysis. May use wildcard 141 * characters (* and ?). When not specified nothing will be excluded. 142 */ 143 @Parameter 144 private List<String> excludes; 145 146 private boolean violations; 147 canCheckCoverage()148 private boolean canCheckCoverage() { 149 if (!dataFile.exists()) { 150 getLog().info(MSG_SKIPPING + dataFile); 151 return false; 152 } 153 final File classesDirectory = new File( 154 getProject().getBuild().getOutputDirectory()); 155 if (!classesDirectory.exists()) { 156 getLog().info( 157 "Skipping JaCoCo execution due to missing classes directory:" 158 + classesDirectory); 159 return false; 160 } 161 return true; 162 } 163 164 @Override executeMojo()165 public void executeMojo() throws MojoExecutionException { 166 if (!canCheckCoverage()) { 167 return; 168 } 169 executeCheck(); 170 } 171 executeCheck()172 private void executeCheck() throws MojoExecutionException { 173 violations = false; 174 175 final ReportSupport support = new ReportSupport(getLog()); 176 177 final List<Rule> checkerrules = new ArrayList<Rule>(); 178 for (final RuleConfiguration r : rules) { 179 checkerrules.add(r.rule); 180 } 181 support.addRulesChecker(checkerrules, this); 182 183 try { 184 final IReportVisitor visitor = support.initRootVisitor(); 185 support.loadExecutionData(dataFile); 186 support.processProject(visitor, getProject(), includes, excludes); 187 visitor.visitEnd(); 188 } catch (final IOException e) { 189 throw new MojoExecutionException( 190 "Error while checking code coverage: " + e.getMessage(), e); 191 } 192 if (violations) { 193 if (this.haltOnFailure) { 194 throw new MojoExecutionException(CHECK_FAILED); 195 } else { 196 this.getLog().warn(CHECK_FAILED); 197 } 198 } else { 199 this.getLog().info(CHECK_SUCCESS); 200 } 201 } 202 onViolation(final ICoverageNode node, final Rule rule, final Limit limit, final String message)203 public void onViolation(final ICoverageNode node, final Rule rule, 204 final Limit limit, final String message) { 205 this.getLog().warn(message); 206 violations = true; 207 } 208 209 } 210