aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2014-11-26 15:09:22 +0100
committerJulien HENRY <julien.henry@sonarsource.com>2014-12-02 09:11:06 +0100
commit0c8e5f2b0a7e36bdc7e0ee67b28982c0e7749995 (patch)
tree04491c803516c2a374cee23c18318cb2e28990b1 /plugins
parent1c56d9b8abc3c2a6dc2fdb93221b0dbc36c2c850 (diff)
downloadsonarqube-0c8e5f2b0a7e36bdc7e0ee67b28982c0e7749995.tar.gz
sonarqube-0c8e5f2b0a7e36bdc7e0ee67b28982c0e7749995.zip
SONAR-5868 Issue tracking now use hash WS instead of raw sources
Diffstat (limited to 'plugins')
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTracking.java38
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java28
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java83
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/FileHashes.java (renamed from plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/HashedSequence.java)44
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/HashedSequenceComparator.java46
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizer.java31
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingFileHashes.java (renamed from plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingHashSequence.java)43
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingHashSequenceComparator.java46
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/Sequence.java32
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/SequenceComparator.java42
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/SourceChecksum.java64
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/StringText.java72
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/StringTextComparator.java93
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java90
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingTest.java114
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/SourceHashHolderTest.java79
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizerTest.java19
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/RollingFileHashesTest.java (renamed from plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/StringTextComparatorTest.java)24
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/RollingHashSequenceTest.java57
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/SourceChecksumTest.java61
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/StringTextTest.java41
21 files changed, 300 insertions, 847 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 0e26a7904cc..b470b424f84 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
@@ -30,14 +30,8 @@ import org.sonar.api.BatchExtension;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.rule.RuleKey;
import org.sonar.core.issue.db.IssueDto;
-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.SourceChecksum;
-import org.sonar.plugins.core.issue.tracking.StringText;
-import org.sonar.plugins.core.issue.tracking.StringTextComparator;
+import org.sonar.plugins.core.issue.tracking.RollingFileHashes;
import javax.annotation.Nullable;
@@ -49,10 +43,15 @@ import java.util.Map;
public class IssueTracking implements BatchExtension {
- public IssueTrackingResult track(SourceHashHolder sourceHashHolder, Collection<IssueDto> dbIssues, Collection<DefaultIssue> newIssues) {
+ /**
+ * @param sourceHashHolder Null when working on resource that is not a file (directory/project)
+ */
+ public IssueTrackingResult track(@Nullable SourceHashHolder sourceHashHolder, Collection<IssueDto> dbIssues, Collection<DefaultIssue> newIssues) {
IssueTrackingResult result = new IssueTrackingResult();
- setChecksumOnNewIssues(newIssues, sourceHashHolder);
+ if (sourceHashHolder != null) {
+ setChecksumOnNewIssues(newIssues, sourceHashHolder);
+ }
// Map new issues with old ones
mapIssues(newIssues, dbIssues, sourceHashHolder, result);
@@ -63,9 +62,10 @@ public class IssueTracking implements BatchExtension {
if (issues.isEmpty()) {
return;
}
- List<String> checksums = SourceChecksum.lineChecksumsOfFile(sourceHashHolder.getSource());
for (DefaultIssue issue : issues) {
- issue.setChecksum(SourceChecksum.getChecksumForLine(checksums, issue.line()));
+ if (issue.line() != null) {
+ issue.setChecksum(sourceHashHolder.getHashedSource().getHash(issue.line()));
+ }
}
}
@@ -80,7 +80,7 @@ public class IssueTracking implements BatchExtension {
// If each new issue matches an old one we can stop the matching mechanism
if (result.matched().size() != newIssues.size()) {
- if (sourceHashHolder.hasBothReferenceAndCurrentSource() && hasLastScan) {
+ if (sourceHashHolder != null && sourceHashHolder.getHashedReference() != null && hasLastScan) {
mapNewissues(sourceHashHolder, newIssues, result);
}
mapIssuesOnSameRule(newIssues, result);
@@ -110,12 +110,10 @@ public class IssueTracking implements BatchExtension {
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(sourceHashHolder.getHashedReference(), sourceHashHolder.getHashedSource());
- 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);
+ RollingFileHashes a = RollingFileHashes.create(sourceHashHolder.getHashedReference(), 5);
+ RollingFileHashes b = RollingFileHashes.create(sourceHashHolder.getHashedSource(), 5);
Multimap<Integer, DefaultIssue> newIssuesByLines = newIssuesByLines(newIssues, rec, result);
Multimap<Integer, IssueDto> lastIssuesByLines = lastIssuesByLines(result.unmatched(), rec);
@@ -123,7 +121,7 @@ public class IssueTracking implements BatchExtension {
Map<Integer, HashOccurrence> map = Maps.newHashMap();
for (Integer line : lastIssuesByLines.keySet()) {
- int hash = cmp.hash(a, line - 1);
+ int hash = a.getHash(line);
HashOccurrence hashOccurrence = map.get(hash);
if (hashOccurrence == null) {
// first occurrence in A
@@ -137,7 +135,7 @@ public class IssueTracking implements BatchExtension {
}
for (Integer line : newIssuesByLines.keySet()) {
- int hash = cmp.hash(b, line - 1);
+ int hash = b.getHash(line);
HashOccurrence hashOccurrence = map.get(hash);
if (hashOccurrence != null) {
hashOccurrence.lineB = line;
@@ -159,7 +157,7 @@ public class IssueTracking implements BatchExtension {
List<LinePair> possibleLinePairs = Lists.newArrayList();
for (Integer oldLine : lastIssuesByLines.keySet()) {
for (Integer newLine : newIssuesByLines.keySet()) {
- int weight = rec.computeLengthOfMaximalBlock(oldLine - 1, newLine - 1);
+ int weight = rec.computeLengthOfMaximalBlock(oldLine, newLine);
possibleLinePairs.add(new LinePair(oldLine, newLine, weight));
}
}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java
index 37276d00f8e..7c39348de1f 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java
@@ -30,13 +30,15 @@ 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.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.component.ResourcePerspectives;
import org.sonar.api.issue.Issuable;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.issue.internal.IssueChangeContext;
import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
@@ -46,7 +48,8 @@ import org.sonar.api.rules.RuleFinder;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.scan.LastSnapshots;
+import org.sonar.batch.scan.LastLineHashes;
+import org.sonar.batch.scan.filesystem.InputPathCache;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.core.issue.db.IssueChangeDto;
import org.sonar.core.issue.db.IssueDto;
@@ -63,8 +66,7 @@ public class IssueTrackingDecorator implements Decorator {
private final IssueCache issueCache;
private final InitialOpenIssuesStack initialOpenIssues;
private final IssueTracking tracking;
- private final LastSnapshots lastSnapshots;
- private final SonarIndex index;
+ private final LastLineHashes lastLineHashes;
private final IssueHandlers handlers;
private final IssueWorkflow workflow;
private final IssueUpdater updater;
@@ -72,23 +74,26 @@ public class IssueTrackingDecorator implements Decorator {
private final ResourcePerspectives perspectives;
private final RulesProfile rulesProfile;
private final RuleFinder ruleFinder;
+ private final InputPathCache inputPathCache;
+ private final Project project;
public IssueTrackingDecorator(IssueCache issueCache, InitialOpenIssuesStack initialOpenIssues, IssueTracking tracking,
- LastSnapshots lastSnapshots, SonarIndex index,
+ LastLineHashes lastLineHashes,
IssueHandlers handlers, IssueWorkflow workflow,
IssueUpdater updater,
Project project,
ResourcePerspectives perspectives,
RulesProfile rulesProfile,
- RuleFinder ruleFinder) {
+ RuleFinder ruleFinder, InputPathCache inputPathCache) {
this.issueCache = issueCache;
this.initialOpenIssues = initialOpenIssues;
this.tracking = tracking;
- this.lastSnapshots = lastSnapshots;
- this.index = index;
+ this.lastLineHashes = lastLineHashes;
this.handlers = handlers;
this.workflow = workflow;
this.updater = updater;
+ this.project = project;
+ this.inputPathCache = inputPathCache;
this.changeContext = IssueChangeContext.createScan(project.getAnalysisDate());
this.perspectives = perspectives;
this.rulesProfile = rulesProfile;
@@ -120,7 +125,12 @@ public class IssueTrackingDecorator implements Decorator {
// all the issues that are not closed in db before starting this module scan, including manual issues
Collection<IssueDto> dbOpenIssues = initialOpenIssues.selectAndRemoveIssues(resource.getEffectiveKey());
- SourceHashHolder sourceHashHolder = new SourceHashHolder(index, lastSnapshots, resource);
+ SourceHashHolder sourceHashHolder = null;
+ if (ResourceUtils.isFile(resource)) {
+ File sonarFile = (File) resource;
+ InputFile file = inputPathCache.getFile(project.getEffectiveKey(), sonarFile.getPath());
+ sourceHashHolder = new SourceHashHolder((DefaultInputFile) file, lastLineHashes);
+ }
IssueTrackingResult trackingResult = tracking.track(sourceHashHolder, dbOpenIssues, issues);
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java
index c10586fda05..ffb8be6b4ef 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java
@@ -19,77 +19,52 @@
*/
package org.sonar.plugins.core.issue;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.SonarIndex;
-import org.sonar.api.resources.Resource;
-import org.sonar.batch.scan.LastSnapshots;
-import org.sonar.plugins.core.issue.tracking.HashedSequence;
-import org.sonar.plugins.core.issue.tracking.StringText;
-import org.sonar.plugins.core.issue.tracking.StringTextComparator;
+import org.sonar.api.batch.fs.InputFile.Status;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.batch.scan.LastLineHashes;
+import org.sonar.plugins.core.issue.tracking.FileHashes;
+
+import javax.annotation.CheckForNull;
import java.util.Collection;
public class SourceHashHolder {
- private final SonarIndex index;
- private final LastSnapshots lastSnapshots;
- private final Resource resource;
-
- private String source;
- private boolean sourceInitialized;
- private String referenceSource;
- private boolean referenceSourceInitialized;
+ private final LastLineHashes lastSnapshots;
- private HashedSequence<StringText> hashedReference;
- private HashedSequence<StringText> hashedSource;
+ private FileHashes hashedReference;
+ private FileHashes hashedSource;
+ private DefaultInputFile inputFile;
- public SourceHashHolder(SonarIndex index, LastSnapshots lastSnapshots, Resource resource) {
- this.index = index;
+ public SourceHashHolder(DefaultInputFile inputFile, LastLineHashes lastSnapshots) {
+ this.inputFile = inputFile;
this.lastSnapshots = lastSnapshots;
- this.resource = resource;
}
private void initHashes() {
- hashedReference = HashedSequence.wrap(new StringText(getReferenceSource()), StringTextComparator.IGNORE_WHITESPACE);
- hashedSource = HashedSequence.wrap(new StringText(getSource()), StringTextComparator.IGNORE_WHITESPACE);
- }
-
- public HashedSequence<StringText> getHashedReference() {
- initHashesIfNull(hashedReference);
- return hashedReference;
- }
-
- public HashedSequence<StringText> getHashedSource() {
- initHashesIfNull(hashedSource);
- return hashedSource;
- }
-
- public String getSource() {
- if (!sourceInitialized) {
- source = StringUtils.defaultString(index.getSource(resource), "");
- sourceInitialized = true;
- }
- return source;
- }
-
- public String getReferenceSource() {
- if (!referenceSourceInitialized) {
- if (resource != null) {
- referenceSource = lastSnapshots.getSource(resource);
+ if (hashedSource == null) {
+ hashedSource = FileHashes.create(inputFile.lineHashes());
+ Status status = inputFile.status();
+ if (status == Status.ADDED) {
+ hashedReference = null;
+ } else if (status == Status.SAME) {
+ hashedReference = hashedSource;
+ } else {
+ String[] lineHashes = lastSnapshots.getLineHashes(inputFile.key());
+ hashedReference = lineHashes != null ? FileHashes.create(lineHashes) : null;
}
- referenceSourceInitialized = true;
}
- return referenceSource;
}
- public boolean hasBothReferenceAndCurrentSource() {
- return getSource() != null && getReferenceSource() != null;
+ @CheckForNull
+ public FileHashes getHashedReference() {
+ initHashes();
+ return hashedReference;
}
- private void initHashesIfNull(Object required) {
- if (required == null) {
- initHashes();
- }
+ public FileHashes getHashedSource() {
+ initHashes();
+ return hashedSource;
}
public Collection<Integer> getNewLinesMatching(Integer originLine) {
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/HashedSequence.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/FileHashes.java
index abfc23fc982..d45ac2d65cc 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/HashedSequence.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/FileHashes.java
@@ -21,47 +21,57 @@ package org.sonar.plugins.core.issue.tracking;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.lang.ObjectUtils;
import java.util.Collection;
/**
* Wraps a {@link Sequence} to assign hash codes to elements.
*/
-public final class HashedSequence<S extends Sequence> implements Sequence {
+public final class FileHashes {
- final S base;
- final int[] hashes;
- final Multimap<Integer, Integer> linesByHash;
+ private final String[] hashes;
+ private final Multimap<String, Integer> linesByHash;
- private HashedSequence(S base, int[] hashes, Multimap<Integer, Integer> linesByHash) {
- this.base = base;
+ private FileHashes(String[] hashes, Multimap<String, Integer> linesByHash) {
this.hashes = hashes;
this.linesByHash = linesByHash;
}
- public static <S extends Sequence> HashedSequence<S> wrap(S base, SequenceComparator<S> cmp) {
- int size = base.length();
- int[] hashes = new int[size];
- Multimap<Integer, Integer> linesByHash = LinkedHashMultimap.create();
+ public static FileHashes create(String[] hashes) {
+ int size = hashes.length;
+ Multimap<String, Integer> linesByHash = LinkedHashMultimap.create();
for (int i = 0; i < size; i++) {
- hashes[i] = cmp.hash(base, i);
// indices in array are shifted one line before
linesByHash.put(hashes[i], i + 1);
}
- return new HashedSequence<S>(base, hashes, linesByHash);
+ return new FileHashes(hashes, linesByHash);
+ }
+
+ public static FileHashes create(byte[][] hashes) {
+ int size = hashes.length;
+ Multimap<String, Integer> linesByHash = LinkedHashMultimap.create();
+ String[] hexHashes = new String[size];
+ for (int i = 0; i < size; i++) {
+ String hash = hashes[i] != null ? Hex.encodeHexString(hashes[i]) : "";
+ hexHashes[i] = hash;
+ // indices in array are shifted one line before
+ linesByHash.put(hash, i + 1);
+ }
+ return new FileHashes(hexHashes, linesByHash);
}
- @Override
public int length() {
- return base.length();
+ return hashes.length;
}
- public Collection<Integer> getLinesForHash(Integer hash) {
+ public Collection<Integer> getLinesForHash(String hash) {
return linesByHash.get(hash);
}
- public Integer getHash(Integer line) {
+ public String getHash(int line) {
// indices in array are shifted one line before
- return hashes[line - 1];
+ return (String) ObjectUtils.defaultIfNull(hashes[line - 1], "");
}
}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/HashedSequenceComparator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/HashedSequenceComparator.java
deleted file mode 100644
index 093cf962698..00000000000
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/HashedSequenceComparator.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.core.issue.tracking;
-
-/**
- * Wrap another {@link SequenceComparator} for use with {@link HashedSequence}.
- */
-public class HashedSequenceComparator<S extends Sequence> implements SequenceComparator<HashedSequence<S>> {
-
- private final SequenceComparator<? super S> cmp;
-
- public HashedSequenceComparator(SequenceComparator<? super S> cmp) {
- this.cmp = cmp;
- }
-
- @Override
- public boolean equals(HashedSequence<S> a, int ai, HashedSequence<S> b, int bi) {
- if (a.hashes[ai] == b.hashes[bi]) {
- return cmp.equals(a.base, ai, b.base, bi);
- }
- return false;
- }
-
- @Override
- public int hash(HashedSequence<S> seq, int i) {
- return seq.hashes[i];
- }
-
-}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizer.java
index 15d0bc15398..11611ad952d 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizer.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizer.java
@@ -19,31 +19,16 @@
*/
package org.sonar.plugins.core.issue.tracking;
-import com.google.common.annotations.VisibleForTesting;
-import org.sonar.plugins.core.issue.tracking.HashedSequence;
-import org.sonar.plugins.core.issue.tracking.HashedSequenceComparator;
-import org.sonar.plugins.core.issue.tracking.StringText;
-import org.sonar.plugins.core.issue.tracking.StringTextComparator;
-
import javax.annotation.Nullable;
public class IssueTrackingBlocksRecognizer {
- private final HashedSequence<StringText> a;
- private final HashedSequence<StringText> b;
- private final HashedSequenceComparator<StringText> cmp;
-
- @VisibleForTesting
- public IssueTrackingBlocksRecognizer(String referenceSource, String source) {
- this.a = HashedSequence.wrap(new StringText(referenceSource), StringTextComparator.IGNORE_WHITESPACE);
- this.b = HashedSequence.wrap(new StringText(source), StringTextComparator.IGNORE_WHITESPACE);
- this.cmp = new HashedSequenceComparator<StringText>(StringTextComparator.IGNORE_WHITESPACE);
- }
+ private final FileHashes a;
+ private final FileHashes b;
- public IssueTrackingBlocksRecognizer(HashedSequence<StringText> a, HashedSequence<StringText> b, HashedSequenceComparator<StringText> cmp) {
+ public IssueTrackingBlocksRecognizer(FileHashes a, FileHashes b) {
this.a = a;
this.b = b;
- this.cmp = cmp;
}
public boolean isValidLineInReference(@Nullable Integer line) {
@@ -55,24 +40,24 @@ public class IssueTrackingBlocksRecognizer {
}
/**
- * @param startA number of line from first version of text (numbering starts from 0)
- * @param startB number of line from second version of text (numbering starts from 0)
+ * @param startA number of line from first version of text (numbering starts from 1)
+ * @param startB number of line from second version of text (numbering starts from 1)
*/
public int computeLengthOfMaximalBlock(int startA, int startB) {
- if (!cmp.equals(a, startA, b, startB)) {
+ if (!a.getHash(startA).equals(b.getHash(startB))) {
return 0;
}
int length = 0;
int ai = startA;
int bi = startB;
- while (ai < a.length() && bi < b.length() && cmp.equals(a, ai, b, bi)) {
+ while (ai <= a.length() && bi <= b.length() && a.getHash(ai).equals(b.getHash(bi))) {
ai++;
bi++;
length++;
}
ai = startA;
bi = startB;
- while (ai >= 0 && bi >= 0 && cmp.equals(a, ai, b, bi)) {
+ while (ai > 0 && bi > 0 && a.getHash(ai).equals(b.getHash(bi))) {
ai--;
bi--;
length++;
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingHashSequence.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingFileHashes.java
index cb3d7acfc09..313507a4d69 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingHashSequence.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingFileHashes.java
@@ -20,44 +20,41 @@
package org.sonar.plugins.core.issue.tracking;
/**
- * Wraps a {@link Sequence} to assign hash codes to elements.
+ * Compute hashes of block around each line
*/
-public class RollingHashSequence<S extends Sequence> implements Sequence {
+public class RollingFileHashes {
- final S base;
- final int[] hashes;
+ final int[] rollingHashes;
- public static <S extends Sequence> RollingHashSequence<S> wrap(S base, SequenceComparator<S> cmp, int lines) {
- int size = base.length();
- int[] hashes = new int[size];
+ public static RollingFileHashes create(FileHashes hashes, int halfBlockSize) {
+ int size = hashes.length();
+ int[] rollingHashes = new int[size];
- RollingHashCalculator hashCalulator = new RollingHashCalculator(lines * 2 + 1);
- for (int i = 0; i <= Math.min(size - 1, lines); i++) {
- hashCalulator.add(cmp.hash(base, i));
+ RollingHashCalculator hashCalulator = new RollingHashCalculator(halfBlockSize * 2 + 1);
+ for (int i = 1; i <= Math.min(size, halfBlockSize + 1); i++) {
+ hashCalulator.add(hashes.getHash(i).hashCode());
}
- for (int i = 0; i < size; i++) {
- hashes[i] = hashCalulator.getHash();
- if (i - lines >= 0) {
- hashCalulator.remove(cmp.hash(base, i - lines));
+ for (int i = 1; i <= size; i++) {
+ rollingHashes[i - 1] = hashCalulator.getHash();
+ if (i - halfBlockSize > 0) {
+ hashCalulator.remove(hashes.getHash(i - halfBlockSize).hashCode());
}
- if (i + lines + 1 < size) {
- hashCalulator.add(cmp.hash(base, i + lines + 1));
+ if (i + 1 + halfBlockSize <= size) {
+ hashCalulator.add(hashes.getHash(i + 1 + halfBlockSize).hashCode());
} else {
hashCalulator.add(0);
}
}
- return new RollingHashSequence<S>(base, hashes);
+ return new RollingFileHashes(rollingHashes);
}
- private RollingHashSequence(S base, int[] hashes) {
- this.base = base;
- this.hashes = hashes;
+ public int getHash(int line) {
+ return rollingHashes[line - 1];
}
- @Override
- public int length() {
- return base.length();
+ private RollingFileHashes(int[] hashes) {
+ this.rollingHashes = hashes;
}
private static class RollingHashCalculator {
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingHashSequenceComparator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingHashSequenceComparator.java
deleted file mode 100644
index b3663b47d48..00000000000
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/RollingHashSequenceComparator.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.core.issue.tracking;
-
-/**
- * Wrap another {@link SequenceComparator} for use with {@link RollingHashSequence}.
- */
-public class RollingHashSequenceComparator<S extends Sequence> implements SequenceComparator<RollingHashSequence<S>> {
-
- private final SequenceComparator<? super S> cmp;
-
- public RollingHashSequenceComparator(SequenceComparator<? super S> cmp) {
- this.cmp = cmp;
- }
-
- @Override
- public boolean equals(RollingHashSequence<S> a, int ai, RollingHashSequence<S> b, int bi) {
- if (a.hashes[ai] == b.hashes[bi]) {
- return cmp.equals(a.base, ai, b.base, bi);
- }
- return false;
- }
-
- @Override
- public int hash(RollingHashSequence<S> seq, int i) {
- return seq.hashes[i];
- }
-
-}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/Sequence.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/Sequence.java
deleted file mode 100644
index e4f2b12e668..00000000000
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/Sequence.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.core.issue.tracking;
-
-/**
- * Arbitrary sequence of elements.
- */
-public interface Sequence {
-
- /**
- * @return total number of items in the sequence
- */
- int length();
-
-}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/SequenceComparator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/SequenceComparator.java
deleted file mode 100644
index b2a860531a3..00000000000
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/SequenceComparator.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.core.issue.tracking;
-
-/**
- * Equivalence function for a {@link Sequence}.
- */
-public interface SequenceComparator<S extends Sequence> {
-
- /**
- * Compare two items to determine if they are equivalent.
- */
- boolean equals(S a, int ai, S b, int bi);
-
- /**
- * Get a hash value for an item in a sequence.
- *
- * If two items are equal according to this comparator's
- * {@link #equals(Sequence, int, Sequence, int)} method,
- * then this hash method must produce the same integer result for both items.
- * However not required to have different hash values for different items.
- */
- int hash(S seq, int i);
-
-}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/SourceChecksum.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/SourceChecksum.java
deleted file mode 100644
index 21c1bb207f7..00000000000
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/SourceChecksum.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.core.issue.tracking;
-
-import com.google.common.collect.Lists;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.lang.StringUtils;
-
-import javax.annotation.Nullable;
-import java.util.List;
-
-public final class SourceChecksum {
-
- private static final String SPACE_CHARS = "\t\n\r ";
-
- private SourceChecksum() {
- // only static methods
- }
-
- /**
- * @param line line number (first line has number 1)
- * @return checksum or null if checksum not exists for line
- */
- public static String getChecksumForLine(List<String> checksums, @Nullable Integer line) {
- if (line == null || line < 1 || line > checksums.size()) {
- return null;
- }
- return checksums.get(line - 1);
- }
-
- public static List<String> lineChecksumsOfFile(String file) {
- List<String> result = Lists.newArrayList();
- if (file != null) {
- String[] lines = file.split("\r?\n|\r", -1);
- for (String line : lines) {
- result.add(lineChecksum(line));
- }
- }
- return result;
- }
-
- public static String lineChecksum(String line) {
- String reducedLine = StringUtils.replaceChars(line, SPACE_CHARS, "");
- return DigestUtils.md5Hex(reducedLine);
- }
-
-}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/StringText.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/StringText.java
deleted file mode 100644
index 50ce2d4912f..00000000000
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/StringText.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.core.issue.tracking;
-
-import com.google.common.collect.Lists;
-
-import java.util.List;
-
-/**
- * Text is a {@link Sequence} of lines.
- */
-public class StringText implements Sequence {
-
- final String content;
-
- /**
- * Map of line number to starting position within {@link #content}.
- */
- final List<Integer> lines;
-
- public StringText(String str) {
- this.content = str;
- this.lines = lineMap(content, 0, content.length());
- }
-
- @Override
- public int length() {
- return lines.size() - 2;
- }
-
- private static List<Integer> lineMap(String buf, int ptr, int end) {
- List<Integer> lines = Lists.newArrayList();
- lines.add(Integer.MIN_VALUE);
- for (; ptr < end; ptr = nextLF(buf, ptr)) {
- lines.add(ptr);
- }
- lines.add(end);
- return lines;
- }
-
- private static int nextLF(String b, int ptr) {
- return next(b, ptr, '\n');
- }
-
- private static int next(final String b, int ptr, final char chrA) {
- final int sz = b.length();
- while (ptr < sz) {
- if (b.charAt(ptr++) == chrA) {
- return ptr;
- }
- }
- return ptr;
- }
-
-}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/StringTextComparator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/StringTextComparator.java
deleted file mode 100644
index e7f63900ea1..00000000000
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/tracking/StringTextComparator.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.core.issue.tracking;
-
-/**
- * Equivalence function for {@link StringText}.
- */
-public abstract class StringTextComparator implements SequenceComparator<StringText> {
-
- /**
- * Ignores all whitespace.
- */
- public static final StringTextComparator IGNORE_WHITESPACE = new StringTextComparator() {
-
- @Override
- public boolean equals(StringText a, int ai, StringText b, int bi) {
- ai++;
- bi++;
- int as = a.lines.get(ai);
- int bs = b.lines.get(bi);
- int ae = a.lines.get(ai + 1);
- int be = b.lines.get(bi + 1);
- ae = trimTrailingWhitespace(a.content, as, ae);
- be = trimTrailingWhitespace(b.content, bs, be);
- while ((as < ae) && (bs < be)) {
- char ac = a.content.charAt(as);
- char bc = b.content.charAt(bs);
- while ((as < ae - 1) && (Character.isWhitespace(ac))) {
- as++;
- ac = a.content.charAt(as);
- }
- while ((bs < be - 1) && (Character.isWhitespace(bc))) {
- bs++;
- bc = b.content.charAt(bs);
- }
- if (ac != bc) {
- return false;
- }
- as++;
- bs++;
- }
- return (as == ae) && (bs == be);
- }
-
- @Override
- protected int hashRegion(String content, int start, int end) {
- int hash = 5381;
- for (; start < end; start++) {
- char c = content.charAt(start);
- if (!Character.isWhitespace(c)) {
- hash = ((hash << 5) + hash) + (c & 0xff);
- }
- }
- return hash;
- }
-
- };
-
- @Override
- public int hash(StringText seq, int line) {
- final int begin = seq.lines.get(line + 1);
- final int end = seq.lines.get(line + 2);
- return hashRegion(seq.content, begin, end);
- }
-
- protected abstract int hashRegion(String content, int start, int end);
-
- public static int trimTrailingWhitespace(String content, int start, int end) {
- end--;
- while (start <= end && Character.isWhitespace(content.charAt(end))) {
- end--;
- }
- return end + 1;
- }
-
-}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java
index 7d4bc08c27a..844b2a8dfd8 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java
@@ -19,12 +19,13 @@
*/
package org.sonar.plugins.core.issue;
+import org.apache.commons.codec.digest.DigestUtils;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.sonar.api.batch.DecoratorContext;
-import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.component.ResourcePerspectives;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
@@ -38,7 +39,8 @@ import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleFinder;
import org.sonar.api.utils.Duration;
import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.scan.LastSnapshots;
+import org.sonar.batch.scan.LastLineHashes;
+import org.sonar.batch.scan.filesystem.InputPathCache;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.core.issue.db.IssueChangeDto;
import org.sonar.core.issue.db.IssueDto;
@@ -55,11 +57,17 @@ import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyCollection;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.*;
-import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.RETURNS_MOCKS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
@@ -67,14 +75,14 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
IssueCache issueCache = mock(IssueCache.class, RETURNS_MOCKS);
InitialOpenIssuesStack initialOpenIssues = mock(InitialOpenIssuesStack.class);
IssueTracking tracking = mock(IssueTracking.class, RETURNS_MOCKS);
- LastSnapshots lastSnapshots = mock(LastSnapshots.class);
- SonarIndex index = mock(SonarIndex.class);
+ LastLineHashes lastSnapshots = mock(LastLineHashes.class);
IssueHandlers handlers = mock(IssueHandlers.class);
IssueWorkflow workflow = mock(IssueWorkflow.class);
IssueUpdater updater = mock(IssueUpdater.class);
ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
RulesProfile profile = mock(RulesProfile.class);
RuleFinder ruleFinder = mock(RuleFinder.class);
+ InputPathCache inputPathCache = mock(InputPathCache.class);
@Before
public void init() {
@@ -83,14 +91,14 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
initialOpenIssues,
tracking,
lastSnapshots,
- index,
handlers,
workflow,
updater,
- mock(Project.class),
+ new Project("foo"),
perspectives,
profile,
- ruleFinder);
+ ruleFinder,
+ inputPathCache);
}
@Test
@@ -160,8 +168,6 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
@Test
public void manual_issues_should_be_moved_if_matching_line_found() throws Exception {
- Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
-
// INPUT : one issue existing during previous scan
IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(6).setStatus("OPEN").setRuleKey("manual", "Performance");
when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance"));
@@ -183,8 +189,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
+ " void method3();\n"
+ " void method4();\n"
+ "}";
- when(index.getSource(file)).thenReturn(newSource);
- when(lastSnapshots.getSource(file)).thenReturn(originalSource);
+ Resource file = mockHashes(originalSource, newSource);
when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
@@ -204,9 +209,19 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
assertThat(issue.isOnDisabledRule()).isFalse();
}
+ private Resource mockHashes(String originalSource, String newSource) {
+ DefaultInputFile inputFile = mock(DefaultInputFile.class);
+ byte[][] hashes = computeHashes(newSource);
+ when(inputFile.lineHashes()).thenReturn(hashes);
+ when(inputFile.key()).thenReturn("foo:Action.java");
+ when(inputPathCache.getFile("foo", "Action.java")).thenReturn(inputFile);
+ when(lastSnapshots.getLineHashes("foo:Action.java")).thenReturn(computeHexHashes(originalSource));
+ Resource file = File.create("Action.java");
+ return file;
+ }
+
@Test
public void manual_issues_should_be_untouched_if_already_closed() throws Exception {
- Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
// INPUT : one issue existing during previous scan
IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("CLOSED").setRuleKey("manual", "Performance");
@@ -216,8 +231,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
trackingResult.addUnmatched(unmatchedIssue);
String originalSource = "public interface Action {}";
- when(index.getSource(file)).thenReturn(originalSource);
- when(lastSnapshots.getSource(file)).thenReturn(originalSource);
+ Resource file = mockHashes(originalSource, originalSource);
when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
@@ -240,7 +254,6 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
@Test
public void manual_issues_should_be_untouched_if_line_is_null() throws Exception {
- Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
// INPUT : one issue existing during previous scan
IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(null).setStatus("OPEN").setRuleKey("manual", "Performance");
@@ -250,8 +263,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
trackingResult.addUnmatched(unmatchedIssue);
String originalSource = "public interface Action {}";
- when(index.getSource(file)).thenReturn(originalSource);
- when(lastSnapshots.getSource(file)).thenReturn(originalSource);
+ Resource file = mockHashes(originalSource, originalSource);
when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
@@ -275,7 +287,6 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
@Test
public void manual_issues_should_be_kept_if_matching_line_not_found() throws Exception {
// "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
- Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
// INPUT : one issue existing during previous scan
final int issueOnLine = 6;
@@ -299,8 +310,8 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
+ " void method4();\n"
+ " void method6();\n" // Poof, no method5 anymore
+ "}";
- when(index.getSource(file)).thenReturn(newSource);
- when(lastSnapshots.getSource(file)).thenReturn(originalSource);
+
+ Resource file = mockHashes(originalSource, newSource);
when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
@@ -323,7 +334,6 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
@Test
public void manual_issues_should_be_kept_if_multiple_matching_lines_found() throws Exception {
// "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
- Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
// INPUT : one issue existing during previous scan
final int issueOnLine = 3;
@@ -347,8 +357,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
+ " notify();\n"
+ " }\n"
+ "}";
- when(index.getSource(file)).thenReturn(newSource);
- when(lastSnapshots.getSource(file)).thenReturn(originalSource);
+ Resource file = mockHashes(originalSource, newSource);
when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
@@ -368,11 +377,9 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
assertThat(issue.isOnDisabledRule()).isFalse();
}
-
@Test
public void manual_issues_should_be_closed_if_manual_rule_is_removed() throws Exception {
// "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
- Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
// INPUT : one issue existing during previous scan
IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("OPEN").setRuleKey("manual", "Performance");
@@ -382,8 +389,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
trackingResult.addUnmatched(unmatchedIssue);
String source = "public interface Action {}";
- when(index.getSource(file)).thenReturn(source);
- when(lastSnapshots.getSource(file)).thenReturn(source);
+ Resource file = mockHashes(source, source);
when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
@@ -405,7 +411,6 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
@Test
public void manual_issues_should_be_closed_if_manual_rule_is_not_found() throws Exception {
// "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
- Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
// INPUT : one issue existing during previous scan
IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("OPEN").setRuleKey("manual", "Performance");
@@ -415,8 +420,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
trackingResult.addUnmatched(unmatchedIssue);
String source = "public interface Action {}";
- when(index.getSource(file)).thenReturn(source);
- when(lastSnapshots.getSource(file)).thenReturn(source);
+ Resource file = mockHashes(source, source);
when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
@@ -438,7 +442,6 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
@Test
public void manual_issues_should_be_closed_if_new_source_is_shorter() throws Exception {
// "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
- Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
// INPUT : one issue existing during previous scan
IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(6).setStatus("OPEN").setRuleKey("manual", "Performance");
@@ -458,8 +461,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
+ " void method1();\n"
+ " void method2();\n"
+ "}";
- when(index.getSource(file)).thenReturn(newSource);
- when(lastSnapshots.getSource(file)).thenReturn(originalSource);
+ Resource file = mockHashes(originalSource, newSource);
when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
@@ -556,4 +558,22 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
assertThat(issue.changes()).hasSize(1);
}
+ private byte[][] computeHashes(String source) {
+ String[] lines = source.split("\n");
+ byte[][] hashes = new byte[lines.length][];
+ for (int i = 0; i < lines.length; i++) {
+ hashes[i] = DigestUtils.md5(lines[i].replaceAll("[\t ]", ""));
+ }
+ return hashes;
+ }
+
+ private String[] computeHexHashes(String source) {
+ String[] lines = source.split("\n");
+ String[] hashes = new String[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ hashes[i] = DigestUtils.md5Hex(lines[i].replaceAll("[\t ]", ""));
+ }
+ return hashes;
+ }
+
}
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 4b14f77c5b5..bef10585986 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
@@ -22,15 +22,16 @@ package org.sonar.plugins.core.issue;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
+import org.apache.commons.codec.digest.DigestUtils;
import org.junit.Before;
import org.junit.Test;
-import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.rule.RuleKey;
-import org.sonar.batch.scan.LastSnapshots;
+import org.sonar.batch.scan.LastLineHashes;
import org.sonar.core.issue.db.IssueDto;
import java.io.IOException;
@@ -48,14 +49,12 @@ public class IssueTrackingTest {
IssueTracking tracking;
Resource project;
SourceHashHolder sourceHashHolder;
- SonarIndex index;
- LastSnapshots lastSnapshots;
+ LastLineHashes lastSnapshots;
long violationId = 0;
@Before
public void before() {
- index = mock(SonarIndex.class);
- lastSnapshots = mock(LastSnapshots.class);
+ lastSnapshots = mock(LastLineHashes.class);
project = mock(Project.class);
tracking = new IssueTracking();
@@ -77,8 +76,6 @@ public class IssueTrackingTest {
@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");
@@ -86,7 +83,7 @@ public class IssueTrackingTest {
DefaultIssue newIssue2 = newDefaultIssue("message", 5, RuleKey.of("squid", "AvoidCycle"), "checksum2");
IssueTrackingResult result = new IssueTrackingResult();
- tracking.mapIssues(newArrayList(newIssue1, newIssue2), newArrayList(referenceIssue1, referenceIssue2), sourceHashHolder, result);
+ tracking.mapIssues(newArrayList(newIssue1, newIssue2), newArrayList(referenceIssue1, referenceIssue2), null, result);
assertThat(result.matching(newIssue1)).isSameAs(referenceIssue1);
assertThat(result.matching(newIssue2)).isSameAs(referenceIssue2);
}
@@ -96,61 +93,51 @@ public class IssueTrackingTest {
*/
@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), sourceHashHolder, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, 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), sourceHashHolder, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, 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), sourceHashHolder, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, 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), sourceHashHolder, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, 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), sourceHashHolder, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result);
assertThat(result.matching(newIssue)).isSameAs(referenceIssue);
}
@@ -159,80 +146,66 @@ public class IssueTrackingTest {
*/
@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), sourceHashHolder, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, 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), sourceHashHolder, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, 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), sourceHashHolder, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, 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), sourceHashHolder, result);
+ tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, 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"));
- when(index.getSource(project)).thenReturn(load("example2-v2"));
- sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
+ initLastHashes("example2-v1", "example2-v2");
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), sourceHashHolder, result);
+ IssueTrackingResult result = tracking.track(sourceHashHolder, newArrayList(referenceIssue), newArrayList(newIssue));
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"));
- when(index.getSource(project)).thenReturn(load("example2-v2"));
- sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
+ initLastHashes("example2-v1", "example2-v2");
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), sourceHashHolder, result);
+ IssueTrackingResult result = tracking.track(sourceHashHolder, newArrayList(referenceIssue), newArrayList(newIssue));
assertThat(result.matched()).isEmpty();
}
@@ -242,15 +215,12 @@ public class IssueTrackingTest {
*/
@Test
public void issue_not_associated_with_line() throws Exception {
- when(lastSnapshots.getSource(project)).thenReturn(load("example2-v1"));
- when(index.getSource(project)).thenReturn(load("example2-v2"));
- sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
+ initLastHashes("example2-v1", "example2-v2");
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), sourceHashHolder, result);
+ IssueTrackingResult result = tracking.track(sourceHashHolder, newArrayList(referenceIssue), newArrayList(newIssue));
assertThat(result.matching(newIssue)).isEqualTo(referenceIssue);
}
@@ -260,9 +230,7 @@ public class IssueTrackingTest {
*/
@Test
public void should_track_issues_based_on_blocks_recognition_on_example1() throws Exception {
- when(lastSnapshots.getSource(project)).thenReturn(load("example1-v1"));
- when(index.getSource(project)).thenReturn(load("example1-v2"));
- sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
+ initLastHashes("example1-v1", "example1-v2");
IssueDto referenceIssue1 = newReferenceIssue("Indentation", 7, "squid", "AvoidCycle", null);
IssueDto referenceIssue2 = newReferenceIssue("Indentation", 11, "squid", "AvoidCycle", null);
@@ -272,8 +240,7 @@ public class IssueTrackingTest {
DefaultIssue newIssue3 = newDefaultIssue("Indentation", 17, RuleKey.of("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), sourceHashHolder, result);
+ IssueTrackingResult result = tracking.track(sourceHashHolder, Arrays.asList(referenceIssue1, referenceIssue2), Arrays.asList(newIssue1, newIssue2, newIssue3, newIssue4));
assertThat(result.matching(newIssue1)).isNull();
assertThat(result.matching(newIssue2)).isNull();
@@ -286,9 +253,7 @@ public class IssueTrackingTest {
*/
@Test
public void should_track_issues_based_on_blocks_recognition_on_example2() throws Exception {
- when(lastSnapshots.getSource(project)).thenReturn(load("example2-v1"));
- when(index.getSource(project)).thenReturn(load("example2-v2"));
- sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
+ initLastHashes("example2-v1", "example2-v2");
IssueDto referenceIssue1 = newReferenceIssue("SystemPrintln", 5, "squid", "AvoidCycle", null);
@@ -309,9 +274,7 @@ public class IssueTrackingTest {
@Test
public void should_track_issues_based_on_blocks_recognition_on_example3() throws Exception {
- when(lastSnapshots.getSource(project)).thenReturn(load("example3-v1"));
- when(index.getSource(project)).thenReturn(load("example3-v2"));
- sourceHashHolder = new SourceHashHolder(index, lastSnapshots, project);
+ initLastHashes("example3-v1", "example3-v2");
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");
@@ -376,4 +339,31 @@ public class IssueTrackingTest {
referenceIssue.setStatus(Issue.STATUS_OPEN);
return referenceIssue;
}
+
+ private void initLastHashes(String reference, String newSource) throws IOException {
+ DefaultInputFile inputFile = mock(DefaultInputFile.class);
+ byte[][] hashes = computeHashes(load(newSource));
+ when(inputFile.lineHashes()).thenReturn(hashes);
+ when(inputFile.key()).thenReturn("foo:Action.java");
+ when(lastSnapshots.getLineHashes("foo:Action.java")).thenReturn(computeHexHashes(load(reference)));
+ sourceHashHolder = new SourceHashHolder(inputFile, lastSnapshots);
+ }
+
+ private byte[][] computeHashes(String source) {
+ String[] lines = source.split("\n");
+ byte[][] hashes = new byte[lines.length][];
+ for (int i = 0; i < lines.length; i++) {
+ hashes[i] = DigestUtils.md5(lines[i].replaceAll("[\t ]", ""));
+ }
+ return hashes;
+ }
+
+ private String[] computeHexHashes(String source) {
+ String[] lines = source.split("\n");
+ String[] hashes = new String[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ hashes[i] = DigestUtils.md5Hex(lines[i].replaceAll("[\t ]", ""));
+ }
+ return hashes;
+ }
}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/SourceHashHolderTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/SourceHashHolderTest.java
index 5687bc449f1..5c9c9c9a5ce 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/SourceHashHolderTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/SourceHashHolderTest.java
@@ -19,64 +19,89 @@
*/
package org.sonar.plugins.core.issue;
-import static org.mockito.Mockito.verify;
-
-import org.mockito.Mockito;
import org.junit.Before;
import org.junit.Test;
-import org.sonar.api.batch.SonarIndex;
-import org.sonar.api.resources.Resource;
-import org.sonar.batch.scan.LastSnapshots;
+import org.mockito.Mockito;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.batch.scan.LastLineHashes;
+
+import static org.apache.commons.codec.digest.DigestUtils.md5;
+import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class SourceHashHolderTest {
SourceHashHolder sourceHashHolder;
- SonarIndex index;
- LastSnapshots lastSnapshots;
- Resource resource;
+ LastLineHashes lastSnapshots;
+ DefaultInputFile file;
@Before
public void setUp() {
- index = mock(SonarIndex.class);
- lastSnapshots = mock(LastSnapshots.class);
- resource = mock(Resource.class);
+ lastSnapshots = mock(LastLineHashes.class);
+ file = mock(DefaultInputFile.class);
- sourceHashHolder = new SourceHashHolder(index, lastSnapshots, resource);
+ sourceHashHolder = new SourceHashHolder(file, lastSnapshots);
}
@Test
- public void should_lazy_load_source() {
+ public void should_lazy_load_line_hashes() {
final String source = "source";
- when(index.getSource(resource)).thenReturn(source);
+ when(file.lineHashes()).thenReturn(new byte[][] {md5(source), null});
- assertThat(sourceHashHolder.getSource()).isEqualTo(source);
- verify(index).getSource(resource);
+ assertThat(sourceHashHolder.getHashedSource().getHash(1)).isEqualTo(md5Hex(source));
+ assertThat(sourceHashHolder.getHashedSource().getHash(2)).isEqualTo("");
+ verify(file).lineHashes();
+ verify(file).key();
+ verify(file).status();
- assertThat(sourceHashHolder.getSource()).isEqualTo(source);
- Mockito.verifyNoMoreInteractions(index);
+ assertThat(sourceHashHolder.getHashedSource().getHash(1)).isEqualTo(md5Hex(source));
+ Mockito.verifyNoMoreInteractions(file);
}
@Test
- public void should_lazy_load_reference_source() {
+ public void should_lazy_load_reference_hashes_when_status_changed() {
final String source = "source";
- when(lastSnapshots.getSource(resource)).thenReturn(source);
+ String key = "foo:src/Foo.java";
+ when(file.lineHashes()).thenReturn(new byte[][] {md5(source)});
+ when(file.key()).thenReturn(key);
+ when(file.status()).thenReturn(InputFile.Status.CHANGED);
+ when(lastSnapshots.getLineHashes(key)).thenReturn(new String[] {md5Hex(source)});
+
+ assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source));
+ verify(lastSnapshots).getLineHashes(key);
- assertThat(sourceHashHolder.getReferenceSource()).isEqualTo(source);
- verify(lastSnapshots).getSource(resource);
+ assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source));
+ Mockito.verifyNoMoreInteractions(lastSnapshots);
+ }
- assertThat(sourceHashHolder.getReferenceSource()).isEqualTo(source);
+ @Test
+ public void should_not_load_reference_hashes_when_status_same() {
+ final String source = "source";
+ String key = "foo:src/Foo.java";
+ when(file.lineHashes()).thenReturn(new byte[][] {md5(source)});
+ when(file.key()).thenReturn(key);
+ when(file.status()).thenReturn(InputFile.Status.SAME);
+
+ assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source));
+ assertThat(sourceHashHolder.getHashedReference().getHash(1)).isEqualTo(md5Hex(source));
Mockito.verifyNoMoreInteractions(lastSnapshots);
}
@Test
- public void should_have_null_reference_source_for_null_resource() {
- sourceHashHolder = new SourceHashHolder(index, lastSnapshots, null);
+ public void no_reference_hashes_when_status_added() {
+ final String source = "source";
+ String key = "foo:src/Foo.java";
+ when(file.lineHashes()).thenReturn(new byte[][] {md5(source)});
+ when(file.key()).thenReturn(key);
+ when(file.status()).thenReturn(InputFile.Status.ADDED);
- assertThat(sourceHashHolder.getReferenceSource()).isNull();
+ assertThat(sourceHashHolder.getHashedReference()).isNull();
Mockito.verifyNoMoreInteractions(lastSnapshots);
}
+
}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizerTest.java
index 871402fe7d4..d48126b7d0c 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizerTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/IssueTrackingBlocksRecognizerTest.java
@@ -20,7 +20,6 @@
package org.sonar.plugins.core.issue.tracking;
import org.junit.Test;
-import org.sonar.plugins.core.issue.tracking.IssueTrackingBlocksRecognizer;
import static org.fest.assertions.Assertions.assertThat;
@@ -28,23 +27,23 @@ public class IssueTrackingBlocksRecognizerTest {
@Test
public void test() {
- assertThat(compute(t("abcde"), t("abcde"), 3, 3)).isEqualTo(5);
- assertThat(compute(t("abcde"), t("abcd"), 3, 3)).isEqualTo(4);
- assertThat(compute(t("bcde"), t("abcde"), 3, 3)).isEqualTo(0);
- assertThat(compute(t("bcde"), t("abcde"), 2, 3)).isEqualTo(4);
+ assertThat(compute(t("abcde"), t("abcde"), 4, 4)).isEqualTo(5);
+ assertThat(compute(t("abcde"), t("abcd"), 4, 4)).isEqualTo(4);
+ assertThat(compute(t("bcde"), t("abcde"), 4, 4)).isEqualTo(0);
+ assertThat(compute(t("bcde"), t("abcde"), 3, 4)).isEqualTo(4);
}
- private static int compute(String a, String b, int ai, int bi) {
+ private static int compute(FileHashes a, FileHashes b, int ai, int bi) {
IssueTrackingBlocksRecognizer rec = new IssueTrackingBlocksRecognizer(a, b);
return rec.computeLengthOfMaximalBlock(ai, bi);
}
- private static String t(String text) {
- StringBuilder sb = new StringBuilder();
+ private static FileHashes t(String text) {
+ String[] array = new String[text.length()];
for (int i = 0; i < text.length(); i++) {
- sb.append(text.charAt(i)).append('\n');
+ array[i] = "" + text.charAt(i);
}
- return sb.toString();
+ return FileHashes.create(array);
}
}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/StringTextComparatorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/RollingFileHashesTest.java
index f0a1d027cab..db1050c761b 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/StringTextComparatorTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/RollingFileHashesTest.java
@@ -20,26 +20,24 @@
package org.sonar.plugins.core.issue.tracking;
import org.junit.Test;
-import org.sonar.plugins.core.issue.tracking.StringText;
-import org.sonar.plugins.core.issue.tracking.StringTextComparator;
+import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
import static org.fest.assertions.Assertions.assertThat;
-
-public class StringTextComparatorTest {
+public class RollingFileHashesTest {
@Test
- public void testEquals() {
- StringTextComparator cmp = StringTextComparator.IGNORE_WHITESPACE;
+ public void test_equals() {
+ RollingFileHashes a = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line0"), md5Hex("line1"), md5Hex("line2")}), 1);
+ RollingFileHashes b = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line0"), md5Hex("line1"), md5Hex("line2"), md5Hex("line3")}), 1);
- StringText a = new StringText("abc\nabc\na bc");
- StringText b = new StringText("abc\nabc d\nab c");
+ assertThat(a.getHash(1) == b.getHash(1)).isTrue();
+ assertThat(a.getHash(2) == b.getHash(2)).isTrue();
+ assertThat(a.getHash(3) == b.getHash(3)).isFalse();
- assertThat(cmp.equals(a, 0, b, 0)).as("abc == abc").isTrue();
- assertThat(cmp.equals(a, 1, b, 1)).as("abc != abc d").isFalse();
- assertThat(cmp.equals(a, 2, b, 2)).as("a bc == ab c").isTrue();
- assertThat(cmp.hash(a, 0)).isEqualTo(cmp.hash(b, 0));
- assertThat(cmp.hash(a, 2)).isEqualTo(cmp.hash(b, 2));
+ RollingFileHashes c = RollingFileHashes.create(FileHashes.create(new String[] {md5Hex("line-1"), md5Hex("line0"), md5Hex("line1"), md5Hex("line2"), md5Hex("line3")}), 1);
+ assertThat(a.getHash(1) == c.getHash(2)).isFalse();
+ assertThat(a.getHash(2) == c.getHash(3)).isTrue();
}
}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/RollingHashSequenceTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/RollingHashSequenceTest.java
deleted file mode 100644
index 6a4b69b6667..00000000000
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/RollingHashSequenceTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.core.issue.tracking;
-
-import org.junit.Test;
-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 static org.fest.assertions.Assertions.assertThat;
-
-public class RollingHashSequenceTest {
-
- @Test
- public void test_hash() {
- StringText seq = new StringText("line0 \n line1 \n line2");
- StringTextComparator cmp = StringTextComparator.IGNORE_WHITESPACE;
- RollingHashSequence<StringText> seq2 = RollingHashSequence.wrap(seq, cmp, 1);
- RollingHashSequenceComparator<StringText> cmp2 = new RollingHashSequenceComparator<StringText>(cmp);
-
- assertThat(seq2.length()).isEqualTo(3);
- assertThat(cmp2.hash(seq2, 0)).isEqualTo(cmp.hash(seq, 0) * 31 + cmp.hash(seq, 1));
- assertThat(cmp2.hash(seq2, 1)).isEqualTo((cmp.hash(seq, 0) * 31 + cmp.hash(seq, 1)) * 31 + cmp.hash(seq, 2));
- assertThat(cmp2.hash(seq2, 2)).isEqualTo((cmp.hash(seq, 1) * 31 + cmp.hash(seq, 2)) * 31);
- }
-
- @Test
- public void test_equals() {
- StringTextComparator baseCmp = StringTextComparator.IGNORE_WHITESPACE;
- RollingHashSequence<StringText> a = RollingHashSequence.wrap(new StringText("line0 \n line1 \n line2"), baseCmp, 1);
- RollingHashSequence<StringText> b = RollingHashSequence.wrap(new StringText("line0 \n line1 \n line2 \n line3"), baseCmp, 1);
- RollingHashSequenceComparator<StringText> cmp = new RollingHashSequenceComparator<StringText>(baseCmp);
-
- assertThat(cmp.equals(a, 0, b, 0)).isTrue();
- assertThat(cmp.equals(a, 1, b, 1)).isTrue();
- assertThat(cmp.equals(a, 2, b, 2)).isFalse();
- }
-
-}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/SourceChecksumTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/SourceChecksumTest.java
deleted file mode 100644
index e81b27798af..00000000000
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/SourceChecksumTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.core.issue.tracking;
-
-import org.junit.Test;
-import org.sonar.plugins.core.issue.tracking.SourceChecksum;
-
-import java.util.List;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class SourceChecksumTest {
- @Test
- public void shouldGetChecksumForLine() {
- List<String> checksums = SourceChecksum.lineChecksumsOfFile("line");
- assertThat(SourceChecksum.getChecksumForLine(checksums, null)).isNull();
- assertThat(SourceChecksum.getChecksumForLine(checksums, 0)).isNull();
- assertThat(SourceChecksum.getChecksumForLine(checksums, 1)).isNotNull();
- assertThat(SourceChecksum.getChecksumForLine(checksums, 2)).isNull();
- }
-
- /**
- * See http://jira.codehaus.org/browse/SONAR-2358
- */
- @Test
- public void shouldGenerateCorrectChecksums() {
- List<String> encoding = SourceChecksum.lineChecksumsOfFile("Привет Мир");
- assertThat(encoding).hasSize(1);
- assertThat(encoding.get(0)).isEqualTo("5ba3a45e1299ede07f56e5531351be52");
- }
-
- @Test
- public void shouldSplitLinesAndIgnoreSpaces() {
- List<String> crlf = SourceChecksum.lineChecksumsOfFile("Hello\r\nWorld");
- List<String> lf = SourceChecksum.lineChecksumsOfFile("Hello\nWorld");
- List<String> cr = SourceChecksum.lineChecksumsOfFile("Hello\rWorld");
- assertThat(crlf).hasSize(2);
- assertThat(crlf.get(0)).isNotEqualTo(crlf.get(1));
- assertThat(lf).isEqualTo(crlf);
- assertThat(cr).isEqualTo(crlf);
-
- assertThat(SourceChecksum.lineChecksum("\tvoid method() {\n")).isEqualTo(SourceChecksum.lineChecksum(" void method() {"));
- }
-}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/StringTextTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/StringTextTest.java
deleted file mode 100644
index 765ee05554d..00000000000
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/tracking/StringTextTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.core.issue.tracking;
-
-import org.junit.Test;
-import org.sonar.plugins.core.issue.tracking.StringText;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class StringTextTest {
-
- @Test
- public void testEmpty() {
- StringText r = new StringText("");
- assertThat(r.length()).isEqualTo(0);
- }
-
- @Test
- public void testTwoLines() {
- StringText r = new StringText("a\nb");
- assertThat(r.length()).isEqualTo(2);
- }
-
-}