aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorsimonbrandhof <simon.brandhof@gmail.com>2011-02-27 23:49:20 +0100
committersimonbrandhof <simon.brandhof@gmail.com>2011-02-27 23:50:12 +0100
commitdc45bd7b11b12ba9eff294c8a2de11c5b8fc545e (patch)
tree3605bd5d809ac6cd55ddca918613e6996dd759dc /plugins
parentcef0256eb03e86d6a2963e384e65f79132a182eb (diff)
downloadsonarqube-dc45bd7b11b12ba9eff294c8a2de11c5b8fc545e.tar.gz
sonarqube-dc45bd7b11b12ba9eff294c8a2de11c5b8fc545e.zip
SONAR-2218 aggregate variations of coverage of changed code + add sample of widget
Diffstat (limited to 'plugins')
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java4
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageAggregator.java86
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzer.java (renamed from plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageDecorator.java)17
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/NewCoverageWidget.java42
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/new_coverage.html.erb39
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzerTest.java (renamed from plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageDecoratorTest.java)23
6 files changed, 187 insertions, 24 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
index a343985c1de..c8f802a0e5d 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
@@ -201,6 +201,7 @@ public class CorePlugin implements Plugin {
extensions.add(SizeWidget.class);
extensions.add(EventsWidget.class);
extensions.add(CustomMeasuresWidget.class);
+ extensions.add(NewCoverageWidget.class);
// chart
extensions.add(XradarChart.class);
@@ -237,7 +238,8 @@ public class CorePlugin implements Plugin {
extensions.add(ViolationPersisterDecorator.class);
extensions.add(NewViolationsDecorator.class);
extensions.add(TimeMachineConfigurationPersister.class);
- extensions.add(NewCoverageDecorator.class);
+ extensions.add(NewCoverageFileAnalyzer.class);
+ extensions.add(NewCoverageAggregator.class);
return extensions;
}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageAggregator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageAggregator.java
new file mode 100644
index 00000000000..f4d56097161
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageAggregator.java
@@ -0,0 +1,86 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.timemachine;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Scopes;
+
+import java.util.Arrays;
+import java.util.List;
+
+public final class NewCoverageAggregator implements Decorator {
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependedUpon
+ public List<Metric> generatesNewCoverageMetrics() {
+ return Arrays.asList(CoreMetrics.NEW_LINES_TO_COVER, CoreMetrics.NEW_UNCOVERED_LINES,
+ CoreMetrics.NEW_CONDITIONS_TO_COVER, CoreMetrics.NEW_UNCOVERED_CONDITIONS);
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldDecorate(resource)) {
+ int maxPeriods = (Qualifiers.isView(resource, true) ? 3 : 5);
+ aggregate(context, CoreMetrics.NEW_LINES_TO_COVER, maxPeriods);
+ aggregate(context, CoreMetrics.NEW_UNCOVERED_LINES, maxPeriods);
+ aggregate(context, CoreMetrics.NEW_CONDITIONS_TO_COVER, maxPeriods);
+ aggregate(context, CoreMetrics.NEW_UNCOVERED_CONDITIONS, maxPeriods);
+ }
+ }
+
+ private void aggregate(DecoratorContext context, Metric metric, int maxPeriods) {
+ int[] variations = {0,0,0,0,0};
+ boolean[] hasValues = {false,false,false,false,false};
+ for (Measure child : context.getChildrenMeasures(metric)) {
+ for (int indexPeriod=1 ; indexPeriod<=maxPeriods ; indexPeriod++) {
+ Double variation = child.getVariation(indexPeriod);
+ if (variation!=null) {
+ variations[indexPeriod-1]=variations[indexPeriod-1] + variation.intValue();
+ hasValues[indexPeriod-1]=true;
+ }
+ }
+ }
+
+ if (ArrayUtils.contains(hasValues, true)) {
+ Measure measure = new Measure(metric);
+ for (int index=0 ; index<5 ; index++) {
+ if (hasValues[index]) {
+ measure.setVariation(index+1, (double)variations[index]);
+ }
+ }
+ context.saveMeasure(measure);
+ }
+ }
+
+ private boolean shouldDecorate(Resource resource) {
+ return Scopes.isHigherThan(resource, Scopes.FILE);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzer.java
index 18297718721..6b7018560b1 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageDecorator.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzer.java
@@ -21,7 +21,6 @@ package org.sonar.plugins.core.timemachine;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import org.apache.commons.lang.NumberUtils;
import org.apache.commons.lang.ObjectUtils;
import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext;
@@ -46,18 +45,18 @@ import java.util.Map;
/**
* @since 2.7
*/
-public final class NewCoverageDecorator implements Decorator {
+public final class NewCoverageFileAnalyzer implements Decorator {
private List<PeriodStruct> structs;
- public NewCoverageDecorator(TimeMachineConfiguration timeMachineConfiguration) {
+ public NewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration) {
structs = Lists.newArrayList();
for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
structs.add(new PeriodStruct(pastSnapshot));
}
}
- NewCoverageDecorator(List<PeriodStruct> structs) {
+ NewCoverageFileAnalyzer(List<PeriodStruct> structs) {
this.structs = structs;
}
@@ -67,7 +66,7 @@ public final class NewCoverageDecorator implements Decorator {
}
private boolean shouldDecorate(Resource resource) {
- return isFile(resource);
+ return Scopes.isFile(resource) && !Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier());
}
@DependsUpon
@@ -156,10 +155,6 @@ public final class NewCoverageDecorator implements Decorator {
}
- private boolean isFile(Resource resource) {
- return Scopes.isFile(resource) && !Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier());
- }
-
public static final class PeriodStruct {
int index;
Date date;
@@ -167,7 +162,7 @@ public final class NewCoverageDecorator implements Decorator {
PeriodStruct(PastSnapshot pastSnapshot) {
this.index = pastSnapshot.getIndex();
- this.date = pastSnapshot.getDate();
+ this.date = pastSnapshot.getTargetDate();
}
PeriodStruct(int index, Date date) {
@@ -187,7 +182,7 @@ public final class NewCoverageDecorator implements Decorator {
//TODO warning
} else if (lineDate.after(date)) {
- // TODO experiment if date comparison is faster or not
+ // TODO test if string comparison is faster or not
addLine(hits > 0);
addConditions(conditions, coveredConditions);
}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/NewCoverageWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/NewCoverageWidget.java
new file mode 100644
index 00000000000..8ca5e63cb4e
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/NewCoverageWidget.java
@@ -0,0 +1,42 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.widgets;
+
+import org.sonar.api.web.AbstractRubyTemplate;
+import org.sonar.api.web.Description;
+import org.sonar.api.web.RubyRailsWidget;
+import org.sonar.api.web.WidgetCategory;
+
+@WidgetCategory({"Tests"})
+@Description("TODO")
+public class NewCoverageWidget extends AbstractRubyTemplate implements RubyRailsWidget {
+ public String getId() {
+ return "new_coverage";
+ }
+
+ public String getTitle() {
+ return "New coverage";
+ }
+
+ @Override
+ protected String getTemplatePath() {
+ return "/org/sonar/plugins/core/widgets/new_coverage.html.erb";
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/new_coverage.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/new_coverage.html.erb
new file mode 100644
index 00000000000..102db31ca17
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/new_coverage.html.erb
@@ -0,0 +1,39 @@
+<h2>New coverage</h2>
+ <table>
+ <tr>
+ <td>New lines</td>
+ <% m=measure('new_lines_to_cover') %>
+ <% 5.times do |index| %>
+ <td>
+ <%= format_variation(m, :index => index+1) -%>
+ </td>
+ <% end %>
+ </tr>
+ <tr>
+ <td>New uncovered</td>
+ <% m=measure('new_uncovered_lines') %>
+ <% 5.times do |index| %>
+ <td>
+ <%= format_variation(m, :index => index+1) -%>
+ </td>
+ <% end %>
+ </tr>
+ <tr>
+ <td>New conditions</td>
+ <% m=measure('new_conditions_to_cover') %>
+ <% 5.times do |index| %>
+ <td>
+ <%= format_variation(m, :index => index+1) -%>
+ </td>
+ <% end %>
+ </tr>
+ <tr>
+ <td>New uncovered conditions</td>
+ <% m=measure('new_uncovered_conditions') %>
+ <% 5.times do |index| %>
+ <td>
+ <%= format_variation(m, :index => index+1) -%>
+ </td>
+ <% end %>
+ </tr>
+ </table>
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzerTest.java
index e94cb5b8518..3eb6a0f27ed 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageDecoratorTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzerTest.java
@@ -19,7 +19,6 @@
*/
package org.sonar.plugins.core.timemachine;
-import org.apache.commons.lang.NumberUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.Test;
@@ -37,7 +36,7 @@ import java.util.List;
import static org.mockito.Mockito.*;
-public class NewCoverageDecoratorTest {
+public class NewCoverageFileAnalyzerTest {
@Test
public void shouldDoNothingIfNoScmData() throws ParseException {
@@ -45,7 +44,7 @@ public class NewCoverageDecoratorTest {
when(context.getMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA))
.thenReturn(new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "1=10"));
- NewCoverageDecorator decorator = newDecorator();
+ NewCoverageFileAnalyzer decorator = newDecorator();
decorator.doDecorate(context);
verify(context, never()).saveMeasure((Measure) anyObject());
}
@@ -56,7 +55,7 @@ public class NewCoverageDecoratorTest {
when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE))
.thenReturn(new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2008-05-18T00:00:00+0000"));
- NewCoverageDecorator decorator = newDecorator();
+ NewCoverageFileAnalyzer decorator = newDecorator();
decorator.doDecorate(context);
verify(context, never()).saveMeasure((Measure) anyObject());
@@ -70,7 +69,7 @@ public class NewCoverageDecoratorTest {
when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE)).thenReturn(
new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2007-01-15T00:00:00+0000;11=2011-01-01T00:00:00+0000"));
- NewCoverageDecorator decorator = newDecorator();
+ NewCoverageFileAnalyzer decorator = newDecorator();
decorator.doDecorate(context);
// line 11 has been updated after date1 (2009-12-25). This line is covered.
@@ -102,7 +101,7 @@ public class NewCoverageDecoratorTest {
when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE)).thenReturn(
new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2007-01-15T00:00:00+0000;11=2011-01-01T00:00:00+0000"));
- NewCoverageDecorator decorator = newDecorator();
+ NewCoverageFileAnalyzer decorator = newDecorator();
decorator.doDecorate(context);
// line 11 has been updated after date1 (2009-12-25). This line has 1 covered condition amongst 4
@@ -134,7 +133,7 @@ public class NewCoverageDecoratorTest {
when(context.getMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE)).thenReturn(
new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "10=2007-01-15T00:00:00+0000;11=2011-01-01T00:00:00+0000"));
- NewCoverageDecorator decorator = newDecorator();
+ NewCoverageFileAnalyzer decorator = newDecorator();
decorator.doDecorate(context);
// line 11 has been updated after date1 (2009-12-25) but it has no conditions
@@ -170,11 +169,11 @@ public class NewCoverageDecoratorTest {
}
}
- private NewCoverageDecorator newDecorator() throws ParseException {
- List<NewCoverageDecorator.PeriodStruct> structs = Arrays.asList(
- new NewCoverageDecorator.PeriodStruct(1, newDate("2009-12-25")),
- new NewCoverageDecorator.PeriodStruct(3, newDate("2011-02-18")));
- return new NewCoverageDecorator(structs);
+ private NewCoverageFileAnalyzer newDecorator() throws ParseException {
+ List<NewCoverageFileAnalyzer.PeriodStruct> structs = Arrays.asList(
+ new NewCoverageFileAnalyzer.PeriodStruct(1, newDate("2009-12-25")),
+ new NewCoverageFileAnalyzer.PeriodStruct(3, newDate("2011-02-18")));
+ return new NewCoverageFileAnalyzer(structs);
}
private Date newDate(String s) throws ParseException {