Home | History | Annotate | Download | only in check
      1 /*******************************************************************************
      2  * Copyright (c) 2009, 2015 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  *    Marc R. Hoffmann - initial API and implementation
     10  *
     11  *******************************************************************************/
     12 package org.jacoco.report.check;
     13 
     14 import java.math.BigDecimal;
     15 import java.math.RoundingMode;
     16 import java.util.Collections;
     17 import java.util.HashMap;
     18 import java.util.Map;
     19 
     20 import org.jacoco.core.analysis.ICounter.CounterValue;
     21 import org.jacoco.core.analysis.ICoverageNode;
     22 import org.jacoco.core.analysis.ICoverageNode.CounterEntity;
     23 
     24 /**
     25  * Descriptor for a limit which is given by a {@link Rule}.
     26  */
     27 public class Limit {
     28 
     29 	private static final Map<CounterValue, String> VALUE_NAMES;
     30 	private static final Map<CounterEntity, String> ENTITY_NAMES;
     31 
     32 	static {
     33 		final Map<CounterValue, String> values = new HashMap<CounterValue, String>();
     34 		values.put(CounterValue.TOTALCOUNT, "total count");
     35 		values.put(CounterValue.MISSEDCOUNT, "missed count");
     36 		values.put(CounterValue.COVEREDCOUNT, "covered count");
     37 		values.put(CounterValue.MISSEDRATIO, "missed ratio");
     38 		values.put(CounterValue.COVEREDRATIO, "covered ratio");
     39 		VALUE_NAMES = Collections.unmodifiableMap(values);
     40 
     41 		final Map<CounterEntity, String> entities = new HashMap<CounterEntity, String>();
     42 		entities.put(CounterEntity.INSTRUCTION, "instructions");
     43 		entities.put(CounterEntity.BRANCH, "branches");
     44 		entities.put(CounterEntity.COMPLEXITY, "complexity");
     45 		entities.put(CounterEntity.LINE, "lines");
     46 		entities.put(CounterEntity.METHOD, "methods");
     47 		entities.put(CounterEntity.CLASS, "classes");
     48 		ENTITY_NAMES = Collections.unmodifiableMap(entities);
     49 	}
     50 
     51 	private CounterEntity entity;
     52 
     53 	private CounterValue value;
     54 
     55 	private BigDecimal minimum;
     56 
     57 	private BigDecimal maximum;
     58 
     59 	/**
     60 	 * Creates a new instance with the following defaults:
     61 	 * <ul>
     62 	 * <li>counter entity: {@link CounterEntity#INSTRUCTION}
     63 	 * <li>counter value: {@link CounterValue#COVEREDRATIO}
     64 	 * <li>minimum: no limit
     65 	 * <li>maximum: no limit
     66 	 * </ul>
     67 	 */
     68 	public Limit() {
     69 		this.entity = CounterEntity.INSTRUCTION;
     70 		this.value = CounterValue.COVEREDRATIO;
     71 	}
     72 
     73 	/**
     74 	 * @return the configured counter entity to check
     75 	 */
     76 	public CounterEntity getEntity() {
     77 		return entity;
     78 	}
     79 
     80 	/**
     81 	 * Sets the counter entity to check.
     82 	 *
     83 	 * @param entity
     84 	 *            counter entity to check
     85 	 * TODO: use CounterEntity directly once Maven 3 is required.
     86 	 */
     87 	public void setCounter(final String entity) {
     88 		this.entity = CounterEntity.valueOf(entity);
     89 	}
     90 
     91 	/**
     92 	 * @return the configured value to check
     93 	 */
     94 	public CounterValue getValue() {
     95 		return value;
     96 	}
     97 
     98 	/**
     99 	 * Sets the value to check.
    100 	 *
    101 	 * @param value
    102 	 *            value to check
    103 	 * TODO: use CounterValue directly once Maven 3 is required.
    104 	 */
    105 	public void setValue(final String value) {
    106 		this.value = CounterValue.valueOf(value);
    107 	}
    108 
    109 	/**
    110 	 * @return configured minimum value, or <code>null</code> if no minimum is
    111 	 *         given
    112 	 */
    113 	public String getMinimum() {
    114 		return minimum == null ? null : minimum.toPlainString();
    115 	}
    116 
    117 	/**
    118 	 * Sets allowed minimum value as decimal string representation. The given
    119 	 * precision is also considered in error messages. Coverage ratios are given
    120 	 * in the range from 0.0 to 1.0.
    121 	 *
    122 	 * @param minimum
    123 	 *            allowed minimum or <code>null</code>, if no minimum should be
    124 	 *            checked
    125 	 */
    126 	public void setMinimum(final String minimum) {
    127 		this.minimum = minimum == null ? null : new BigDecimal(minimum);
    128 	}
    129 
    130 	/**
    131 	 * @return configured maximum value, or <code>null</code> if no maximum is
    132 	 *         given
    133 	 */
    134 	public String getMaximum() {
    135 		return maximum == null ? null : maximum.toPlainString();
    136 	}
    137 
    138 	/**
    139 	 * Sets allowed maximum value as decimal string representation. The given
    140 	 * precision is also considered in error messages. Coverage ratios are given
    141 	 * in the range from 0.0 to 1.0.
    142 	 *
    143 	 * @param maximum
    144 	 *            allowed maximum or <code>null</code>, if no maximum should be
    145 	 *            checked
    146 	 */
    147 	public void setMaximum(final String maximum) {
    148 		this.maximum = maximum == null ? null : new BigDecimal(maximum);
    149 	}
    150 
    151 	String check(final ICoverageNode node) {
    152 		final double d = node.getCounter(entity).getValue(value);
    153 		if (Double.isNaN(d)) {
    154 			return null;
    155 		}
    156 		final BigDecimal bd = BigDecimal.valueOf(d);
    157 		if (minimum != null && minimum.compareTo(bd) > 0) {
    158 			return message("minimum", bd, minimum, RoundingMode.FLOOR);
    159 		}
    160 		if (maximum != null && maximum.compareTo(bd) < 0) {
    161 			return message("maximum", bd, maximum, RoundingMode.CEILING);
    162 		}
    163 		return null;
    164 	}
    165 
    166 	private String message(final String minmax, final BigDecimal v,
    167 			final BigDecimal ref, final RoundingMode mode) {
    168 		final BigDecimal rounded = v.setScale(ref.scale(), mode);
    169 		return String.format("%s %s is %s, but expected %s is %s",
    170 				ENTITY_NAMES.get(entity), VALUE_NAMES.get(value),
    171 				rounded.toPlainString(), minmax, ref.toPlainString());
    172 	}
    173 
    174 }
    175