• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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