diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2011-12-04 23:24:13 +0100 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2011-12-04 23:24:33 +0100 |
commit | ee09f485818f4808fa7aff7c02969c972931a180 (patch) | |
tree | 1f2f1ddcc33e1256cd3140182fb76fc867246ed6 /plugins/sonar-core-plugin | |
parent | 524677d6bdb4b9810583c7f2fe8b20de0d070ffe (diff) | |
download | sonarqube-ee09f485818f4808fa7aff7c02969c972931a180.tar.gz sonarqube-ee09f485818f4808fa7aff7c02969c972931a180.zip |
SONAR-1974 inject manual violations into batch
Diffstat (limited to 'plugins/sonar-core-plugin')
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"); |