summaryrefslogtreecommitdiffstats
path: root/plugins/sonar-core-plugin
diff options
context:
space:
mode:
authorsimonbrandhof <simon.brandhof@gmail.com>2011-12-04 23:24:13 +0100
committersimonbrandhof <simon.brandhof@gmail.com>2011-12-04 23:24:33 +0100
commitee09f485818f4808fa7aff7c02969c972931a180 (patch)
tree1f2f1ddcc33e1256cd3140182fb76fc867246ed6 /plugins/sonar-core-plugin
parent524677d6bdb4b9810583c7f2fe8b20de0d070ffe (diff)
downloadsonarqube-ee09f485818f4808fa7aff7c02969c972931a180.tar.gz
sonarqube-ee09f485818f4808fa7aff7c02969c972931a180.zip
SONAR-1974 inject manual violations into batch
Diffstat (limited to 'plugins/sonar-core-plugin')
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java1
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CloseReviewsDecorator.java20
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualViolationInjector.java81
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecorator.java29
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ManualViolationInjectorTest.java68
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecoratorTest.java16
6 files changed, 201 insertions, 14 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 4299ce8c699..e51aeefa2fc 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
@@ -283,6 +283,7 @@ public class CorePlugin extends SonarPlugin {
extensions.add(CloseReviewsDecorator.class);
extensions.add(ReferenceAnalysis.class);
extensions.add(ManualMeasureDecorator.class);
+ extensions.add(ManualViolationInjector.class);
extensions.add(UpdateReviewsDecorator.class);
// time machine
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CloseReviewsDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CloseReviewsDecorator.java
index 86e849d2c0a..c7c12be103e 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CloseReviewsDecorator.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CloseReviewsDecorator.java
@@ -19,15 +19,9 @@
*/
package org.sonar.plugins.core.sensors;
-import java.util.List;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-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.batch.*;
import org.sonar.api.database.DatabaseSession;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.database.model.User;
@@ -41,6 +35,8 @@ import org.sonar.batch.index.ResourcePersister;
import org.sonar.core.NotDryRun;
import org.sonar.jpa.entity.Review;
+import java.util.List;
+
/**
* Decorator that handles the life cycle of a review (for instance, closes a review when its corresponding violation has been fixed).
*/
@@ -92,13 +88,17 @@ public class CloseReviewsDecorator implements Decorator {
*/
protected int closeReviews(Resource resource, int resourceId, int snapshotId) {
String conditions = " WHERE status!='CLOSED' AND resource_id=" + resourceId
- + " AND rule_failure_permanent_id NOT IN " + "(SELECT permanent_id FROM rule_failures WHERE snapshot_id=" + snapshotId
+ + " AND manual_violation=:manualViolation AND rule_failure_permanent_id NOT IN " + "(SELECT permanent_id FROM rule_failures WHERE snapshot_id=" + snapshotId
+ " AND permanent_id IS NOT NULL)";
- List<Review> reviews = databaseSession.getEntityManager().createNativeQuery("SELECT * FROM reviews " + conditions, Review.class).getResultList();
+ List<Review> reviews = databaseSession.getEntityManager().createNativeQuery("SELECT * FROM reviews " + conditions, Review.class)
+ .setParameter("manualViolation", false)
+ .getResultList();
for (Review review : reviews) {
notifyClosed(resource, review);
}
- int rowUpdated = databaseSession.createNativeQuery("UPDATE reviews SET status='CLOSED', updated_at=CURRENT_TIMESTAMP" + conditions).executeUpdate();
+ int rowUpdated = databaseSession.createNativeQuery("UPDATE reviews SET status='CLOSED', updated_at=CURRENT_TIMESTAMP" + conditions)
+ .setParameter("manualViolation", false)
+ .executeUpdate();
LOG.debug("- {} reviews set to 'closed' on resource #{}", rowUpdated, resourceId);
return rowUpdated;
}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualViolationInjector.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualViolationInjector.java
new file mode 100644
index 00000000000..4e9901e3a1d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualViolationInjector.java
@@ -0,0 +1,81 @@
+/*
+ * 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.sensors;
+
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.Phase;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.rules.Violation;
+import org.sonar.persistence.dao.ReviewDao;
+import org.sonar.persistence.model.Review;
+import org.sonar.persistence.model.ReviewQuery;
+
+
+import java.util.List;
+
+@Phase(name = Phase.Name.PRE)
+public class ManualViolationInjector implements Decorator {
+
+ private ReviewDao reviewDao;
+ private RuleFinder ruleFinder;
+
+ public ManualViolationInjector(ReviewDao reviewDao, RuleFinder ruleFinder) {
+ this.reviewDao = reviewDao;
+ this.ruleFinder = ruleFinder;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (resource.getId() != null) {
+ ReviewQuery query = ReviewQuery.create().setManualViolation(true).setResourceId(resource.getId()).setStatus(Review.STATUS_OPEN);
+ List<Review> reviews = reviewDao.selectByQuery(query);
+ for (Review review : reviews) {
+ if (review.getRuleId()==null) {
+ LoggerFactory.getLogger(getClass()).warn("No rule is defined on the review with id: " + review.getId());
+ }
+ if (review.getViolationPermanentId()==null) {
+ LoggerFactory.getLogger(getClass()).warn("Permanent id of manual violation is missing on the review with id: " + review.getId());
+ }
+ Violation violation = Violation.create(ruleFinder.findById(review.getRuleId()), resource);
+ violation.setManual(true);
+ violation.setLineId(review.getLine());
+ violation.setPermanentId(review.getViolationPermanentId());
+ violation.setSwitchedOff(false);
+ violation.setCreatedAt(review.getCreatedAt());
+ violation.setMessage(review.getTitle());
+ violation.setSeverity(RulePriority.valueOf(review.getSeverity()));
+ context.saveViolation(violation);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecorator.java
index 93347cd2f17..62d259df24c 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecorator.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecorator.java
@@ -90,13 +90,23 @@ public class ViolationTrackingDecorator implements Decorator {
pastViolationsByRule.put(pastViolation.getRuleId(), pastViolation);
}
- // Try first to match violations on same rule with same line and with same checkum (but not necessarily with same message)
+ // Match the permanent id of the violation. This id is for example set explicitly when injecting manual violations
for (Violation newViolation : newViolations) {
mapViolation(newViolation,
- findPastViolationWithSameLineAndChecksum(newViolation, pastViolationsByRule.get(newViolation.getRule().getId())),
+ findPastViolationWithSamePermanentId(newViolation, pastViolationsByRule.get(newViolation.getRule().getId())),
pastViolationsByRule, referenceViolationsMap);
}
+
+ // Try first to match violations on same rule with same line and with same checkum (but not necessarily with same message)
+ for (Violation newViolation : newViolations) {
+ if (isNotAlreadyMapped(newViolation, referenceViolationsMap)) {
+ mapViolation(newViolation,
+ findPastViolationWithSameLineAndChecksum(newViolation, pastViolationsByRule.get(newViolation.getRule().getId())),
+ pastViolationsByRule, referenceViolationsMap);
+ }
+ }
+
// If each new violation matches an old one we can stop the matching mechanism
if (referenceViolationsMap.size() != newViolations.size()) {
// Try then to match violations on same rule with same message and with same checkum
@@ -170,6 +180,15 @@ public class ViolationTrackingDecorator implements Decorator {
return null;
}
+ private RuleFailureModel findPastViolationWithSamePermanentId(Violation newViolation, Collection<RuleFailureModel> pastViolations) {
+ for (RuleFailureModel pastViolation : pastViolations) {
+ if (isSamePermanentId(newViolation, pastViolation)) {
+ return pastViolation;
+ }
+ }
+ return null;
+ }
+
private boolean isSameChecksum(Violation newViolation, RuleFailureModel pastViolation) {
return StringUtils.equals(pastViolation.getChecksum(), newViolation.getChecksum());
}
@@ -182,6 +201,10 @@ public class ViolationTrackingDecorator implements Decorator {
return StringUtils.equals(RuleFailureModel.abbreviateMessage(newViolation.getMessage()), pastViolation.getMessage());
}
+ private boolean isSamePermanentId(Violation newViolation, RuleFailureModel pastViolation) {
+ return newViolation.getPermanentId() != null && newViolation.getPermanentId().equals(pastViolation.getPermanentId());
+ }
+
private void mapViolation(Violation newViolation, RuleFailureModel pastViolation,
Multimap<Integer, RuleFailureModel> pastViolationsByRule, Map<Violation, RuleFailureModel> violationMap) {
if (pastViolation != null) {
@@ -190,7 +213,7 @@ public class ViolationTrackingDecorator implements Decorator {
newViolation.setNew(false);
pastViolationsByRule.remove(newViolation.getRule().getId(), pastViolation);
violationMap.put(newViolation, pastViolation);
-
+
} else {
newViolation.setNew(true);
newViolation.setCreatedAt(project.getAnalysisDate());
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ManualViolationInjectorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ManualViolationInjectorTest.java
new file mode 100644
index 00000000000..bb7c3e8b483
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ManualViolationInjectorTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.sensors;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.resources.Project;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.rules.Violation;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.persistence.dao.ReviewDao;
+import org.sonar.persistence.model.Review;
+import org.sonar.persistence.model.ReviewQuery;
+
+import java.util.Arrays;
+import java.util.Date;
+
+import static org.mockito.Mockito.*;
+
+public class ManualViolationInjectorTest {
+
+ @Test
+ public void shouldInjectManualViolationsDefinedByReviews() {
+ ReviewDao reviewDao = mock(ReviewDao.class);
+ final Date reviewCreatedAt = DateUtils.parseDate("2011-12-25");
+ Review review = new Review().setRuleId(3).setViolationPermanentId(100).setCreatedAt(reviewCreatedAt).setSeverity("BLOCKER");
+ when(reviewDao.selectByQuery(Matchers.<ReviewQuery>anyObject())).thenReturn(Arrays.<Review>asList(review));
+ RuleFinder ruleFinder = mock(RuleFinder.class);
+ when(ruleFinder.findById(3)).thenReturn(new Rule());
+ DecoratorContext context = mock(DecoratorContext.class);
+ ManualViolationInjector injector = new ManualViolationInjector(reviewDao, ruleFinder);
+
+ injector.decorate(new Project("key").setId(100), context);
+
+ verify(context, times(1)).saveViolation(argThat(new BaseMatcher<Violation>() {
+ public boolean matches(Object o) {
+ Violation v = (Violation) o;
+ return v.getPermanentId() == 100 && v.getRule() != null && v.isManual() && v.getCreatedAt().equals(reviewCreatedAt)
+ && v.getSeverity().equals(RulePriority.BLOCKER);
+ }
+
+ public void describeTo(Description description) {
+ }
+ }));
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecoratorTest.java
index 5aee8dc0586..4f5671d75be 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecoratorTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecoratorTest.java
@@ -49,7 +49,21 @@ public class ViolationTrackingDecoratorTest {
when(project.getAnalysisDate()).thenReturn(analysisDate);
decorator = new ViolationTrackingDecorator(project, null, null);
}
-
+
+ @Test
+ public void permanentIdShouldBeThePrioritaryFieldToCheck() {
+ RuleFailureModel referenceViolation1 = newReferenceViolation("message", 10, 1, "checksum1").setPermanentId(100);
+ RuleFailureModel referenceViolation2 = newReferenceViolation("message", 18, 1, "checksum2").setPermanentId(200);
+ Violation newViolation = newViolation("message", 10, 1, "checksum1"); // exactly the fields of referenceViolation1
+ newViolation.setPermanentId(200);
+
+ Map<Violation, RuleFailureModel> mapping = decorator.mapViolations(Lists.newArrayList(newViolation),
+ Lists.newArrayList(referenceViolation1, referenceViolation2));
+
+ assertThat(mapping.get(newViolation), equalTo(referenceViolation2));// same permanent id
+ assertThat(newViolation.isNew(), is(false));
+ }
+
@Test
public void checksumShouldHaveGreaterPriorityThanLine() {
RuleFailureModel referenceViolation1 = newReferenceViolation("message", 1, 50, "checksum1");