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