]> source.dussan.org Git - sonarqube.git/blob
d44ffd056e1c9646a81da437aa54da48b72ed6f9
[sonarqube.git] /
1 /*
2  * Sonar, open source software quality management tool.
3  * Copyright (C) 2008-2011 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
6  * Sonar 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  * Sonar 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
17  * License along with Sonar; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
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.PastSnapshot;
35 import org.sonar.batch.components.TimeMachineConfiguration;
36 import org.sonar.core.NotDryRun;
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 @NotDryRun
47 @DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE)
48 public final class NewCoverageFileAnalyzer implements Decorator {
49
50   private List<PeriodStruct> structs;
51
52   public NewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration) {
53     structs = Lists.newArrayList();
54     for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
55       structs.add(new PeriodStruct(pastSnapshot));
56     }
57   }
58
59   NewCoverageFileAnalyzer(List<PeriodStruct> structs) {
60     this.structs = structs;
61   }
62
63   public boolean shouldExecuteOnProject(Project project) {
64     return project.isLatestAnalysis() && !structs.isEmpty();
65   }
66
67   private boolean shouldDecorate(Resource resource) {
68     return Scopes.isFile(resource) && !Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier());
69   }
70
71   @DependsUpon
72   public List<Metric> dependsOnMetrics() {
73     return Arrays.asList(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, CoreMetrics.COVERAGE_LINE_HITS_DATA,
74         CoreMetrics.CONDITIONS_BY_LINE, CoreMetrics.COVERED_CONDITIONS_BY_LINE);
75   }
76
77   @DependedUpon
78   public List<Metric> generatesNewCoverageMetrics() {
79     return Arrays.asList(CoreMetrics.NEW_LINES_TO_COVER, CoreMetrics.NEW_UNCOVERED_LINES,
80         CoreMetrics.NEW_CONDITIONS_TO_COVER, CoreMetrics.NEW_UNCOVERED_CONDITIONS);
81   }
82
83   public void decorate(Resource resource, DecoratorContext context) {
84     if (shouldDecorate(resource)) {
85       doDecorate(context);
86     }
87   }
88
89   void doDecorate(DecoratorContext context) {
90     if (parse(context)) {
91       compute(context);
92     }
93   }
94
95   private boolean parse(DecoratorContext context) {
96     Measure lastCommits = context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE);
97     Measure hitsByLineMeasure = context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA);
98
99     if (lastCommits != null && lastCommits.hasData() && hitsByLineMeasure != null && hitsByLineMeasure.hasData()) {
100       Map<Integer, Date> datesByLine = KeyValueFormat.parseIntDateTime(lastCommits.getData());
101       Map<Integer, Integer> hitsByLine = parseCountByLine(hitsByLineMeasure);
102       Map<Integer, Integer> conditionsByLine = parseCountByLine(context.getMeasure(CoreMetrics.CONDITIONS_BY_LINE));
103       Map<Integer, Integer> coveredConditionsByLine = parseCountByLine(context.getMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE));
104
105       reset();
106
107       for (Map.Entry<Integer, Integer> entry : hitsByLine.entrySet()) {
108         int lineId = entry.getKey();
109         int hits = entry.getValue();
110         int conditions = (Integer)ObjectUtils.defaultIfNull(conditionsByLine.get(lineId), 0);
111         int coveredConditions = (Integer)ObjectUtils.defaultIfNull(coveredConditionsByLine.get(lineId), 0);
112         Date date = datesByLine.get(lineId);
113         for (PeriodStruct struct : structs) {
114           struct.analyze(date, hits, conditions, coveredConditions);
115         }
116       }
117
118       return true;
119     }
120     return false;
121   }
122
123   private void reset() {
124     for (PeriodStruct struct : structs) {
125       struct.reset();
126     }
127   }
128
129   private void compute(DecoratorContext context) {
130     Measure newLines = new Measure(CoreMetrics.NEW_LINES_TO_COVER);
131     Measure newUncoveredLines = new Measure(CoreMetrics.NEW_UNCOVERED_LINES);
132     Measure newConditions = new Measure(CoreMetrics.NEW_CONDITIONS_TO_COVER);
133     Measure newUncoveredConditions = new Measure(CoreMetrics.NEW_UNCOVERED_CONDITIONS);
134
135     for (PeriodStruct struct : structs) {
136       newLines.setVariation(struct.index, (double)struct.newLines);
137       newUncoveredLines.setVariation(struct.index, (double) (struct.newLines - struct.newCoveredLines));
138       newConditions.setVariation(struct.index, (double)struct.newConditions);
139       newUncoveredConditions.setVariation(struct.index, (double)struct.newConditions-struct.newCoveredConditions);
140     }
141
142     context.saveMeasure(newLines);
143     context.saveMeasure(newUncoveredLines);
144     context.saveMeasure(newConditions);
145     context.saveMeasure(newUncoveredConditions);
146   }
147
148   private Map<Integer, Integer> parseCountByLine(Measure measure) {
149     if (measure != null && measure.hasData()) {
150       return KeyValueFormat.parseIntInt(measure.getData());
151     }
152     return Maps.newHashMap();
153   }
154
155   public static final class PeriodStruct {
156     int index;
157     Date date;
158     int newLines = 0, newCoveredLines = 0, newConditions = 0, newCoveredConditions = 0;
159
160     PeriodStruct(PastSnapshot pastSnapshot) {
161       this.index = pastSnapshot.getIndex();
162       this.date = pastSnapshot.getTargetDate();
163     }
164
165     PeriodStruct(int index, Date date) {
166       this.index = index;
167       this.date = date;
168     }
169
170     void reset() {
171       newLines = 0;
172       newCoveredLines = 0;
173       newConditions = 0;
174       newCoveredConditions = 0;
175     }
176
177     void analyze(Date lineDate, int hits, int conditions, int coveredConditions) {
178       if (lineDate == null) {
179         // TODO warning
180
181       } else if (date == null || lineDate.after(date)) {
182         // TODO test if string comparison is faster or not
183         addLine(hits > 0);
184         addConditions(conditions, coveredConditions);
185       }
186     }
187
188     void addLine(boolean covered) {
189       newLines += 1;
190       if (covered) {
191         newCoveredLines += 1;
192       }
193     }
194
195     void addConditions(int count, int countCovered) {
196       newConditions += count;
197       if (count > 0) {
198         newCoveredConditions += countCovered;
199       }
200     }
201   }
202 }