aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@gmail.com>2013-04-19 11:35:10 +0200
committerJulien Lancelot <julien.lancelot@gmail.com>2013-04-19 11:35:10 +0200
commit828fb94ce229e3f67f9886ac57b505a08af7db0a (patch)
tree1ae1846f09588ad8b578ca3552a06a50b4e14568 /plugins
parentfa3659c5725a26df504b4d52726970c015278a21 (diff)
downloadsonarqube-828fb94ce229e3f67f9886ac57b505a08af7db0a.tar.gz
sonarqube-828fb94ce229e3f67f9886ac57b505a08af7db0a.zip
SONAR-3755 Fix issue in Issues Tracking
Diffstat (limited to 'plugins')
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTracking.java49
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecorator.java46
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingTest.java52
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v1.txt16
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v2.txt20
5 files changed, 147 insertions, 36 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTracking.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTracking.java
index da62686b1a6..1f9e4233091 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTracking.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTracking.java
@@ -23,6 +23,9 @@ package org.sonar.plugins.core.issue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.collect.*;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.api.BatchExtension;
import org.sonar.api.batch.SonarIndex;
import org.sonar.api.resources.Project;
@@ -41,9 +44,15 @@ import java.util.*;
public class IssueTracking implements BatchExtension {
+ private static final Logger LOG = LoggerFactory.getLogger(IssueTracking.class);
+
private static final Comparator<LinePair> LINE_PAIR_COMPARATOR = new Comparator<LinePair>() {
public int compare(LinePair o1, LinePair o2) {
- return o2.weight - o1.weight;
+ if (o2.weight - o1.weight != 0) {
+ return o2.weight - o1.weight;
+ } else {
+ return Math.abs(o1.lineA -o1.lineB) - Math.abs(o2.lineA - o2.lineB);
+ }
}
};
private final Project project;
@@ -67,6 +76,8 @@ public class IssueTracking implements BatchExtension {
}
public void track(Resource resource, Collection<IssueDto> referenceIssues, Collection<DefaultIssue> newIssues) {
+ LOG.debug("Tracking : " + resource);
+
referenceIssuesMap.clear();
String source = index.getSource(resource);
@@ -117,13 +128,13 @@ public class IssueTracking implements BatchExtension {
unmappedLastIssues.addAll(lastIssues);
for (IssueDto lastIssue : lastIssues) {
- lastIssuesByRule.put(getRule(lastIssue), lastIssue);
+ lastIssuesByRule.put(getRuleId(lastIssue), lastIssue);
}
// Match the key of the issue. (For manual issues)
for (DefaultIssue newIssue : newIssues) {
mapIssue(newIssue,
- findLastIssueWithSameKey(newIssue, lastIssuesByRule.get(getRule(newIssue))),
+ findLastIssueWithSameKey(newIssue, lastIssuesByRule.get(getRuleId(newIssue))),
lastIssuesByRule, referenceIssuesMap);
}
@@ -131,7 +142,7 @@ public class IssueTracking implements BatchExtension {
for (DefaultIssue newIssue : newIssues) {
if (isNotAlreadyMapped(newIssue)) {
mapIssue(newIssue,
- findLastIssueWithSameLineAndChecksum(newIssue, lastIssuesByRule.get(getRule(newIssue))),
+ findLastIssueWithSameLineAndChecksum(newIssue, lastIssuesByRule.get(getRuleId(newIssue))),
lastIssuesByRule, referenceIssuesMap);
}
}
@@ -179,6 +190,7 @@ public class IssueTracking implements BatchExtension {
for (HashOccurrence hashOccurrence : map.values()) {
if (hashOccurrence.countA == 1 && hashOccurrence.countB == 1) {
// Guaranteed that lineA has been moved to lineB, so we can map all issues on lineA to all issues on lineB
+ LOG.debug("*** Guaranteed that lineA has been moved to lineB, so we can map all issues on lineA to all issues on lineB");
map(newIssuesByLines.get(hashOccurrence.lineB), lastIssuesByLines.get(hashOccurrence.lineA), lastIssuesByRule);
lastIssuesByLines.removeAll(hashOccurrence.lineA);
newIssuesByLines.removeAll(hashOccurrence.lineB);
@@ -197,6 +209,7 @@ public class IssueTracking implements BatchExtension {
Collections.sort(possibleLinePairs, LINE_PAIR_COMPARATOR);
for (LinePair linePair : possibleLinePairs) {
// High probability that lineA has been moved to lineB, so we can map all Issues on lineA to all Issues on lineB
+ LOG.debug("*** High probability that lineA has been moved to lineB, so we can map all Issues on lineA to all Issues on lineB");
map(newIssuesByLines.get(linePair.lineB), lastIssuesByLines.get(linePair.lineA), lastIssuesByRule);
}
}
@@ -206,8 +219,9 @@ public class IssueTracking implements BatchExtension {
// Try then to match issues on same rule with same message and with same checksum
for (DefaultIssue newIssue : newIssues) {
if (isNotAlreadyMapped(newIssue)) {
+ LOG.debug("*** Try then to match issues on same rule with same message and with same checksum");
mapIssue(newIssue,
- findLastIssueWithSameChecksumAndMessage(newIssue, lastIssuesByRule.get(getRule(newIssue))),
+ findLastIssueWithSameChecksumAndMessage(newIssue, lastIssuesByRule.get(getRuleId(newIssue))),
lastIssuesByRule, referenceIssuesMap);
}
}
@@ -215,8 +229,9 @@ public class IssueTracking implements BatchExtension {
// Try then to match issues on same rule with same line and with same message
for (DefaultIssue newIssue : newIssues) {
if (isNotAlreadyMapped(newIssue)) {
+ LOG.debug("*** Try then to match issues on same rule with same line and with same message");
mapIssue(newIssue,
- findLastIssueWithSameLineAndMessage(newIssue, lastIssuesByRule.get(getRule(newIssue))),
+ findLastIssueWithSameLineAndMessage(newIssue, lastIssuesByRule.get(getRuleId(newIssue))),
lastIssuesByRule, referenceIssuesMap);
}
}
@@ -225,8 +240,9 @@ public class IssueTracking implements BatchExtension {
// See SONAR-2812
for (DefaultIssue newIssue : newIssues) {
if (isNotAlreadyMapped(newIssue)) {
+ LOG.debug("*** Last check: match issue if same rule and same checksum but different line and different message");
mapIssue(newIssue,
- findLastIssueWithSameChecksum(newIssue, lastIssuesByRule.get(getRule(newIssue))),
+ findLastIssueWithSameChecksum(newIssue, lastIssuesByRule.get(getRuleId(newIssue))),
lastIssuesByRule, referenceIssuesMap);
}
}
@@ -236,9 +252,12 @@ public class IssueTracking implements BatchExtension {
for (DefaultIssue newIssue : newIssues) {
if (isNotAlreadyMapped(newIssue)) {
for (IssueDto pastIssue : lastIssues) {
- if (isNotAlreadyMapped(pastIssue) && Objects.equal(getRule(newIssue), getRule(pastIssue))) {
+ if (isNotAlreadyMapped(pastIssue) && Objects.equal(getRuleId(newIssue), getRuleId(pastIssue))) {
+ LOG.debug("mapIssue newIssue : "+ newIssue + " with : "+ pastIssue);
mapIssue(newIssue, pastIssue, lastIssuesByRule, referenceIssuesMap);
break;
+ } else {
+ LOG.debug("Not mapIssue newIssue : "+ newIssue + " with : "+ pastIssue);
}
}
}
@@ -319,7 +338,7 @@ public class IssueTracking implements BatchExtension {
}
private boolean isSameChecksum(DefaultIssue newIssue, IssueDto pastIssue) {
- return Objects.equal(pastIssue.getChecksum(), newIssue.getChecksum());
+ return StringUtils.equals(pastIssue.getChecksum(), newIssue.getChecksum());
}
private boolean isSameLine(DefaultIssue newIssue, IssueDto pastIssue) {
@@ -327,7 +346,7 @@ public class IssueTracking implements BatchExtension {
}
private boolean isSameMessage(DefaultIssue newIssue, IssueDto pastIssue) {
- return Objects.equal(IssueDto.abbreviateMessage(newIssue.message()), pastIssue.getMessage());
+ return StringUtils.equals(IssueDto.abbreviateMessage(newIssue.message()), pastIssue.getMessage());
}
private boolean isSameKey(DefaultIssue newIssue, IssueDto pastIssue) {
@@ -336,6 +355,8 @@ public class IssueTracking implements BatchExtension {
private void mapIssue(DefaultIssue newIssue, IssueDto pastIssue, Multimap<Integer, IssueDto> lastIssuesByRule, Map<DefaultIssue, IssueDto> issueMap) {
if (pastIssue != null) {
+ LOG.debug("Mapping with old issue from newIssue : "+ newIssue + " and pastIssue : "+ pastIssue);
+
newIssue.setKey(pastIssue.getUuid());
if (pastIssue.isManualSeverity()) {
newIssue.setSeverity(pastIssue.getSeverity());
@@ -348,10 +369,12 @@ public class IssueTracking implements BatchExtension {
// TODO
// newIssue.setPersonId(pastIssue.getPersonId());
- lastIssuesByRule.remove(getRule(newIssue), pastIssue);
+ lastIssuesByRule.remove(getRuleId(newIssue), pastIssue);
issueMap.put(newIssue, pastIssue);
unmappedLastIssues.remove(pastIssue);
} else {
+ LOG.debug("No old issue, creating new one with newIssue : "+ newIssue + " and pastIssue : "+ pastIssue);
+
newIssue.setNew(true);
newIssue.setCreatedAt(project.getAnalysisDate());
}
@@ -362,11 +385,11 @@ public class IssueTracking implements BatchExtension {
return referenceIssuesMap.get(issue);
}
- private Integer getRule(DefaultIssue issue) {
+ private Integer getRuleId(DefaultIssue issue) {
return ruleFinder.findByKey(issue.ruleKey()).getId();
}
- private Integer getRule(IssueDto issue) {
+ private Integer getRuleId(IssueDto issue) {
return issue.getRuleId();
}
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 430e707e803..7971be2434e 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
@@ -21,44 +21,30 @@ package org.sonar.plugins.core.timemachine;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
+import com.google.common.collect.*;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
-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.SonarIndex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.*;
import org.sonar.api.database.model.RuleFailureModel;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.rules.Violation;
import org.sonar.api.violations.ViolationQuery;
import org.sonar.batch.scan.LastSnapshots;
-import org.sonar.plugins.core.timemachine.tracking.HashedSequence;
-import org.sonar.plugins.core.timemachine.tracking.HashedSequenceComparator;
-import org.sonar.plugins.core.timemachine.tracking.RollingHashSequence;
-import org.sonar.plugins.core.timemachine.tracking.RollingHashSequenceComparator;
-import org.sonar.plugins.core.timemachine.tracking.StringText;
-import org.sonar.plugins.core.timemachine.tracking.StringTextComparator;
+import org.sonar.plugins.core.timemachine.tracking.*;
import javax.annotation.Nullable;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
@DependsUpon({DecoratorBarriers.END_OF_VIOLATIONS_GENERATION, DecoratorBarriers.START_VIOLATION_TRACKING})
@DependedUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING)
public class ViolationTrackingDecorator implements Decorator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ViolationTrackingDecorator.class);
+
private LastSnapshots lastSnapshots;
private Map<Violation, RuleFailureModel> referenceViolationsMap = Maps.newIdentityHashMap();
private SonarIndex index;
@@ -80,6 +66,8 @@ public class ViolationTrackingDecorator implements Decorator {
}
public void decorate(Resource resource, DecoratorContext context) {
+ LOG.debug("ViolationTracking : " + resource);
+
referenceViolationsMap.clear();
ViolationQuery violationQuery = ViolationQuery.create().forResource(resource).setSwitchMode(ViolationQuery.SwitchMode.BOTH);
@@ -195,6 +183,7 @@ public class ViolationTrackingDecorator implements Decorator {
for (HashOccurrence hashOccurrence : map.values()) {
if (hashOccurrence.countA == 1 && hashOccurrence.countB == 1) {
// Guaranteed that lineA has been moved to lineB, so we can map all violations on lineA to all violations on lineB
+ LOG.debug("*** Guaranteed that lineA has been moved to lineB, so we can map all issues on lineA to all issues on lineB");
map(newViolationsByLines.get(hashOccurrence.lineB), lastViolationsByLines.get(hashOccurrence.lineA), lastViolationsByRule);
lastViolationsByLines.removeAll(hashOccurrence.lineA);
newViolationsByLines.removeAll(hashOccurrence.lineB);
@@ -213,6 +202,7 @@ public class ViolationTrackingDecorator implements Decorator {
Collections.sort(possibleLinePairs, LINE_PAIR_COMPARATOR);
for (LinePair linePair : possibleLinePairs) {
// High probability that lineA has been moved to lineB, so we can map all violations on lineA to all violations on lineB
+ LOG.debug("*** High probability that lineA has been moved to lineB, so we can map all Issues on lineA to all Issues on lineB");
map(newViolationsByLines.get(linePair.lineB), lastViolationsByLines.get(linePair.lineA), lastViolationsByRule);
}
}
@@ -222,6 +212,7 @@ public class ViolationTrackingDecorator implements Decorator {
// Try then to match violations on same rule with same message and with same checksum
for (Violation newViolation : newViolations) {
if (isNotAlreadyMapped(newViolation)) {
+ LOG.debug("*** Try then to match issues on same rule with same message and with same checksum");
mapViolation(newViolation,
findLastViolationWithSameChecksumAndMessage(newViolation, lastViolationsByRule.get(newViolation.getRule().getId())),
lastViolationsByRule, referenceViolationsMap);
@@ -231,6 +222,7 @@ public class ViolationTrackingDecorator implements Decorator {
// Try then to match violations on same rule with same line and with same message
for (Violation newViolation : newViolations) {
if (isNotAlreadyMapped(newViolation)) {
+ LOG.debug("*** Try then to match issues on same rule with same line and with same message");
mapViolation(newViolation,
findLastViolationWithSameLineAndMessage(newViolation, lastViolationsByRule.get(newViolation.getRule().getId())),
lastViolationsByRule, referenceViolationsMap);
@@ -241,6 +233,7 @@ public class ViolationTrackingDecorator implements Decorator {
// See SONAR-2812
for (Violation newViolation : newViolations) {
if (isNotAlreadyMapped(newViolation)) {
+ LOG.debug("*** Last check: match issue if same rule and same checksum but different line and different message");
mapViolation(newViolation,
findLastViolationWithSameChecksum(newViolation, lastViolationsByRule.get(newViolation.getRule().getId())),
lastViolationsByRule, referenceViolationsMap);
@@ -257,8 +250,11 @@ public class ViolationTrackingDecorator implements Decorator {
if (isNotAlreadyMapped(newViolation)) {
for (RuleFailureModel pastViolation : lastViolations) {
if (isNotAlreadyMapped(pastViolation) && Objects.equal(newViolation.getRule().getId(), pastViolation.getRuleId())) {
+ LOG.debug("mapIssue newViolation : " + newViolation + " with pastViolation : " + pastViolation);
mapViolation(newViolation, pastViolation, lastViolationsByRule, referenceViolationsMap);
break;
+ } else {
+ LOG.debug("Not mapIssue newViolation : " + newViolation + " with pastViolation : " + pastViolation);
}
}
}
@@ -384,6 +380,8 @@ public class ViolationTrackingDecorator implements Decorator {
private void mapViolation(Violation newViolation, RuleFailureModel pastViolation,
Multimap<Integer, RuleFailureModel> lastViolationsByRule, Map<Violation, RuleFailureModel> violationMap) {
if (pastViolation != null) {
+ LOG.debug("Mapping with old violation from newViolation : " + newViolation + " and pastViolation : " + pastViolation);
+
newViolation.setCreatedAt(pastViolation.getCreatedAt());
newViolation.setPermanentId(pastViolation.getPermanentId());
newViolation.setSwitchedOff(pastViolation.isSwitchedOff());
@@ -393,6 +391,8 @@ public class ViolationTrackingDecorator implements Decorator {
violationMap.put(newViolation, pastViolation);
unmappedLastViolations.remove(pastViolation);
} else {
+ LOG.debug("No old violation, creating new one with newViolation : " + newViolation + " and pastViolation : " + pastViolation);
+
newViolation.setNew(true);
newViolation.setCreatedAt(project.getAnalysisDate());
}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingTest.java
index eab2ff4a71c..550fbb95d60 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingTest.java
@@ -62,12 +62,16 @@ public class IssueTrackingTest {
rule1.setId(1);
Rule rule2 = Rule.create("squid", "NullDeref");
rule2.setId(2);
+ Rule rule3 = Rule.create("pmd", "UnusedLocalVariable");
+ rule3.setId(3);
ruleFinder = mock(RuleFinder.class);
when(ruleFinder.findById(1)).thenReturn(rule1);
when(ruleFinder.findById(2)).thenReturn(rule2);
+ when(ruleFinder.findById(3)).thenReturn(rule3);
when(ruleFinder.findByKey(RuleKey.of("squid", "AvoidCycle"))).thenReturn(rule1);
when(ruleFinder.findByKey(RuleKey.of("squid", "NullDeref"))).thenReturn(rule2);
+ when(ruleFinder.findByKey(RuleKey.of("pmd", "UnusedLocalVariable"))).thenReturn(rule3);
lastSnapshots = mock(LastSnapshots.class);
@@ -346,6 +350,41 @@ public class IssueTrackingTest {
assertThat(newIssue3.isNew()).isTrue();
}
+ @Test
+ public void should_track_issues_based_on_blocks_recognition_on_example3() throws Exception {
+ when(lastSnapshots.getSource(project)).thenReturn(load("example3-v1"));
+ String source = load("example3-v2");
+
+ IssueDto referenceIssue1 = newReferenceIssue("Avoid unused local variables such as 'j'.", 6, 1, "63c11570fc0a76434156be5f8138fa03");
+ IssueDto referenceIssue2 = newReferenceIssue("Avoid unused private methods such as 'myMethod()'.", 13, 2, "ef23288705d1ef1e512448ace287586e");
+ IssueDto referenceIssue3 = newReferenceIssue("Method 'avoidUtilityClass' is not designed for extension - needs to be abstract, final or empty.", 9, 3, "ed5cdd046fda82727d6fedd1d8e3a310");
+
+ // New issue
+ DefaultIssue newIssue1 = newDefaultIssue("Avoid unused local variables such as 'msg'.", 18, RuleKey.of("squid", "AvoidCycle"), "a24254126be2bf1a9b9a8db43f633733");
+ // Same as referenceIssue2
+ DefaultIssue newIssue2 = newDefaultIssue("Avoid unused private methods such as 'myMethod()'.", 13, RuleKey.of("squid", "NullDeref"), "ef23288705d1ef1e512448ace287586e");
+ // Same as referenceIssue3
+ DefaultIssue newIssue3 = newDefaultIssue("Method 'avoidUtilityClass' is not designed for extension - needs to be abstract, final or empty.", 9, RuleKey.of("pmd", "UnusedLocalVariable"), "ed5cdd046fda82727d6fedd1d8e3a310");
+ // New issue
+ DefaultIssue newIssue4 = newDefaultIssue("Method 'newViolation' is not designed for extension - needs to be abstract, final or empty.", 17, RuleKey.of("pmd", "UnusedLocalVariable"), "7d58ac9040c27e4ca2f11a0269e251e2");
+ // Same as referenceIssue1
+ DefaultIssue newIssue5 = newDefaultIssue("Avoid unused local variables such as 'j'.", 6, RuleKey.of("squid", "AvoidCycle"), "4432a2675ec3e1620daefe38386b51ef");
+
+ Map<DefaultIssue, IssueDto> mapping = decorator.mapIssues(
+ Arrays.asList(newIssue1, newIssue2, newIssue3, newIssue4, newIssue5),
+ Arrays.asList(referenceIssue1, referenceIssue2, referenceIssue3),
+ source, project);
+
+ assertThat(newIssue1.isNew()).isTrue();
+ assertThat(newIssue2.isNew()).isFalse();
+ assertThat(newIssue3.isNew()).isFalse();
+ assertThat(newIssue4.isNew()).isTrue();
+ assertThat(newIssue5.isNew()).isFalse();
+ assertThat(mapping.get(newIssue2)).isEqualTo(referenceIssue2);
+ assertThat(mapping.get(newIssue3)).isEqualTo(referenceIssue3);
+ assertThat(mapping.get(newIssue5)).isEqualTo(referenceIssue1);
+ }
+
private static String load(String name) throws IOException {
return Resources.toString(IssueTrackingTest.class.getResource("IssueTrackingTest/" + name + ".txt"), Charsets.UTF_8);
}
@@ -366,4 +405,17 @@ public class IssueTrackingTest {
return referenceIssue;
}
+ public void test(){
+ IssueDto referenceIssue1 = newReferenceIssue("Avoid unused local variables such as 'j'.", 6, 1, "63c11570fc0a76434156be5f8138fa03");
+ IssueDto referenceIssue2 = newReferenceIssue("Avoid unused private methods such as 'myMethod()'.", 13, 2, "ef23288705d1ef1e512448ace287586e");
+ IssueDto referenceIssue3 = newReferenceIssue("Method 'avoidUtilityClass' is not designed for extension - needs to be abstract, final or empty.", 9, 3, "ed5cdd046fda82727d6fedd1d8e3a310");
+
+ DefaultIssue newIssue1 = newDefaultIssue("Avoid unused local variables such as 'msg'.", 18, RuleKey.of("squid", "AvoidCycle"), "a24254126be2bf1a9b9a8db43f633733");
+ DefaultIssue newIssue2 = newDefaultIssue("Avoid unused private methods such as 'myMethod()'.", 13, RuleKey.of("squid", "NullDeref"), "ef23288705d1ef1e512448ace287586e");
+ DefaultIssue newIssue3 = newDefaultIssue("Method 'avoidUtilityClass' is not designed for extension - needs to be abstract, final or empty.", 9, RuleKey.of("pmd", "UnusedLocalVariable"), "ed5cdd046fda82727d6fedd1d8e3a310");
+ DefaultIssue newIssue4 = newDefaultIssue("Method 'newViolation' is not designed for extension - needs to be abstract, final or empty.", 17, RuleKey.of("pmd", "UnusedLocalVariable"), "7d58ac9040c27e4ca2f11a0269e251e2");
+ DefaultIssue newIssue5 = newDefaultIssue("Avoid unused local variables such as 'j'.", 6, RuleKey.of("squid", "AvoidCycle"), "4432a2675ec3e1620daefe38386b51ef");
+
+ }
+
}
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v1.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v1.txt
new file mode 100644
index 00000000000..facdcbc008c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v1.txt
@@ -0,0 +1,16 @@
+package sample;
+
+public class Sample {
+
+ public Sample(int i) {
+ int j = i+1; // violation: unused local variable
+ }
+
+ public boolean avoidUtilityClass() {
+ return true;
+ }
+
+ private String myMethod() { // violation : unused private method
+ return "hello";
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v2.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v2.txt
new file mode 100644
index 00000000000..91db843fc4d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/IssueTrackingTest/example3-v2.txt
@@ -0,0 +1,20 @@
+package sample;
+
+public class Sample {
+
+ public Sample(int i) {
+ int j = i+1; // still the same violation: unused local variable
+ }
+
+ public boolean avoidUtilityClass() {
+ return true;
+ }
+
+ private String myMethod() { // violation "unused private method" is fixed because it's called in newViolation
+ return "hello";
+ }
+
+ public void newViolation() {
+ String msg = myMethod(); // new violation : msg is an unused variable
+ }
+}