CheckMojo.java
/*******************************************************************************
* Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Evgeny Mandrikov - initial API and implementation
* Kyle Lieber - implementation of CheckMojo
*
*******************************************************************************/
package org.jacoco.maven;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import org.apache.maven.plugin.MojoExecutionException;
import org.jacoco.core.analysis.IBundleCoverage;
import org.jacoco.core.analysis.ICounter;
import org.jacoco.core.analysis.ICoverageNode.CounterEntity;
import org.jacoco.core.data.ExecFileLoader;
import org.jacoco.core.data.ExecutionDataStore;
/**
* Checks that the code coverage metrics are being met.
*
* @goal check
* @phase verify
* @requiresProject true
* @threadSafe
*/
public class CheckMojo extends AbstractJacocoMojo {
private static final String MSG_SKIPPING = "Skipping JaCoCo execution due to missing execution data file";
private static final String ERROR_UNABLE_TO_READ = "Unable to read execution data file %s: %s";
private static final String ERROR_CHECKING_COVERAGE = "Error while checking coverage: %s";
private static final String INSUFFICIENT_COVERAGE = "Insufficient code coverage for %s: %2$.2f%% < %3$.2f%%";
private static final String CHECK_FAILED = "Coverage checks have not been met. See report for details.";
private static final String CHECK_SUCCESS = "All coverage checks have been met.";
/**
* <p>
* Check configuration. Used to specify minimum coverage percentages that
* must be met. Defaults to 0% if a percentage ratio is not specified.
* </p>
*
* <p>
* Example requiring 100% coverage for class, instruction, method, branch,
* complexity, and line:
* </p>
*
* <pre>
* {@code
* <check>
* <classRatio>100</classRatio>
* <instructionRatio>100</instructionRatio>
* <methodRatio>100</methodRatio>
* <branchRatio>100</branchRatio>
* <complexityRatio>100</complexityRatio>
* <lineRatio>100</lineRatio>
* </check>}
* </pre>
*
* @parameter
* @required
*/
private CheckConfiguration check;
/**
* Halt the build if any of the checks fail.
*
* @parameter expression="${jacoco.haltOnFailure}" default-value="true"
* @required
*/
private boolean haltOnFailure;
/**
* File with execution data.
*
* @parameter default-value="${project.build.directory}/jacoco.exec"
*/
private File dataFile;
private ExecutionDataStore executionDataStore;
private boolean canCheckCoverage() {
if (!dataFile.exists()) {
getLog().info(MSG_SKIPPING);
return false;
}
return true;
}
@Override
public void executeMojo() throws MojoExecutionException,
MojoExecutionException {
if (!canCheckCoverage()) {
return;
}
executeCheck();
}
private void executeCheck() throws MojoExecutionException {
try {
loadExecutionData();
} catch (final IOException e) {
throw new MojoExecutionException(String.format(
ERROR_UNABLE_TO_READ, dataFile, e.getMessage()), e);
}
try {
if (check()) {
this.getLog().info(CHECK_SUCCESS);
} else {
this.handleFailure();
}
} catch (final IOException e) {
throw new MojoExecutionException(String.format(
ERROR_CHECKING_COVERAGE, e.getMessage()), e);
}
}
private void loadExecutionData() throws IOException {
final ExecFileLoader loader = new ExecFileLoader();
loader.load(dataFile);
executionDataStore = loader.getExecutionDataStore();
}
private boolean check() throws IOException {
final FileFilter fileFilter = new FileFilter(this.getIncludes(),
this.getExcludes());
final BundleCreator creator = new BundleCreator(this.getProject(),
fileFilter);
final IBundleCoverage bundle = creator.createBundle(executionDataStore);
boolean passed = true;
for (final CounterEntity entity : CounterEntity.values()) {
passed = this.checkCounter(entity, bundle.getCounter(entity),
check.getRatio(entity))
&& passed;
}
return passed;
}
private boolean checkCounter(final CounterEntity entity,
final ICounter counter, final double checkRatio) {
boolean passed = true;
final double ratio = counter.getCoveredRatio() * 100;
if (ratio < checkRatio) {
this.getLog().warn(
String.format(INSUFFICIENT_COVERAGE, entity.name(),
truncate(ratio), truncate(checkRatio)));
passed = false;
}
return passed;
}
private BigDecimal truncate(final double value) {
return new BigDecimal(value).setScale(2, BigDecimal.ROUND_FLOOR);
}
private void handleFailure() throws MojoExecutionException {
if (this.haltOnFailure) {
throw new MojoExecutionException(CHECK_FAILED);
} else {
this.getLog().warn(CHECK_FAILED);
}
}
}