]> source.dussan.org Git - sonarqube.git/blob
a5677b6a55c2b9ac479fa04fa56c2e53f18f42cc
[sonarqube.git] /
1 /*
2  * SonarQube, open source software quality management tool.
3  * Copyright (C) 2008-2013 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
6  * SonarQube is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * SonarQube is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.plugins.core.timemachine;
21
22 import com.google.common.collect.Lists;
23 import com.google.common.collect.Maps;
24 import org.apache.commons.lang.ObjectUtils;
25 import org.sonar.api.batch.*;
26 import org.sonar.api.measures.CoreMetrics;
27 import org.sonar.api.measures.Measure;
28 import org.sonar.api.measures.Metric;
29 import org.sonar.api.resources.Project;
30 import org.sonar.api.resources.Qualifiers;
31 import org.sonar.api.resources.Resource;
32 import org.sonar.api.resources.Scopes;
33 import org.sonar.api.utils.KeyValueFormat;
34 import org.sonar.batch.components.Period;
35 import org.sonar.batch.components.TimeMachineConfiguration;
36 import org.sonar.core.DryRunIncompatible;
37
38 import java.util.Arrays;
39 import java.util.Date;
40 import java.util.List;
41 import java.util.Map;
42
43 /**
44  * @since 2.7
45  */
46 @DryRunIncompatible
47 @DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE)
48 public abstract class AbstractNewCoverageFileAnalyzer implements Decorator {
49
50   private List<PeriodStruct> structs;
51
52   public AbstractNewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration) {
53     structs = Lists.newArrayList();
54     for (Period period : timeMachineConfiguration.periods()) {
55       structs.add(new PeriodStruct(period.getIndex(), period.getTargetDate()));
56     }
57   }
58
59   AbstractNewCoverageFileAnalyzer(List<PeriodStruct> structs) {
60     this.structs = structs;
61   }
62
63   public abstract Metric getCoverageLineHitsDataMetric();
64
65   public abstract Metric getConditionsByLineMetric();
66
67   public abstract Metric getCoveredConditionsByLineMetric();
68
69   public abstract Metric getNewLinesToCoverMetric();
70
71   public abstract Metric getNewUncoveredLinesMetric();
72
73   public abstract Metric getNewConditionsToCoverMetric();
74
75   public abstract Metric getNewUncoveredConditionsMetric();
76
77   public boolean shouldExecuteOnProject(Project project) {
78     return !structs.isEmpty();
79   }
80
81   private boolean shouldDecorate(Resource resource) {
82     return Scopes.isFile(resource) && !Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier());
83   }
84
85   @DependsUpon
86   public List<Metric> dependsOnMetrics() {
87
88     return Arrays.asList(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE,
89       getCoverageLineHitsDataMetric(), getConditionsByLineMetric(), getCoveredConditionsByLineMetric());
90   }
91
92   @DependedUpon
93   public List<Metric> generatesNewCoverageMetrics() {
94     return Arrays.asList(getNewLinesToCoverMetric(), getNewUncoveredLinesMetric(), getNewConditionsToCoverMetric(), getNewUncoveredConditionsMetric());
95   }
96
97   public void decorate(Resource resource, DecoratorContext context) {
98     if (shouldDecorate(resource)) {
99       doDecorate(context);
100     }
101   }
102
103   void doDecorate(DecoratorContext context) {
104     if (parse(context)) {
105       compute(context);
106     }
107   }
108
109   private boolean parse(DecoratorContext context) {
110     Measure lastCommits = context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE);
111     Measure hitsByLineMeasure = context.getMeasure(getCoverageLineHitsDataMetric());
112
113     if (lastCommits != null && lastCommits.hasData() && hitsByLineMeasure != null && hitsByLineMeasure.hasData()) {
114       Map<Integer, Date> datesByLine = KeyValueFormat.parseIntDateTime(lastCommits.getData());
115       Map<Integer, Integer> hitsByLine = parseCountByLine(hitsByLineMeasure);
116       Map<Integer, Integer> conditionsByLine = parseCountByLine(context.getMeasure(getConditionsByLineMetric()));
117       Map<Integer, Integer> coveredConditionsByLine = parseCountByLine(context.getMeasure(getCoveredConditionsByLineMetric()));
118
119       reset();
120
121       for (Map.Entry<Integer, Integer> entry : hitsByLine.entrySet()) {
122         int lineId = entry.getKey();
123         int hits = entry.getValue();
124         int conditions = (Integer) ObjectUtils.defaultIfNull(conditionsByLine.get(lineId), 0);
125         int coveredConditions = (Integer) ObjectUtils.defaultIfNull(coveredConditionsByLine.get(lineId), 0);
126         Date date = datesByLine.get(lineId);
127         for (PeriodStruct struct : structs) {
128           struct.analyze(date, hits, conditions, coveredConditions);
129         }
130       }
131
132       return true;
133     }
134     return false;
135   }
136
137   private void reset() {
138     for (PeriodStruct struct : structs) {
139       struct.reset();
140     }
141   }
142
143   private void compute(DecoratorContext context) {
144     Measure newLines = new Measure(getNewLinesToCoverMetric());
145     Measure newUncoveredLines = new Measure(getNewUncoveredLinesMetric());
146     Measure newConditions = new Measure(getNewConditionsToCoverMetric());
147     Measure newUncoveredConditions = new Measure(getNewUncoveredConditionsMetric());
148
149     for (PeriodStruct struct : structs) {
150       if (struct.hasNewCode()) {
151         newLines.setVariation(struct.index, (double) struct.getNewLines());
152         newUncoveredLines.setVariation(struct.index, (double) (struct.getNewLines() - struct.getNewCoveredLines()));
153         newConditions.setVariation(struct.index, (double) struct.getNewConditions());
154         newUncoveredConditions.setVariation(struct.index, (double) struct.getNewConditions() - struct.getNewCoveredConditions());
155       }
156     }
157
158     context.saveMeasure(newLines);
159     context.saveMeasure(newUncoveredLines);
160     context.saveMeasure(newConditions);
161     context.saveMeasure(newUncoveredConditions);
162   }
163
164   private Map<Integer, Integer> parseCountByLine(Measure measure) {
165     if (measure != null && measure.hasData()) {
166       return KeyValueFormat.parseIntInt(measure.getData());
167     }
168     return Maps.newHashMap();
169   }
170
171   public static final class PeriodStruct {
172     int index;
173     Date date;
174     Integer newLines;
175     Integer newCoveredLines;
176     Integer newConditions;
177     Integer newCoveredConditions;
178
179     PeriodStruct(int index, Date date) {
180       this.index = index;
181       this.date = date;
182     }
183
184     void reset() {
185       newLines = null;
186       newCoveredLines = null;
187       newConditions = null;
188       newCoveredConditions = null;
189     }
190
191     void analyze(Date lineDate, int hits, int conditions, int coveredConditions) {
192       if (lineDate == null) {
193         // TODO warning
194
195       } else if (date == null || lineDate.after(date)) {
196         // TODO test if string comparison is faster or not
197         addLine(hits > 0);
198         addConditions(conditions, coveredConditions);
199       }
200     }
201
202     void addLine(boolean covered) {
203       if (newLines == null) {
204         newLines = 0;
205       }
206       newLines += 1;
207       if (covered) {
208         if (newCoveredLines == null) {
209           newCoveredLines = 0;
210         }
211         newCoveredLines += 1;
212       }
213     }
214
215     void addConditions(int count, int countCovered) {
216       if (newConditions == null) {
217         newConditions = 0;
218       }
219       newConditions += count;
220       if (count > 0) {
221         if (newCoveredConditions == null) {
222           newCoveredConditions = 0;
223         }
224         newCoveredConditions += countCovered;
225       }
226     }
227
228     boolean hasNewCode() {
229       return newLines != null;
230     }
231
232     public int getNewLines() {
233       return newLines != null ? newLines : 0;
234     }
235
236     public int getNewCoveredLines() {
237       return newCoveredLines != null ? newCoveredLines : 0;
238     }
239
240     public int getNewConditions() {
241       return newConditions != null ? newConditions : 0;
242     }
243
244     public int getNewCoveredConditions() {
245       return newCoveredConditions != null ? newCoveredConditions : 0;
246     }
247   }
248 }