diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2011-02-27 23:49:20 +0100 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2011-02-27 23:50:12 +0100 |
commit | dc45bd7b11b12ba9eff294c8a2de11c5b8fc545e (patch) | |
tree | 3605bd5d809ac6cd55ddca918613e6996dd759dc /plugins | |
parent | cef0256eb03e86d6a2963e384e65f79132a182eb (diff) | |
download | sonarqube-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.java | 4 | ||||
-rw-r--r-- | plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageAggregator.java | 86 | ||||
-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.java | 42 | ||||
-rw-r--r-- | plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/new_coverage.html.erb | 39 | ||||
-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 { |