001 /* 002 * SonarQube, open source software quality management tool. 003 * Copyright (C) 2008-2014 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * SonarQube is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * SonarQube is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020 package org.sonar.api.batch.sensor.test.internal; 021 022 import com.google.common.base.Preconditions; 023 import com.google.common.collect.Maps; 024 import org.sonar.api.batch.fs.InputFile; 025 import org.sonar.api.batch.fs.InputFile.Type; 026 import org.sonar.api.batch.sensor.SensorStorage; 027 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; 028 import org.sonar.api.batch.sensor.test.Coverage; 029 import org.sonar.api.utils.KeyValueFormat; 030 031 import java.util.SortedMap; 032 033 public final class DefaultCoverage implements Coverage { 034 035 private static final String INPUT_FILE_SHOULD_BE_NON_NULL = "InputFile should be non null"; 036 037 private InputFile file; 038 private CoverageType type; 039 private int totalCoveredLines = 0, totalConditions = 0, totalCoveredConditions = 0; 040 private SortedMap<Integer, Integer> hitsByLine = Maps.newTreeMap(); 041 private SortedMap<Integer, Integer> conditionsByLine = Maps.newTreeMap(); 042 private SortedMap<Integer, Integer> coveredConditionsByLine = Maps.newTreeMap(); 043 044 protected final transient SensorStorage storage; 045 private transient boolean saved = false; 046 047 public DefaultCoverage() { 048 this.storage = null; 049 } 050 051 public DefaultCoverage(SensorStorage storage) { 052 this.storage = storage; 053 } 054 055 @Override 056 public DefaultCoverage lineHits(int lineId, int hits) { 057 Preconditions.checkArgument(lineId >= 1, "Line number should be positive and non zero [" + file.relativePath() + ":" + lineId + "]"); 058 Preconditions.checkArgument(hits >= 0, "Hits should be positive [" + file.relativePath() + ":" + lineId + "]"); 059 Preconditions.checkArgument(!hitsByLine.containsKey(lineId), "Hits already saved on line [" + file.relativePath() + ":" + lineId + "]"); 060 hitsByLine.put(lineId, hits); 061 if (hits > 0) { 062 totalCoveredLines += 1; 063 } 064 return this; 065 } 066 067 @Override 068 public DefaultCoverage conditions(int lineId, int conditions, int coveredConditions) { 069 Preconditions.checkArgument(lineId >= 1, "Line number should be positive and non zero [" + file.relativePath() + ":" + lineId + "]"); 070 Preconditions.checkArgument(conditions >= 0, "Number of conditions should be positive [" + file.relativePath() + ":" + lineId + "]"); 071 Preconditions.checkArgument(coveredConditions >= 0, "Number of covered conditions should be positive [" + file.relativePath() + ":" + lineId + "]"); 072 Preconditions.checkArgument(conditions >= coveredConditions, "Number of covered conditions can't exceed conditions [" + file.relativePath() + ":" + lineId + "]"); 073 Preconditions.checkArgument(!conditionsByLine.containsKey(lineId), "Conditions already saved on line [" + file.relativePath() + ":" + lineId + "]"); 074 totalConditions += conditions; 075 totalCoveredConditions += coveredConditions; 076 conditionsByLine.put(lineId, conditions); 077 coveredConditionsByLine.put(lineId, coveredConditions); 078 return this; 079 } 080 081 public InputFile file() { 082 return file; 083 } 084 085 @Override 086 public DefaultCoverage onFile(InputFile inputFile) { 087 Preconditions.checkNotNull(inputFile, INPUT_FILE_SHOULD_BE_NON_NULL); 088 Preconditions.checkArgument(inputFile.type() == Type.MAIN, "Coverage is only supported on main files [" + inputFile.relativePath() + "]"); 089 this.file = inputFile; 090 return this; 091 } 092 093 public CoverageType type() { 094 return type; 095 } 096 097 @Override 098 public DefaultCoverage ofType(CoverageType type) { 099 Preconditions.checkNotNull(type); 100 this.type = type; 101 return this; 102 } 103 104 @Override 105 public void save() { 106 Preconditions.checkNotNull(this.storage, "No persister on this object"); 107 Preconditions.checkState(!saved, "This object was already saved"); 108 Preconditions.checkNotNull(this.file, "File is mandatory on Coverage"); 109 Preconditions.checkNotNull(this.type, "Type is mandatory on Coverage"); 110 111 if (!hitsByLine.isEmpty()) { 112 new DefaultMeasure<Integer>(storage) 113 .onFile(file) 114 .forMetric(type.linesToCover()) 115 .withValue(hitsByLine.size()) 116 .save(); 117 new DefaultMeasure<Integer>(storage) 118 .onFile(file) 119 .forMetric(type.uncoveredLines()) 120 .withValue(hitsByLine.size() - totalCoveredLines) 121 .save(); 122 new DefaultMeasure<String>(storage) 123 .onFile(file) 124 .forMetric(type.lineHitsData()) 125 .withValue(KeyValueFormat.format(hitsByLine)) 126 .save(); 127 } 128 if (totalConditions > 0) { 129 new DefaultMeasure<Integer>(storage) 130 .onFile(file) 131 .forMetric(type.conditionsToCover()) 132 .withValue(totalConditions) 133 .save(); 134 new DefaultMeasure<Integer>(storage) 135 .onFile(file) 136 .forMetric(type.uncoveredConditions()) 137 .withValue(totalConditions - totalCoveredConditions) 138 .save(); 139 new DefaultMeasure<String>(storage) 140 .onFile(file) 141 .forMetric(type.coveredConditionsByLine()) 142 .withValue(KeyValueFormat.format(coveredConditionsByLine)) 143 .save(); 144 new DefaultMeasure<String>(storage) 145 .onFile(file) 146 .forMetric(type.conditionsByLine()) 147 .withValue(KeyValueFormat.format(conditionsByLine)) 148 .save(); 149 } 150 this.saved = true; 151 } 152 153 }