package hudson.plugins.cobertura.targets; import gnu.trove.iterator.TIntObjectIterator; import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.TIntObjectHashMap; import hudson.plugins.cobertura.Ratio; import java.io.Serializable; import java.util.EnumMap; import java.util.Map; /** * Line-by-line coverage information. * * @author Stephen Connolly * @since 29-Aug-2007 17:44:29 */ public class CoveragePaint implements Serializable { /** * Generated */ private static final long serialVersionUID = -6265259191856193735L; private static final CoveragePaintDetails[] EMPTY = new CoveragePaintDetails[0]; private static class CoveragePaintDetails implements Serializable { /** * Generated */ private static final long serialVersionUID = -9097537016381444671L; /** * Fly-weight object pool of (n,0,0) instances, which are very common. */ private static final CoveragePaintDetails[] CONSTANTS = new CoveragePaintDetails[128]; /** * Number of times this line is executed. */ final int hitCount; static CoveragePaintDetails create(int hitCount, int branchCount, int branchCoverage) { if (branchCount == 0 && branchCoverage == 0) { if (0 <= hitCount && hitCount < CONSTANTS.length) { CoveragePaintDetails r = CONSTANTS[hitCount]; if (r == null) { CONSTANTS[hitCount] = r = new CoveragePaintDetails(hitCount); } return r; } return new CoveragePaintDetails(hitCount); } else { return new BranchingCoveragePaintDetails(hitCount, branchCount, branchCoverage); } } private CoveragePaintDetails(int hitCount) { this.hitCount = hitCount; } int branchCount() { return 0; } int branchCoverage() { return 0; } /** * Do 'this+that' and return the new object. */ CoveragePaintDetails add(CoveragePaintDetails that) { return CoveragePaintDetails.create( this.hitCount + that.hitCount, // TODO find a better algorithm Math.max(this.branchCount(), that.branchCount()), Math.max(this.branchCoverage(), that.branchCoverage())); } } /** * {@link CoveragePaintDetails} that has non-zero branch coverage numbers. * This is relatively rare, so we use two classes to save 8 bytes of storing 0. */ private static class BranchingCoveragePaintDetails extends CoveragePaintDetails { final int branchCount; final int branchCoverage; private BranchingCoveragePaintDetails(int hitCount, int branchCount, int branchCoverage) { super(hitCount); this.branchCount = branchCount; this.branchCoverage = branchCoverage; } @Override int branchCount() { return branchCount; } @Override int branchCoverage() { return branchCoverage; } private static final long serialVersionUID = 1L; } protected TIntObjectMap<CoveragePaintDetails> lines = new TIntObjectHashMap<CoveragePaintDetails>(); public CoveragePaint(CoverageElement source) { // there were no getters against the source ... // this.source = source; } private void paint(int line, CoveragePaintDetails delta) { CoveragePaintDetails d = lines.get(line); if (d == null) { lines.put(line, delta); } else { lines.put(line, d.add(delta)); } } public void paint(int line, int hits) { paint(line, CoveragePaintDetails.create(hits, 0, 0)); } public void paint(int line, int hits, int branchCover, int branchCount) { paint(line, CoveragePaintDetails.create(hits, branchCount, branchCover)); } public void add(CoveragePaint child) { TIntObjectIterator<CoveragePaintDetails> it = child.lines.iterator(); while (it.hasNext()) { it.advance(); paint(it.key(), it.value()); } } /** * Getter for property 'lineCoverage'. * * @return Value for property 'lineCoverage'. */ public Ratio getLineCoverage() { int covered = 0; for (CoveragePaintDetails d : lines.values(EMPTY)) { if (d.hitCount > 0) { covered++; } } return Ratio.create(covered, lines.size()); } /** * Getter for property 'conditionalCoverage'. * * @return Value for property 'conditionalCoverage'. */ public Ratio getConditionalCoverage() { long maxTotal = 0; long total = 0; for (CoveragePaintDetails d : lines.values(EMPTY)) { maxTotal += d.branchCount(); total += d.branchCoverage(); } return Ratio.create(total, maxTotal); } /** * Getter for property 'results'. * * @return Value for property 'results'. */ public Map<CoverageMetric, Ratio> getResults() { Map<CoverageMetric, Ratio> result = new EnumMap<CoverageMetric, Ratio>(CoverageMetric.class); result.put(CoverageMetric.LINE, getLineCoverage()); result.put(CoverageMetric.CONDITIONAL, getConditionalCoverage()); return result; } public boolean isPainted(int line) { return lines.get(line) != null; } public int getHits(int line) { CoveragePaintDetails d = lines.get(line); if (d == null) { return 0; } else { return d.hitCount; } } public int getBranchTotal(int line) { CoveragePaintDetails d = lines.get(line); if (d == null) { return 0; } else { return d.branchCount(); } } public int getBranchCoverage(int line) { CoveragePaintDetails d = lines.get(line); if (d == null) { return 0; } else { return d.branchCoverage(); } } }