1 /*******************************************************************************
2  * Copyright (c) 2009, 2019 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 	 * This example requires an overall instruction coverage of 80% and no class
68 	 * must be missed:
69 	 * </p>
70 	 *
71 	 * <pre>
72 	 * {@code
73 	 * <rules>
74 	 *   <rule>
75 	 *     <element>BUNDLE</element>
76 	 *     <limits>
77 	 *       <limit>
78 	 *         <counter>INSTRUCTION</counter>
79 	 *         <value>COVEREDRATIO</value>
80 	 *         <minimum>0.80</minimum>
81 	 *       </limit>
82 	 *       <limit>
83 	 *         <counter>CLASS</counter>
84 	 *         <value>MISSEDCOUNT</value>
85 	 *         <maximum>0</maximum>
86 	 *       </limit>
87 	 *     </limits>
88 	 *   </rule>
89 	 * </rules>}
90 	 * </pre>
91 	 *
92 	 * <p>
93 	 * This example requires a line coverage minimum of 50% for every class
94 	 * except test classes:
95 	 * </p>
96 	 *
97 	 * <pre>
98 	 * {@code
99 	 * <rules>
100 	 *   <rule>
101 	 *     <element>CLASS</element>
102 	 *     <excludes>
103 	 *       <exclude>*Test</exclude>
104 	 *     </excludes>
105 	 *     <limits>
106 	 *       <limit>
107 	 *         <counter>LINE</counter>
108 	 *         <value>COVEREDRATIO</value>
109 	 *         <minimum>50%</minimum>
110 	 *       </limit>
111 	 *     </limits>
112 	 *   </rule>
113 	 * </rules>}
114 	 * </pre>
115 	 */
116 	@Parameter(required = true)
117 	private List<RuleConfiguration> rules;
118 
119 	/**
120 	 * Halt the build if any of the checks fail.
121 	 */
122 	@Parameter(property = "jacoco.haltOnFailure", defaultValue = "true", required = true)
123 	private boolean haltOnFailure;
124 
125 	/**
126 	 * File with execution data.
127 	 */
128 	@Parameter(defaultValue = "${project.build.directory}/jacoco.exec")
129 	private File dataFile;
130 
131 	/**
132 	 * A list of class files to include into analysis. May use wildcard
133 	 * characters (* and ?). When not specified everything will be included.
134 	 */
135 	@Parameter
136 	private List<String> includes;
137 
138 	/**
139 	 * A list of class files to exclude from analysis. May use wildcard
140 	 * characters (* and ?). When not specified nothing will be excluded.
141 	 */
142 	@Parameter
143 	private List<String> excludes;
144 
145 	private boolean violations;
146 
canCheckCoverage()147 	private boolean canCheckCoverage() {
148 		if (!dataFile.exists()) {
149 			getLog().info(MSG_SKIPPING + dataFile);
150 			return false;
151 		}
152 		final File classesDirectory = new File(getProject().getBuild()
153 				.getOutputDirectory());
154 		if (!classesDirectory.exists()) {
155 			getLog().info(
156 					"Skipping JaCoCo execution due to missing classes directory:"
157 							+ classesDirectory);
158 			return false;
159 		}
160 		return true;
161 	}
162 
163 	@Override
executeMojo()164 	public void executeMojo() throws MojoExecutionException,
165 			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