1 /******************************************************************************* 2 * Copyright (c) 2009, 2017 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>(); values.put(CounterValue.TOTALCOUNT, "total count")34 values.put(CounterValue.TOTALCOUNT, "total count"); values.put(CounterValue.MISSEDCOUNT, "missed count")35 values.put(CounterValue.MISSEDCOUNT, "missed count"); values.put(CounterValue.COVEREDCOUNT, "covered count")36 values.put(CounterValue.COVEREDCOUNT, "covered count"); values.put(CounterValue.MISSEDRATIO, "missed ratio")37 values.put(CounterValue.MISSEDRATIO, "missed ratio"); values.put(CounterValue.COVEREDRATIO, "covered 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>(); entities.put(CounterEntity.INSTRUCTION, "instructions")42 entities.put(CounterEntity.INSTRUCTION, "instructions"); entities.put(CounterEntity.BRANCH, "branches")43 entities.put(CounterEntity.BRANCH, "branches"); entities.put(CounterEntity.COMPLEXITY, "complexity")44 entities.put(CounterEntity.COMPLEXITY, "complexity"); entities.put(CounterEntity.LINE, "lines")45 entities.put(CounterEntity.LINE, "lines"); entities.put(CounterEntity.METHOD, "methods")46 entities.put(CounterEntity.METHOD, "methods"); entities.put(CounterEntity.CLASS, "classes")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 */ Limit()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 */ getEntity()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 */ setCounter(final String entity)87 public void setCounter(final String entity) { 88 this.entity = CounterEntity.valueOf(entity); 89 } 90 91 /** 92 * @return the configured value to check 93 */ getValue()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 */ setValue(final String value)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 */ getMinimum()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 */ setMinimum(final String minimum)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 */ getMaximum()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 */ setMaximum(final String maximum)147 public void setMaximum(final String maximum) { 148 this.maximum = maximum == null ? null : new BigDecimal(maximum); 149 } 150 check(final ICoverageNode node)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 message(final String minmax, final BigDecimal v, final BigDecimal ref, final RoundingMode mode)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