blob: 2b25e25665068c5dee671859cb86316da81df17c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2017 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
* TODO: use CounterEntity directly once Maven 3 is required.
*/
public void setCounter(final String entity) {
this.entity = CounterEntity.valueOf(entity);
}
/**
* @return the configured value to check
*/
public CounterValue getValue() {
return value;
}
/**
* Sets the value to check.
*
* @param value
* value to check
* TODO: use CounterValue directly once Maven 3 is required.
*/
public void setValue(final String value) {
this.value = CounterValue.valueOf(value);
}
/**
* @return configured minimum value, or <code>null</code> if no minimum is
* given
*/
public String getMinimum() {
return minimum == null ? null : minimum.toPlainString();
}
/**
* Sets allowed maximum value as decimal string or percent representation.
* The given precision is also considered in error messages. 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 String minimum) {
this.minimum = parseValue(minimum);
}
/**
* @return configured maximum value, or <code>null</code> if no maximum is
* given
*/
public String getMaximum() {
return maximum == null ? null : maximum.toPlainString();
}
/**
* Sets allowed maximum value as decimal string or percent representation.
* The given precision is also considered in error messages. 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 String maximum) {
this.maximum = parseValue(maximum);
}
private static BigDecimal parseValue(final String value) {
if (value == null) {
return null;
}
final String trimmedValue = value.trim();
if (trimmedValue.endsWith("%")) {
final String percent = trimmedValue.substring(0, trimmedValue.length() - 1);
return new BigDecimal(percent).movePointLeft(2);
}
return new BigDecimal(trimmedValue);
}
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());
}
}