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    }