diff options
author | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2013-03-20 11:55:53 +0100 |
---|---|---|
committer | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2013-05-23 22:21:25 +0200 |
commit | 55fae171b754e20062fae7cdb2d5feae8ee54017 (patch) | |
tree | 5dfac8aae54da8cb7585f63e6433caf75ccd7531 /org.jacoco.report/src | |
parent | ccbbc428f5bad06f43ae6c42f9691a1c9f5a2f73 (diff) | |
download | jacoco-55fae171b754e20062fae7cdb2d5feae8ee54017.tar.gz |
New coverage check implementation.
New report APIs to check coverage, support for coverage checks in Ant,
rework Maven check goal implementation based on new APIs.
Diffstat (limited to 'org.jacoco.report/src')
8 files changed, 650 insertions, 1 deletions
diff --git a/org.jacoco.report/src/org/jacoco/report/ILanguageNames.java b/org.jacoco.report/src/org/jacoco/report/ILanguageNames.java index fe682696..f45ed644 100644 --- a/org.jacoco.report/src/org/jacoco/report/ILanguageNames.java +++ b/org.jacoco.report/src/org/jacoco/report/ILanguageNames.java @@ -67,4 +67,20 @@ public interface ILanguageNames { public String getMethodName(String vmclassname, String vmmethodname, String vmdesc, String vmsignature); + /** + * Calculates the language specific fully qualified name of a method. + * + * @param vmclassname + * vm name of a containing class + * @param vmmethodname + * vm name of the method + * @param vmdesc + * vm parameter description of the method + * @param vmsignature + * vm signature of the method (may be <code>null</code>) + * @return language specific notation for the method + */ + public String getQualifiedMethodName(String vmclassname, + String vmmethodname, String vmdesc, String vmsignature); + } diff --git a/org.jacoco.report/src/org/jacoco/report/JavaNames.java b/org.jacoco.report/src/org/jacoco/report/JavaNames.java index 34c33963..e2ec69e1 100644 --- a/org.jacoco.report/src/org/jacoco/report/JavaNames.java +++ b/org.jacoco.report/src/org/jacoco/report/JavaNames.java @@ -77,6 +77,19 @@ public class JavaNames implements ILanguageNames { public String getMethodName(final String vmclassname, final String vmmethodname, final String vmdesc, final String vmsignature) { + return getMethodName(vmclassname, vmmethodname, vmdesc, false); + } + + public String getQualifiedMethodName(final String vmclassname, + final String vmmethodname, final String vmdesc, + final String vmsignature) { + return getQualifiedClassName(vmclassname) + "." + + getMethodName(vmclassname, vmmethodname, vmdesc, true); + } + + private String getMethodName(final String vmclassname, + final String vmmethodname, final String vmdesc, + final boolean qualifiedParams) { if ("<clinit>".equals(vmmethodname)) { return "static {...}"; } @@ -99,7 +112,11 @@ public class JavaNames implements ILanguageNames { } else { comma = true; } - result.append(getShortTypeName(arg)); + if (qualifiedParams) { + result.append(getQualifiedClassName(arg.getClassName())); + } else { + result.append(getShortTypeName(arg)); + } } result.append(')'); return result.toString(); diff --git a/org.jacoco.report/src/org/jacoco/report/check/BundleChecker.java b/org.jacoco.report/src/org/jacoco/report/check/BundleChecker.java new file mode 100644 index 00000000..7f2cfc8f --- /dev/null +++ b/org.jacoco.report/src/org/jacoco/report/check/BundleChecker.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * 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: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.report.check; + +import java.util.ArrayList; +import java.util.Collection; + +import org.jacoco.core.analysis.IBundleCoverage; +import org.jacoco.core.analysis.IClassCoverage; +import org.jacoco.core.analysis.ICoverageNode; +import org.jacoco.core.analysis.IMethodCoverage; +import org.jacoco.core.analysis.IPackageCoverage; +import org.jacoco.core.analysis.ISourceFileCoverage; +import org.jacoco.report.ILanguageNames; + +/** + * Internal class to check a list of rules against a {@link IBundleCoverage} + * instance. + */ +class BundleChecker { + + private final ILanguageNames names; + private final IViolationsOutput output; + + private final Collection<Rule> bundleRules; + private final Collection<Rule> packageRules; + private final Collection<Rule> classRules; + private final ArrayList<Rule> sourceFileRules; + private final Collection<Rule> methodRules; + + private final boolean traversePackages; + private final boolean traverseClasses; + private final boolean traverseSourceFiles; + private final boolean traverseMethods; + + public BundleChecker(final Collection<Rule> rules, + final ILanguageNames names, final IViolationsOutput output) { + this.names = names; + this.output = output; + this.bundleRules = new ArrayList<Rule>(); + this.packageRules = new ArrayList<Rule>(); + this.classRules = new ArrayList<Rule>(); + this.sourceFileRules = new ArrayList<Rule>(); + this.methodRules = new ArrayList<Rule>(); + for (final Rule rule : rules) { + switch (rule.getElement()) { + case BUNDLE: + bundleRules.add(rule); + break; + case PACKAGE: + packageRules.add(rule); + break; + case CLASS: + classRules.add(rule); + break; + case SOURCEFILE: + sourceFileRules.add(rule); + break; + case METHOD: + methodRules.add(rule); + break; + } + } + traverseMethods = !methodRules.isEmpty(); + traverseClasses = !classRules.isEmpty() || traverseMethods; + traverseSourceFiles = !sourceFileRules.isEmpty(); + traversePackages = !packageRules.isEmpty() || traverseClasses + || traverseSourceFiles; + } + + public void checkBundle(final IBundleCoverage bundleCoverage) { + final String name = bundleCoverage.getName(); + checkRules(bundleCoverage, bundleRules, "bundle", name); + if (traversePackages) { + for (final IPackageCoverage p : bundleCoverage.getPackages()) { + check(p); + } + } + } + + private void check(final IPackageCoverage packageCoverage) { + final String name = names.getPackageName(packageCoverage.getName()); + checkRules(packageCoverage, packageRules, "package", name); + if (traverseClasses) { + for (final IClassCoverage c : packageCoverage.getClasses()) { + check(c); + } + } + if (traverseSourceFiles) { + for (final ISourceFileCoverage s : packageCoverage.getSourceFiles()) { + check(s); + } + } + } + + private void check(final IClassCoverage classCoverage) { + final String name = names + .getQualifiedClassName(classCoverage.getName()); + checkRules(classCoverage, classRules, "class", name); + if (traverseMethods) { + for (final IMethodCoverage m : classCoverage.getMethods()) { + check(m, classCoverage.getName()); + } + } + } + + private void check(final ISourceFileCoverage sourceFile) { + final String name = sourceFile.getPackageName() + "/" + + sourceFile.getName(); + checkRules(sourceFile, sourceFileRules, "source file", name); + } + + private void check(final IMethodCoverage method, final String className) { + final String name = names.getQualifiedMethodName(className, + method.getName(), method.getDesc(), method.getSignature()); + checkRules(method, methodRules, "method", name); + } + + private void checkRules(final ICoverageNode node, + final Collection<Rule> rules, final String typename, + final String elementname) { + for (final Rule rule : rules) { + if (rule.matches(elementname)) { + for (final Limit limit : rule.getLimits()) { + checkLimit(node, typename, elementname, rule, limit); + } + } + } + } + + private void checkLimit(final ICoverageNode node, final String elementtype, + final String typename, final Rule rule, final Limit limit) { + final String message = limit.check(node); + if (message != null) { + output.onViolation(node, rule, limit, String.format( + "Rule violated for %s %s: %s", elementtype, typename, + message)); + } + } + +} diff --git a/org.jacoco.report/src/org/jacoco/report/check/IViolationsOutput.java b/org.jacoco.report/src/org/jacoco/report/check/IViolationsOutput.java new file mode 100644 index 00000000..9167ccc7 --- /dev/null +++ b/org.jacoco.report/src/org/jacoco/report/check/IViolationsOutput.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.report.check; + +import org.jacoco.core.analysis.ICoverageNode; + +/** + * Call-back interface which is used to report rule violations to. + * + */ +public interface IViolationsOutput { + + /** + * Called for every rule violation. + * + * @param node + * node which violates a rule + * @param rule + * rule which is violated + * @param limit + * limit which is violated + * @param message + * readable message describing this violation + */ + void onViolation(ICoverageNode node, Rule rule, Limit limit, String message); + +} diff --git a/org.jacoco.report/src/org/jacoco/report/check/Limit.java b/org.jacoco.report/src/org/jacoco/report/check/Limit.java new file mode 100644 index 00000000..8aec0ded --- /dev/null +++ b/org.jacoco.report/src/org/jacoco/report/check/Limit.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * 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: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.report.check; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.jacoco.core.analysis.ICounter.CounterValue; +import org.jacoco.core.analysis.ICoverageNode; +import org.jacoco.core.analysis.ICoverageNode.CounterEntity; + +/** + * Descriptor for a limit which is given by a {@link Rule}. + */ +public class Limit { + + private static final Map<CounterValue, String> VALUE_NAMES; + private static final Map<CounterEntity, String> ENTITY_NAMES; + + static { + final Map<CounterValue, String> values = new HashMap<CounterValue, String>(); + values.put(CounterValue.TOTALCOUNT, "total count"); + values.put(CounterValue.MISSEDCOUNT, "missed count"); + values.put(CounterValue.COVEREDCOUNT, "covered count"); + values.put(CounterValue.MISSEDRATIO, "missed ratio"); + values.put(CounterValue.COVEREDRATIO, "covered ratio"); + VALUE_NAMES = Collections.unmodifiableMap(values); + + final Map<CounterEntity, String> entities = new HashMap<CounterEntity, String>(); + entities.put(CounterEntity.INSTRUCTION, "instructions"); + entities.put(CounterEntity.BRANCH, "branches"); + entities.put(CounterEntity.COMPLEXITY, "complexity"); + entities.put(CounterEntity.LINE, "lines"); + entities.put(CounterEntity.METHOD, "methods"); + entities.put(CounterEntity.CLASS, "classes"); + ENTITY_NAMES = Collections.unmodifiableMap(entities); + } + + private CounterEntity entity; + + private CounterValue value; + + private BigDecimal minimum; + + private BigDecimal maximum; + + /** + * Creates a new instance with the following defaults: + * <ul> + * <li>counter entity: {@link CounterEntity#INSTRUCTION} + * <li>counter value: {@link CounterValue#COVEREDRATIO} + * <li>minimum: no limit + * <li>maximum: no limit + * </ul> + */ + public Limit() { + this.entity = CounterEntity.INSTRUCTION; + this.value = CounterValue.COVEREDRATIO; + } + + /** + * @return the configured counter entity to check + */ + public CounterEntity getEntity() { + return entity; + } + + /** + * Sets the counter entity to check. + * + * @param entity + * counter entity to check + */ + public void setCounter(final CounterEntity entity) { + this.entity = entity; + } + + /** + * @return the configured value to check + */ + public CounterValue getValue() { + return value; + } + + /** + * Sets the value to check. + * + * @param value + * value to check + */ + public void setValue(final CounterValue value) { + this.value = value; + } + + /** + * @return configured minimum value, or <code>null</code> if no minimum is + * given + */ + public BigDecimal getMinimum() { + return minimum; + } + + /** + * Sets allowed minimum value. Coverage ratios are given in the range from + * 0.0 to 1.0. + * + * @param minimum + * allowed minimum or <code>null</code>, if no minimum should be + * checked + */ + public void setMinimum(final BigDecimal minimum) { + this.minimum = minimum; + } + + /** + * Sets allowed minimum value as String representation. + * + * @param minimum + * allowed minimum or <code>null</code>, if no minimum should be + * checked + * @see Limit#setMinimum(BigDecimal) + */ + public void setMinimum(final String minimum) { + setMinimum(minimum == null ? null : new BigDecimal(minimum)); + } + + /** + * @return configured maximum value, or <code>null</code> if no maximum is + * given + */ + public BigDecimal getMaximum() { + return maximum; + } + + /** + * Sets allowed maximum value as String representation. + * + * @param maximum + * allowed maximum or <code>null</code>, if no maximum should be + * checked + * @see #setMaximum(BigDecimal) + */ + public void setMaximum(final String maximum) { + setMaximum(maximum == null ? null : new BigDecimal(maximum)); + } + + /** + * Sets allowed maximum value. Coverage ratios are given in the range from + * 0.0 to 1.0. + * + * @param maximum + * allowed maximum or <code>null</code>, if no maximum should be + * checked + */ + public void setMaximum(final BigDecimal maximum) { + this.maximum = maximum; + } + + String check(final ICoverageNode node) { + final double d = node.getCounter(entity).getValue(value); + if (Double.isNaN(d)) { + return null; + } + final BigDecimal bd = BigDecimal.valueOf(d); + if (minimum != null && minimum.compareTo(bd) > 0) { + return message("minimum", bd, minimum, RoundingMode.FLOOR); + } + if (maximum != null && maximum.compareTo(bd) < 0) { + return message("maximum", bd, maximum, RoundingMode.CEILING); + } + return null; + } + + private String message(final String minmax, final BigDecimal v, + final BigDecimal ref, final RoundingMode mode) { + final BigDecimal rounded = v.setScale(ref.scale(), mode); + return String.format("%s %s is %s, but expected %s is %s", + ENTITY_NAMES.get(entity), VALUE_NAMES.get(value), + rounded.toPlainString(), minmax, ref.toPlainString()); + } + +} diff --git a/org.jacoco.report/src/org/jacoco/report/check/Rule.java b/org.jacoco.report/src/org/jacoco/report/check/Rule.java new file mode 100644 index 00000000..3dea01e3 --- /dev/null +++ b/org.jacoco.report/src/org/jacoco/report/check/Rule.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * 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: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.report.check; + +import java.util.ArrayList; +import java.util.List; + +import org.jacoco.core.analysis.ICoverageNode.ElementType; +import org.jacoco.core.runtime.WildcardMatcher; + +/** + * A rule applies for a certain element type and can define any number of limits + * for all elements of this type. + */ +public final class Rule { + + private ElementType element; + private String includes; + private String excludes; + private List<Limit> limits; + + private WildcardMatcher includesMatcher; + private WildcardMatcher excludesMatcher; + + /** + * Creates a new Rule without limits. + */ + public Rule() { + this.element = ElementType.BUNDLE; + this.limits = new ArrayList<Limit>(); + this.setIncludes("*"); + this.setExcludes(""); + } + + /** + * @return element type this rule applies to + */ + public ElementType getElement() { + return element; + } + + /** + * @param elementType + * element type this rule applies to + */ + public void setElement(final ElementType elementType) { + this.element = elementType; + } + + /** + * @return includes pattern + */ + public String getIncludes() { + return includes; + } + + /** + * @param includes + * includes pattern + */ + public void setIncludes(final String includes) { + this.includes = includes; + this.includesMatcher = new WildcardMatcher(includes); + } + + /** + * @return excludes pattern + */ + public String getExcludes() { + return excludes; + } + + /** + * + * @param excludes + * excludes patterns + */ + public void setExcludes(final String excludes) { + this.excludes = excludes; + this.excludesMatcher = new WildcardMatcher(excludes); + } + + /** + * @return list of {@link Limit}s configured for this rule + */ + public List<Limit> getLimits() { + return limits; + } + + /** + * @param limits + * list of {@link Limit}s configured for this rule + */ + public void setLimits(final List<Limit> limits) { + this.limits = limits; + } + + /** + * Creates and adds a new {@link Limit}. + * + * @return creates {@link Limit} + */ + public Limit createLimit() { + final Limit limit = new Limit(); + this.limits.add(limit); + return limit; + } + + boolean matches(final String name) { + return includesMatcher.matches(name) && !excludesMatcher.matches(name); + } + +} diff --git a/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java b/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java new file mode 100644 index 00000000..ddcb7c75 --- /dev/null +++ b/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.report.check; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.jacoco.core.analysis.IBundleCoverage; +import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.SessionInfo; +import org.jacoco.report.ILanguageNames; +import org.jacoco.report.IReportGroupVisitor; +import org.jacoco.report.IReportVisitor; +import org.jacoco.report.ISourceFileLocator; +import org.jacoco.report.JavaNames; + +/** + * Formatter which checks a set of given rules and reports violations to a + * {@link IViolationsOutput} instance. + */ +public class RulesChecker { + + private List<Rule> rules; + private ILanguageNames languageNames; + + /** + * New formatter instance. + */ + public RulesChecker() { + this.rules = new ArrayList<Rule>(); + this.setLanguageNames(new JavaNames()); + } + + /** + * Sets the rules to check by this formatter. + * + * @param rules + * rules to check + */ + public void setRules(final List<Rule> rules) { + this.rules = rules; + } + + /** + * Sets the implementation for language name display for message formatting. + * Java language names are defined by default. + * + * @param languageNames + * converter for language specific names + */ + public void setLanguageNames(final ILanguageNames languageNames) { + this.languageNames = languageNames; + } + + /** + * Creates a new visitor to process the configured checks. + * + * @param output + * call-back to report violations to + * @return visitor to emit the report data to + */ + public IReportVisitor createVisitor(final IViolationsOutput output) { + final BundleChecker bundleChecker = new BundleChecker(rules, + languageNames, output); + return new IReportVisitor() { + + public IReportGroupVisitor visitGroup(final String name) + throws IOException { + return this; + } + + public void visitBundle(final IBundleCoverage bundle, + final ISourceFileLocator locator) throws IOException { + bundleChecker.checkBundle(bundle); + } + + public void visitInfo(final List<SessionInfo> sessionInfos, + final Collection<ExecutionData> executionData) + throws IOException { + } + + public void visitEnd() throws IOException { + } + }; + } + +} diff --git a/org.jacoco.report/src/org/jacoco/report/check/package-info.java b/org.jacoco.report/src/org/jacoco/report/check/package-info.java new file mode 100644 index 00000000..e0fe96a8 --- /dev/null +++ b/org.jacoco.report/src/org/jacoco/report/check/package-info.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * 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: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ + +/** + * Rules check implementation. + */ +package org.jacoco.report.check;
\ No newline at end of file |