aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorFabrice Bellingard <bellingard@gmail.com>2012-01-17 08:38:01 +0100
committerFabrice Bellingard <bellingard@gmail.com>2012-01-17 08:40:29 +0100
commit09c1adf791f2296fde7f12fa4d92532f3f1229a7 (patch)
treeba8a04d3b66ab0a019f1302f406f3f09cc2eaaf0 /plugins
parentd30b0e381a451abaf2ff5aef3978d595c521bde5 (diff)
downloadsonarqube-09c1adf791f2296fde7f12fa4d92532f3f1229a7.tar.gz
sonarqube-09c1adf791f2296fde7f12fa4d92532f3f1229a7.zip
SONAR-3012 Add new_unreviewed_violations metric
In order to more easily track incoming violations without a review
Diffstat (limited to 'plugins')
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsMeasuresDecorator.java74
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewViolationsDecorator.java63
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ReviewsMeasuresDecoratorTest.java137
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewViolationsDecoratorTest.java11
-rw-r--r--plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties5
5 files changed, 215 insertions, 75 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsMeasuresDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsMeasuresDecorator.java
index c581fce39c8..a2bce1fa64c 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsMeasuresDecorator.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ReviewsMeasuresDecorator.java
@@ -19,20 +19,31 @@
*/
package org.sonar.plugins.core.sensors;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.DependsUpon;
import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasureUtils;
import org.sonar.api.measures.Metric;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
+import org.sonar.api.rules.Violation;
+import org.sonar.batch.components.PastSnapshot;
+import org.sonar.batch.components.TimeMachineConfiguration;
import org.sonar.core.review.ReviewDao;
import org.sonar.core.review.ReviewDto;
import org.sonar.core.review.ReviewQuery;
import org.sonar.plugins.core.timemachine.ViolationTrackingDecorator;
+import com.google.common.collect.Maps;
+
/**
* Decorator that creates measures related to reviews.
*
@@ -42,9 +53,11 @@ import org.sonar.plugins.core.timemachine.ViolationTrackingDecorator;
public class ReviewsMeasuresDecorator implements Decorator {
private ReviewDao reviewDao;
+ private TimeMachineConfiguration timeMachineConfiguration;
- public ReviewsMeasuresDecorator(ReviewDao reviewDao) {
+ public ReviewsMeasuresDecorator(ReviewDao reviewDao, TimeMachineConfiguration timeMachineConfiguration) {
this.reviewDao = reviewDao;
+ this.timeMachineConfiguration = timeMachineConfiguration;
}
public boolean shouldExecuteOnProject(Project project) {
@@ -64,28 +77,35 @@ public class ReviewsMeasuresDecorator implements Decorator {
return;
}
- // Open reviews
+ // Load open reviews (used for counting and also for tracking new violations without a review)
ReviewQuery openReviewQuery = ReviewQuery.create().setResourceId(resource.getId()).addStatus(ReviewDto.STATUS_OPEN)
.addStatus(ReviewDto.STATUS_REOPENED);
- Double resourceOpenReviewsCount = reviewDao.countByQuery(openReviewQuery).doubleValue();
+ List<ReviewDto> openReviews = reviewDao.selectByQuery(openReviewQuery);
+ Map<Integer, ReviewDto> openReviewsByViolationPermanentIds = Maps.newHashMap();
+ for (ReviewDto reviewDto : openReviews) {
+ openReviewsByViolationPermanentIds.put(reviewDto.getViolationPermanentId(), reviewDto);
+ }
+
+ // Count open reviews
+ Double resourceOpenReviewsCount = (double) openReviewsByViolationPermanentIds.size();
Double totalOpenReviewsCount = resourceOpenReviewsCount + getChildrenSum(resource, context, CoreMetrics.ACTIVE_REVIEWS);
context.saveMeasure(CoreMetrics.ACTIVE_REVIEWS, totalOpenReviewsCount);
- // Unassigned reviews
+ // Count unassigned reviews
ReviewQuery unassignedReviewQuery = ReviewQuery.copy(openReviewQuery).setNoAssignee();
Double ressourceUnassignedReviewsCount = reviewDao.countByQuery(unassignedReviewQuery).doubleValue();
Double totalUnassignedReviewsCount = ressourceUnassignedReviewsCount
+ getChildrenSum(resource, context, CoreMetrics.UNASSIGNED_REVIEWS);
context.saveMeasure(CoreMetrics.UNASSIGNED_REVIEWS, totalUnassignedReviewsCount);
- // Unplanned reviews
+ // Count unplanned reviews
ReviewQuery plannedReviewQuery = ReviewQuery.copy(openReviewQuery).setPlanned();
Double resourcePlannedReviewsCount = reviewDao.countByQuery(plannedReviewQuery).doubleValue();
Double childrenUnplannedReviewsCount = getChildrenSum(resource, context, CoreMetrics.UNPLANNED_REVIEWS);
context.saveMeasure(CoreMetrics.UNPLANNED_REVIEWS, (resourceOpenReviewsCount - resourcePlannedReviewsCount)
+ childrenUnplannedReviewsCount);
- // False positive reviews
+ // Count false positive reviews
ReviewQuery falsePositiveReviewQuery = ReviewQuery.create().setResourceId(resource.getId())
.addResolution(ReviewDto.RESOLUTION_FALSE_POSITIVE);
Double resourceFalsePositiveReviewsCount = reviewDao.countByQuery(falsePositiveReviewQuery).doubleValue();
@@ -93,9 +113,42 @@ public class ReviewsMeasuresDecorator implements Decorator {
+ getChildrenSum(resource, context, CoreMetrics.FALSE_POSITIVE_REVIEWS);
context.saveMeasure(CoreMetrics.FALSE_POSITIVE_REVIEWS, totalFalsePositiveReviewsCount);
- // Violations without a review
+ // Count violations without a review
Double violationsCount = MeasureUtils.getValue(context.getMeasure(CoreMetrics.VIOLATIONS), 0.0);
context.saveMeasure(CoreMetrics.VIOLATIONS_WITHOUT_REVIEW, violationsCount - totalOpenReviewsCount);
+
+ // And finally track new violations without a review
+ trackNewViolationsWithoutReview(context, openReviewsByViolationPermanentIds);
+ }
+
+ protected void trackNewViolationsWithoutReview(DecoratorContext context, Map<Integer, ReviewDto> openReviewsByViolationPermanentIds) {
+ List<Violation> violations = context.getViolations();
+ Measure measure = new Measure(CoreMetrics.NEW_VIOLATIONS_WITHOUT_REVIEW);
+ for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
+ int newUnreviewedViolations = countNewUnreviewedViolationsForSnapshot(pastSnapshot, violations, openReviewsByViolationPermanentIds);
+ int variationIndex = pastSnapshot.getIndex();
+ Collection<Measure> children = context.getChildrenMeasures(CoreMetrics.NEW_VIOLATIONS_WITHOUT_REVIEW);
+ double sumNewUnreviewedViolations = MeasureUtils.sumOnVariation(true, variationIndex, children) + newUnreviewedViolations;
+ measure.setVariation(variationIndex, sumNewUnreviewedViolations);
+ }
+ context.saveMeasure(measure);
+ }
+
+ protected int countNewUnreviewedViolationsForSnapshot(PastSnapshot pastSnapshot, List<Violation> violations,
+ Map<Integer, ReviewDto> openReviewsByViolationPermanentIds) {
+ Date targetDate = pastSnapshot.getTargetDate();
+ int newViolationCount = 0;
+ int newReviewedViolationCount = 0;
+ for (Violation violation : violations) {
+ if (isAfter(violation, targetDate)) {
+ newViolationCount += 1;
+ if (openReviewsByViolationPermanentIds.get(violation.getPermanentId()) != null) {
+ newReviewedViolationCount += 1;
+ }
+ }
+ }
+ int newUnreviewedViolations = newViolationCount - newReviewedViolationCount;
+ return newUnreviewedViolations;
}
private Double getChildrenSum(Resource<?> resource, DecoratorContext context, Metric metric) {
@@ -106,4 +159,11 @@ public class ReviewsMeasuresDecorator implements Decorator {
return sum;
}
+ private boolean isAfter(Violation violation, Date date) {
+ if (date == null) {
+ return true;
+ }
+ return violation.getCreatedAt() != null && violation.getCreatedAt().after(date);
+ }
+
}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewViolationsDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewViolationsDecorator.java
index b54529a6019..64005d358bd 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewViolationsDecorator.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewViolationsDecorator.java
@@ -19,12 +19,24 @@
*/
package org.sonar.plugins.core.timemachine;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.*;
-import org.sonar.api.measures.*;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorBarriers;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.MeasuresFilters;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.RuleMeasure;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
@@ -35,7 +47,9 @@ import org.sonar.api.rules.Violation;
import org.sonar.batch.components.PastSnapshot;
import org.sonar.batch.components.TimeMachineConfiguration;
-import java.util.*;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Sets;
@DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING)
public class NewViolationsDecorator implements Decorator {
@@ -53,12 +67,12 @@ public class NewViolationsDecorator implements Decorator {
@DependedUpon
public List<Metric> generatesMetric() {
return Arrays.asList(
- CoreMetrics.NEW_VIOLATIONS,
- CoreMetrics.NEW_BLOCKER_VIOLATIONS,
- CoreMetrics.NEW_CRITICAL_VIOLATIONS,
- CoreMetrics.NEW_MAJOR_VIOLATIONS,
- CoreMetrics.NEW_MINOR_VIOLATIONS,
- CoreMetrics.NEW_INFO_VIOLATIONS);
+ CoreMetrics.NEW_VIOLATIONS,
+ CoreMetrics.NEW_BLOCKER_VIOLATIONS,
+ CoreMetrics.NEW_CRITICAL_VIOLATIONS,
+ CoreMetrics.NEW_MAJOR_VIOLATIONS,
+ CoreMetrics.NEW_MINOR_VIOLATIONS,
+ CoreMetrics.NEW_INFO_VIOLATIONS);
}
public void decorate(Resource resource, DecoratorContext context) {
@@ -70,10 +84,10 @@ public class NewViolationsDecorator implements Decorator {
}
private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
- return
- (StringUtils.equals(Scopes.PROJECT, resource.getScope()) || StringUtils.equals(Scopes.DIRECTORY, resource.getScope()) || StringUtils.equals(Scopes.FILE, resource.getScope()))
- && !ResourceUtils.isUnitTestClass(resource)
- && context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null;
+ return (StringUtils.equals(Scopes.PROJECT, resource.getScope()) || StringUtils.equals(Scopes.DIRECTORY, resource.getScope()) || StringUtils
+ .equals(Scopes.FILE, resource.getScope()))
+ && !ResourceUtils.isUnitTestClass(resource)
+ && context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null;
}
private void computeNewViolations(DecoratorContext context) {
@@ -82,7 +96,7 @@ public class NewViolationsDecorator implements Decorator {
int variationIndex = pastSnapshot.getIndex();
Collection<Measure> children = context.getChildrenMeasures(CoreMetrics.NEW_VIOLATIONS);
int count = countViolations(context.getViolations(), pastSnapshot.getTargetDate());
- double sum = sumChildren(variationIndex, children) + count;
+ double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + count;
measure.setVariation(variationIndex, sum);
}
context.saveMeasure(measure);
@@ -101,7 +115,7 @@ public class NewViolationsDecorator implements Decorator {
int variationIndex = pastSnapshot.getIndex();
int count = countViolations(violationsPerSeverities.get(severity), pastSnapshot.getTargetDate());
Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric));
- double sum = sumChildren(variationIndex, children) + count;
+ double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + count;
measure.setVariation(variationIndex, sum);
}
context.saveMeasure(measure);
@@ -138,7 +152,7 @@ public class NewViolationsDecorator implements Decorator {
for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
int variationIndex = pastSnapshot.getIndex();
int count = countViolations(violationsPerRule.get(rule), pastSnapshot.getTargetDate());
- double sum = sumChildren(variationIndex, childMeasuresPerRule.get(rule)) + count;
+ double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRule.get(rule)) + count;
measure.setVariation(variationIndex, sum);
}
context.saveMeasure(measure);
@@ -146,17 +160,6 @@ public class NewViolationsDecorator implements Decorator {
}
}
- int sumChildren(int variationIndex, Collection<Measure> measures) {
- int sum = 0;
- for (Measure measure : measures) {
- Double var = measure.getVariation(variationIndex);
- if (var != null) {
- sum += var.intValue();
- }
- }
- return sum;
- }
-
int countViolations(Collection<Violation> violations, Date targetDate) {
if (violations == null) {
return 0;
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ReviewsMeasuresDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ReviewsMeasuresDecoratorTest.java
index 21938e80a44..533be2b0ae6 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ReviewsMeasuresDecoratorTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ReviewsMeasuresDecoratorTest.java
@@ -30,36 +30,86 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.time.DateUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
+import org.junit.Before;
import org.junit.Test;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.RuleMeasure;
import org.sonar.api.resources.File;
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 org.sonar.api.rules.Rule;
+import org.sonar.api.rules.Violation;
+import org.sonar.batch.components.PastSnapshot;
+import org.sonar.batch.components.TimeMachineConfiguration;
import org.sonar.core.review.ReviewDao;
import org.sonar.core.review.ReviewDto;
import org.sonar.core.review.ReviewQuery;
import org.sonar.plugins.core.timemachine.ViolationTrackingDecorator;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
public class ReviewsMeasuresDecoratorTest {
+ private ReviewDao reviewDao;
+ private ReviewsMeasuresDecorator decorator;
+ private DecoratorContext context;
+
+ private Date rightNow;
+ private Date tenDaysAgo;
+ private Date fiveDaysAgo;
+
+ @Before
+ public void setUp() {
+ reviewDao = mock(ReviewDao.class);
+ when(reviewDao.selectByQuery(argThat(openReviewQueryMatcher()))).thenReturn(createListOf10Reviews());
+ when(reviewDao.countByQuery(argThat(unassignedReviewQueryMatcher()))).thenReturn(2);
+ when(reviewDao.countByQuery(argThat(plannedReviewQueryMatcher()))).thenReturn(3);
+ when(reviewDao.countByQuery(argThat(falsePositiveReviewQueryMatcher()))).thenReturn(4);
+
+ rightNow = new Date();
+ tenDaysAgo = DateUtils.addDays(rightNow, -10);
+ fiveDaysAgo = DateUtils.addDays(rightNow, -5);
+
+ PastSnapshot pastSnapshot = mock(PastSnapshot.class);
+ when(pastSnapshot.getIndex()).thenReturn(1);
+ when(pastSnapshot.getTargetDate()).thenReturn(fiveDaysAgo);
+
+ PastSnapshot pastSnapshot2 = mock(PastSnapshot.class);
+ when(pastSnapshot2.getIndex()).thenReturn(2);
+ when(pastSnapshot2.getTargetDate()).thenReturn(tenDaysAgo);
+
+ TimeMachineConfiguration timeMachineConfiguration = mock(TimeMachineConfiguration.class);
+ when(timeMachineConfiguration.getProjectPastSnapshots()).thenReturn(Arrays.asList(pastSnapshot, pastSnapshot2));
+
+ decorator = new ReviewsMeasuresDecorator(reviewDao, timeMachineConfiguration);
+ context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.VIOLATIONS)).thenReturn(new Measure(CoreMetrics.VIOLATIONS, 35d));
+ }
+
@Test
public void testDependsUponViolationTracking() throws Exception {
- ReviewsMeasuresDecorator decorator = new ReviewsMeasuresDecorator(null);
+ ReviewsMeasuresDecorator decorator = new ReviewsMeasuresDecorator(null, null);
assertEquals(decorator.dependsUponViolationTracking(), ViolationTrackingDecorator.class);
}
@Test
public void shouldExecuteOnProject() throws Exception {
- ReviewsMeasuresDecorator decorator = new ReviewsMeasuresDecorator(null);
+ ReviewsMeasuresDecorator decorator = new ReviewsMeasuresDecorator(null, null);
Project project = new Project("foo");
project.setLatestAnalysis(true);
assertThat(decorator.shouldExecuteOnProject(project), is(true));
@@ -67,7 +117,7 @@ public class ReviewsMeasuresDecoratorTest {
@Test
public void shouldDecoratePersistableResource() throws Exception {
- ReviewsMeasuresDecorator decorator = new ReviewsMeasuresDecorator(null);
+ ReviewsMeasuresDecorator decorator = new ReviewsMeasuresDecorator(null, null);
DecoratorContext context = mock(DecoratorContext.class);
Resource<?> resource = mock(Resource.class);
when(resource.getScope()).thenReturn(Scopes.BLOCK_UNIT);
@@ -77,7 +127,7 @@ public class ReviewsMeasuresDecoratorTest {
@Test
public void shouldNotDecorateUnitTest() throws Exception {
- ReviewsMeasuresDecorator decorator = new ReviewsMeasuresDecorator(null);
+ ReviewsMeasuresDecorator decorator = new ReviewsMeasuresDecorator(null, null);
DecoratorContext context = mock(DecoratorContext.class);
Resource<?> resource = mock(Resource.class);
when(resource.getScope()).thenReturn(Scopes.FILE);
@@ -87,17 +137,8 @@ public class ReviewsMeasuresDecoratorTest {
}
@Test
- public void shouldComputeReviewMetricsOnFile() throws Exception {
- ReviewDao reviewDao = mock(ReviewDao.class);
- when(reviewDao.countByQuery(argThat(openReviewQueryMatcher()))).thenReturn(10);
- when(reviewDao.countByQuery(argThat(unassignedReviewQueryMatcher()))).thenReturn(2);
- when(reviewDao.countByQuery(argThat(plannedReviewQueryMatcher()))).thenReturn(3);
- when(reviewDao.countByQuery(argThat(falsePositiveReviewQueryMatcher()))).thenReturn(4);
-
- ReviewsMeasuresDecorator decorator = new ReviewsMeasuresDecorator(reviewDao);
+ public void shouldComputeReviewsMetricsOnFile() throws Exception {
Resource<?> resource = new File("foo").setId(1);
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getMeasure(CoreMetrics.VIOLATIONS)).thenReturn(new Measure(CoreMetrics.VIOLATIONS, 35d));
decorator.decorate(resource, context);
verify(context).saveMeasure(CoreMetrics.ACTIVE_REVIEWS, 10d);
@@ -108,18 +149,7 @@ public class ReviewsMeasuresDecoratorTest {
}
@Test
- public void shouldComputeReviewMetricsOnProject() throws Exception {
- ReviewDao reviewDao = mock(ReviewDao.class);
- // Same 4 values used as for #shouldComputeReviewMetricsOnFile
- when(reviewDao.countByQuery(argThat(openReviewQueryMatcher()))).thenReturn(10);
- when(reviewDao.countByQuery(argThat(unassignedReviewQueryMatcher()))).thenReturn(2);
- when(reviewDao.countByQuery(argThat(plannedReviewQueryMatcher()))).thenReturn(3);
- when(reviewDao.countByQuery(argThat(falsePositiveReviewQueryMatcher()))).thenReturn(4);
-
- ReviewsMeasuresDecorator decorator = new ReviewsMeasuresDecorator(reviewDao);
- Resource<?> resource = new Project("foo").setId(1);
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getMeasure(CoreMetrics.VIOLATIONS)).thenReturn(new Measure(CoreMetrics.VIOLATIONS, 35d));
+ public void shouldComputeReviewsMetricsOnProject() throws Exception {
when(context.getChildrenMeasures(CoreMetrics.ACTIVE_REVIEWS)).thenReturn(
Lists.newArrayList(new Measure(CoreMetrics.ACTIVE_REVIEWS, 7d)));
when(context.getChildrenMeasures(CoreMetrics.UNASSIGNED_REVIEWS)).thenReturn(
@@ -128,6 +158,8 @@ public class ReviewsMeasuresDecoratorTest {
Lists.newArrayList(new Measure(CoreMetrics.UNPLANNED_REVIEWS, 2d)));
when(context.getChildrenMeasures(CoreMetrics.FALSE_POSITIVE_REVIEWS)).thenReturn(
Lists.newArrayList(new Measure(CoreMetrics.FALSE_POSITIVE_REVIEWS, 2d)));
+
+ Resource<?> resource = new Project("foo").setId(1);
decorator.decorate(resource, context);
// As same values used for #shouldComputeReviewMetricsOnFile, we just add the children measures to verify
@@ -138,6 +170,33 @@ public class ReviewsMeasuresDecoratorTest {
verify(context).saveMeasure(CoreMetrics.VIOLATIONS_WITHOUT_REVIEW, 35d - (10d + 7d));
}
+ @Test
+ public void shouldTrackNewViolationsWithoutReview() throws Exception {
+ Resource<?> resource = new File("foo").setId(1);
+ Violation v1 = Violation.create((Rule) null, resource).setPermanentId(1); // test the null case for the created_at date
+ Violation v2 = Violation.create((Rule) null, resource).setPermanentId(2).setCreatedAt(rightNow);
+ Violation v3 = Violation.create((Rule) null, resource).setPermanentId(3).setCreatedAt(fiveDaysAgo);
+ Violation v4 = Violation.create((Rule) null, resource).setPermanentId(4).setCreatedAt(fiveDaysAgo);
+ Violation v5 = Violation.create((Rule) null, resource).setPermanentId(5).setCreatedAt(fiveDaysAgo);
+ Violation v6 = Violation.create((Rule) null, resource).setPermanentId(6).setCreatedAt(tenDaysAgo);
+ when(context.getViolations()).thenReturn(Arrays.asList(v1, v2, v3, v4, v5, v6));
+
+ Map<Integer, ReviewDto> openReviewsByViolationPermanentIds = Maps.newHashMap();
+ openReviewsByViolationPermanentIds.put(1, new ReviewDto());
+ openReviewsByViolationPermanentIds.put(3, new ReviewDto());
+
+ decorator.trackNewViolationsWithoutReview(context, openReviewsByViolationPermanentIds);
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_VIOLATIONS_WITHOUT_REVIEW, 1.0, 3.0)));
+ }
+
+ private List<ReviewDto> createListOf10Reviews() {
+ List<ReviewDto> reviews = Lists.newArrayList();
+ for (int i = 1; i < 11; i++) {
+ reviews.add(new ReviewDto().setViolationPermanentId(i));
+ }
+ return reviews;
+ }
+
private BaseMatcher<ReviewQuery> openReviewQueryMatcher() {
return new BaseMatcher<ReviewQuery>() {
public boolean matches(Object o) {
@@ -200,4 +259,30 @@ public class ReviewsMeasuresDecoratorTest {
}
};
}
+
+ private class IsVariationMeasure extends BaseMatcher<Measure> {
+ private Metric metric = null;
+ private Double var1 = null;
+ private Double var2 = null;
+
+ public IsVariationMeasure(Metric metric, Double var1, Double var2) {
+ this.metric = metric;
+ this.var1 = var1;
+ this.var2 = var2;
+ }
+
+ public boolean matches(Object o) {
+ if (!(o instanceof Measure)) {
+ return false;
+ }
+ Measure m = (Measure) o;
+ return ObjectUtils.equals(metric, m.getMetric()) &&
+ ObjectUtils.equals(var1, m.getVariation1()) &&
+ ObjectUtils.equals(var2, m.getVariation2()) &&
+ !(m instanceof RuleMeasure);
+ }
+
+ public void describeTo(Description o) {
+ }
+ }
}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewViolationsDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewViolationsDecoratorTest.java
index 3587fb7f1c6..8dc10282cb3 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewViolationsDecoratorTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewViolationsDecoratorTest.java
@@ -116,17 +116,6 @@ public class NewViolationsDecoratorTest {
}
@Test
- public void shouldSumChildren() {
- Measure measure1 = new Measure(CoreMetrics.NEW_VIOLATIONS).setVariation1(1.0).setVariation2(1.0).setVariation3(3.0);
- Measure measure2 = new Measure(CoreMetrics.NEW_VIOLATIONS).setVariation1(1.0).setVariation2(2.0).setVariation3(3.0);
- List<Measure> children = Arrays.asList(measure1, measure2);
-
- assertThat(decorator.sumChildren(1, children), is(2));
- assertThat(decorator.sumChildren(2, children), is(3));
- assertThat(decorator.sumChildren(3, children), is(6));
- }
-
- @Test
public void shouldClearCacheAfterExecution() {
Violation violation1 = Violation.create(rule1, resource).setSeverity(RulePriority.CRITICAL).setCreatedAt(rightNow);
Violation violation2 = Violation.create(rule2, resource).setSeverity(RulePriority.CRITICAL).setCreatedAt(rightNow);
diff --git a/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties
index 174815553b8..0952e5a769b 100644
--- a/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties
+++ b/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties
@@ -1417,7 +1417,7 @@ metric.minor_violations.description=Minor violations
metric.info_violations.name=Info violations
metric.info_violations.description=Info violations
-metric.new_violations.name=New Violations
+metric.new_violations.name=New violations
metric.new_violations.description=New Violations
metric.new_blocker_violations.name=New Blocker violations
@@ -1561,6 +1561,9 @@ metric.team_size.description=Size of the project team
metric.violations_without_review.name=Unreviewed violations
metric.violations_without_review.description=Violations that have not been reviewed yet
+metric.new_violations_without_review.name=New unreviewed violations
+metric.new_violations_without_review.description=New violations that have not been reviewed yet
+
metric.false_positive_reviews.name=False-positive reviews
metric.false_positive_reviews.description=Active false-positive reviews