package org.sonar.plugins.core.issue;
+import org.sonar.plugins.core.issue.tracking.SourceChecksum;
+
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import org.sonar.api.BatchExtension;
-import org.sonar.api.batch.SonarIndex;
import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.resources.Resource;
import org.sonar.api.rule.RuleKey;
-import org.sonar.batch.scan.LastSnapshots;
import org.sonar.core.issue.db.IssueDto;
-import org.sonar.plugins.core.issue.tracking.*;
+import org.sonar.plugins.core.issue.tracking.HashedSequence;
+import org.sonar.plugins.core.issue.tracking.HashedSequenceComparator;
+import org.sonar.plugins.core.issue.tracking.IssueTrackingBlocksRecognizer;
+import org.sonar.plugins.core.issue.tracking.RollingHashSequence;
+import org.sonar.plugins.core.issue.tracking.RollingHashSequenceComparator;
+import org.sonar.plugins.core.issue.tracking.StringText;
+import org.sonar.plugins.core.issue.tracking.StringTextComparator;
import javax.annotation.Nullable;
-import java.util.*;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
public class IssueTracking implements BatchExtension {
- private final LastSnapshots lastSnapshots;
- private final SonarIndex index;
-
- public IssueTracking(LastSnapshots lastSnapshots, SonarIndex index) {
- this.lastSnapshots = lastSnapshots;
- this.index = index;
- }
-
- public IssueTrackingResult track(Resource resource, Collection<IssueDto> dbIssues, Collection<DefaultIssue> newIssues) {
+ public IssueTrackingResult track(SourceHashHolder sourceHashHolder, Collection<IssueDto> dbIssues, Collection<DefaultIssue> newIssues) {
IssueTrackingResult result = new IssueTrackingResult();
- String source = index.getSource(resource);
- setChecksumOnNewIssues(newIssues, source);
+ setChecksumOnNewIssues(newIssues, sourceHashHolder);
// Map new issues with old ones
- mapIssues(newIssues, dbIssues, source, resource, result);
+ mapIssues(newIssues, dbIssues, sourceHashHolder, result);
return result;
}
- private void setChecksumOnNewIssues(Collection<DefaultIssue> issues, String source) {
- List<String> checksums = SourceChecksum.lineChecksumsOfFile(source);
+ private void setChecksumOnNewIssues(Collection<DefaultIssue> issues, SourceHashHolder sourceHashHolder) {
+ List<String> checksums = SourceChecksum.lineChecksumsOfFile(sourceHashHolder.getSource());
for (DefaultIssue issue : issues) {
issue.setChecksum(SourceChecksum.getChecksumForLine(checksums, issue.line()));
}
}
+
@VisibleForTesting
- void mapIssues(Collection<DefaultIssue> newIssues, @Nullable Collection<IssueDto> lastIssues, @Nullable String source, @Nullable Resource resource, IssueTrackingResult result) {
+ void mapIssues(Collection<DefaultIssue> newIssues, @Nullable Collection<IssueDto> lastIssues, SourceHashHolder sourceHashHolder, IssueTrackingResult result) {
boolean hasLastScan = false;
if (lastIssues != null) {
// If each new issue matches an old one we can stop the matching mechanism
if (result.matched().size() != newIssues.size()) {
- if (source != null && resource != null && hasLastScan) {
- String referenceSource = lastSnapshots.getSource(resource);
- if (referenceSource != null) {
- mapNewissues(referenceSource, newIssues, source, result);
- }
+ if (sourceHashHolder.hasBothReferenceAndCurrentSource() && hasLastScan) {
+ mapNewissues(sourceHashHolder, newIssues, result);
}
mapIssuesOnSameRule(newIssues, result);
}
}
}
- private void mapNewissues(String referenceSource, Collection<DefaultIssue> newIssues, String source, IssueTrackingResult result) {
- HashedSequence<StringText> hashedReference = HashedSequence.wrap(new StringText(referenceSource), StringTextComparator.IGNORE_WHITESPACE);
- HashedSequence<StringText> hashedSource = HashedSequence.wrap(new StringText(source), StringTextComparator.IGNORE_WHITESPACE);
+ private void mapNewissues(SourceHashHolder sourceHashHolder, Collection<DefaultIssue> newIssues, IssueTrackingResult result) {
+
HashedSequenceComparator<StringText> hashedComparator = new HashedSequenceComparator<StringText>(StringTextComparator.IGNORE_WHITESPACE);
+ IssueTrackingBlocksRecognizer rec = new IssueTrackingBlocksRecognizer(sourceHashHolder.getHashedReference(), sourceHashHolder.getHashedSource(), hashedComparator);
- IssueTrackingBlocksRecognizer rec = new IssueTrackingBlocksRecognizer(hashedReference, hashedSource, hashedComparator);
+ RollingHashSequence<HashedSequence<StringText>> a = RollingHashSequence.wrap(sourceHashHolder.getHashedReference(), hashedComparator, 5);
+ RollingHashSequence<HashedSequence<StringText>> b = RollingHashSequence.wrap(sourceHashHolder.getHashedSource(), hashedComparator, 5);
+ RollingHashSequenceComparator<HashedSequence<StringText>> cmp = new RollingHashSequenceComparator<HashedSequence<StringText>>(hashedComparator);
Multimap<Integer, DefaultIssue> newIssuesByLines = newIssuesByLines(newIssues, rec, result);
Multimap<Integer, IssueDto> lastIssuesByLines = lastIssuesByLines(result.unmatched(), rec);
- RollingHashSequence<HashedSequence<StringText>> a = RollingHashSequence.wrap(hashedReference, hashedComparator, 5);
- RollingHashSequence<HashedSequence<StringText>> b = RollingHashSequence.wrap(hashedSource, hashedComparator, 5);
- RollingHashSequenceComparator<HashedSequence<StringText>> cmp = new RollingHashSequenceComparator<HashedSequence<StringText>>(hashedComparator);
-
Map<Integer, HashOccurrence> map = Maps.newHashMap();
for (Integer line : lastIssuesByLines.keySet()) {
package org.sonar.plugins.core.issue;
+import org.sonar.api.batch.SonarIndex;
+
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import org.junit.Before;
IssueTracking tracking;
Resource project;
+ SourceHashHolder sourceHashHolder;
+ SonarIndex index;
LastSnapshots lastSnapshots;
long violationId = 0;
@Before
public void before() {
+ index = mock(SonarIndex.class);
lastSnapshots = mock(LastSnapshots.class);
project = mock(Project.class);
- tracking = new IssueTracking(lastSnapshots, null);
+ tracking = new IssueTracking();
}
@Test
DefaultIssue newIssue = newDefaultIssue("message", 10, RuleKey.of("squid", "AvoidCycle"), "checksum1").setKey("200");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue1, referenceIssue2), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue1, referenceIssue2), null, result);
// same key
assertThat(result.matching(newIssue)).isSameAs(referenceIssue2);
}
@Test
public void checksum_should_have_greater_priority_than_line() {
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+
IssueDto referenceIssue1 = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum1");
IssueDto referenceIssue2 = newReferenceIssue("message", 3, "squid", "AvoidCycle", "checksum2");
DefaultIssue newIssue2 = newDefaultIssue("message", 5, RuleKey.of("squid", "AvoidCycle"), "checksum2");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue1, newIssue2), newArrayList(referenceIssue1, referenceIssue2), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue1, newIssue2), newArrayList(referenceIssue1, referenceIssue2), sourceHashHolder, result);
assertThat(result.matching(newIssue1)).isSameAs(referenceIssue1);
assertThat(result.matching(newIssue2)).isSameAs(referenceIssue2);
}
*/
@Test
public void same_rule_and_null_line_and_checksum_but_different_messages() {
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+
DefaultIssue newIssue = newDefaultIssue("new message", null, RuleKey.of("squid", "AvoidCycle"), "checksum1");
IssueDto referenceIssue = newReferenceIssue("old message", null, "squid", "AvoidCycle", "checksum1");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matching(newIssue)).isSameAs(referenceIssue);
}
@Test
public void same_rule_and_line_and_checksum_but_different_messages() {
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+
DefaultIssue newIssue = newDefaultIssue("new message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1");
IssueDto referenceIssue = newReferenceIssue("old message", 1, "squid", "AvoidCycle", "checksum1");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matching(newIssue)).isSameAs(referenceIssue);
}
@Test
public void same_rule_and_line_message() {
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+
DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1");
IssueDto referenceIssue = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum2");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matching(newIssue)).isSameAs(referenceIssue);
}
@Test
public void should_ignore_reference_measure_without_checksum() {
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+
DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), null);
IssueDto referenceIssue = newReferenceIssue("message", 1, "squid", "NullDeref", null);
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matching(newIssue)).isNull();
}
@Test
public void same_rule_and_message_and_checksum_but_different_line() {
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+
DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1");
IssueDto referenceIssue = newReferenceIssue("message", 2, "squid", "AvoidCycle", "checksum1");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matching(newIssue)).isSameAs(referenceIssue);
}
*/
@Test
public void same_checksum_and_rule_but_different_line_and_different_message() {
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+
DefaultIssue newIssue = newDefaultIssue("new message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1");
IssueDto referenceIssue = newReferenceIssue("old message", 2, "squid", "AvoidCycle", "checksum1");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matching(newIssue)).isSameAs(referenceIssue);
}
@Test
public void should_create_new_issue_when_same_rule_same_message_but_different_line_and_checksum() {
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+
DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1");
IssueDto referenceIssue = newReferenceIssue("message", 2, "squid", "AvoidCycle", "checksum2");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matching(newIssue)).isNull();
}
@Test
public void should_not_track_issue_if_different_rule() {
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+
DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1");
IssueDto referenceIssue = newReferenceIssue("message", 1, "squid", "NullDeref", "checksum1");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matching(newIssue)).isNull();
}
@Test
public void should_compare_issues_with_database_format() {
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+
// issue messages are trimmed and can be abbreviated when persisted in database.
// Comparing issue messages must use the same format.
DefaultIssue newIssue = newDefaultIssue(" message ", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1");
IssueDto referenceIssue = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum2");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, null, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matching(newIssue)).isSameAs(referenceIssue);
}
@Test
public void past_issue_not_associated_with_line_should_not_cause_npe() throws Exception {
when(lastSnapshots.getSource(project)).thenReturn(load("example2-v1"));
- String source = load("example2-v2");
+ when(index.getSource(project)).thenReturn(load("example2-v2"));
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
DefaultIssue newIssue = newDefaultIssue("Indentation", 9, RuleKey.of("squid", "AvoidCycle"), "foo");
IssueDto referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null);
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), source, project, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matched()).isEmpty();
}
@Test
public void new_issue_not_associated_with_line_should_not_cause_npe() throws Exception {
when(lastSnapshots.getSource(project)).thenReturn(load("example2-v1"));
- String source = load("example2-v2");
+ when(index.getSource(project)).thenReturn(load("example2-v2"));
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
DefaultIssue newIssue = newDefaultIssue("1 branch need to be covered", null, RuleKey.of("squid", "AvoidCycle"), "foo");
IssueDto referenceIssue = newReferenceIssue("Indentationd", 7, "squid", "AvoidCycle", null);
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), source, project, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matched()).isEmpty();
}
@Test
public void issue_not_associated_with_line() throws Exception {
when(lastSnapshots.getSource(project)).thenReturn(load("example2-v1"));
- String source = load("example2-v2");
+ when(index.getSource(project)).thenReturn(load("example2-v2"));
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
DefaultIssue newIssue = newDefaultIssue("1 branch need to be covered", null, RuleKey.of("squid", "AvoidCycle"), null);
IssueDto referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null);
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), source, project, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), sourceHashHolder, result);
assertThat(result.matching(newIssue)).isEqualTo(referenceIssue);
}
@Test
public void should_track_issues_based_on_blocks_recognition_on_example1() throws Exception {
when(lastSnapshots.getSource(project)).thenReturn(load("example1-v1"));
- String source = load("example1-v2");
+ when(index.getSource(project)).thenReturn(load("example1-v2"));
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
IssueDto referenceIssue1 = newReferenceIssue("Indentation", 7, "squid", "AvoidCycle", null);
IssueDto referenceIssue2 = newReferenceIssue("Indentation", 11, "squid", "AvoidCycle", null);
DefaultIssue newIssue4 = newDefaultIssue("Indentation", 21, RuleKey.of("squid", "AvoidCycle"), null);
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(Arrays.asList(newIssue1, newIssue2, newIssue3, newIssue4), Arrays.asList(referenceIssue1, referenceIssue2), source, project, result);
+ tracking.mapIssues(Arrays.asList(newIssue1, newIssue2, newIssue3, newIssue4), Arrays.asList(referenceIssue1, referenceIssue2), sourceHashHolder, result);
assertThat(result.matching(newIssue1)).isNull();
assertThat(result.matching(newIssue2)).isNull();
@Test
public void should_track_issues_based_on_blocks_recognition_on_example2() throws Exception {
when(lastSnapshots.getSource(project)).thenReturn(load("example2-v1"));
- String source = load("example2-v2");
+ when(index.getSource(project)).thenReturn(load("example2-v2"));
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
IssueDto referenceIssue1 = newReferenceIssue("SystemPrintln", 5, "squid", "AvoidCycle", null);
tracking.mapIssues(
Arrays.asList(newIssue1, newIssue2, newIssue3),
Arrays.asList(referenceIssue1),
- source, project, result);
+ sourceHashHolder, result);
assertThat(result.matching(newIssue1)).isNull();
assertThat(result.matching(newIssue2)).isSameAs(referenceIssue1);
@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");
+ when(index.getSource(project)).thenReturn(load("example3-v2"));
+ sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
IssueDto referenceIssue1 = newReferenceIssue("Avoid unused local variables such as 'j'.", 6, "squid", "AvoidCycle", "63c11570fc0a76434156be5f8138fa03");
IssueDto referenceIssue2 = newReferenceIssue("Avoid unused private methods such as 'myMethod()'.", 13, "squid", "NullDeref", "ef23288705d1ef1e512448ace287586e");
tracking.mapIssues(
Arrays.asList(newIssue1, newIssue2, newIssue3, newIssue4, newIssue5),
Arrays.asList(referenceIssue1, referenceIssue2, referenceIssue3),
- source, project, result);
+ sourceHashHolder, result);
assertThat(result.matching(newIssue1)).isNull();
assertThat(result.matching(newIssue2)).isSameAs(referenceIssue2);