aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java13
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java22
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java6
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java81
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java180
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java20
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java132
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java21
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java84
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java28
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java64
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java6
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java123
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java20
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java40
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/RandomFsAccessMediumTest.java18
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java27
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java75
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java12
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java10
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/EmptyFileTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/IncrementalModeMediumTest.java38
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java62
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java106
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java71
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java10
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java4
29 files changed, 526 insertions, 755 deletions
diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java
index bf0cdfcd736..8c68fa7fce5 100644
--- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java
+++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java
@@ -19,7 +19,11 @@
*/
package org.sonar.batch.protocol.output;
+import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import org.sonar.core.util.ContextException;
import org.sonar.core.util.Protobuf;
public class BatchReportWriter {
@@ -67,6 +71,15 @@ public class BatchReportWriter {
return file;
}
+ public void appendComponentIssue(int componentRef, BatchReport.Issue issue) {
+ File file = fileStructure.fileFor(FileStructure.Domain.ISSUES, componentRef);
+ try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file, true))) {
+ issue.writeDelimitedTo(out);
+ } catch (Exception e) {
+ throw ContextException.of("Unable to write issue", e).addContext("file", file);
+ }
+ }
+
public File writeComponentMeasures(int componentRef, Iterable<BatchReport.Measure> measures) {
File file = fileStructure.fileFor(FileStructure.Domain.MEASURES, componentRef);
Protobuf.writeStream(measures, file, false);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java
index 76970b5c80f..c2639e7cfba 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java
@@ -19,7 +19,7 @@
*/
package org.sonar.batch.issue;
-import com.google.common.collect.Lists;
+import java.util.Collections;
import java.util.List;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
@@ -32,13 +32,11 @@ import org.sonar.batch.index.BatchComponent;
*/
public class DefaultIssuable implements Issuable {
- private final IssueCache cache;
private final BatchComponent component;
private final SensorContext sensorContext;
- DefaultIssuable(BatchComponent component, IssueCache cache, SensorContext sensorContext) {
+ DefaultIssuable(BatchComponent component, SensorContext sensorContext) {
this.component = component;
- this.cache = cache;
this.sensorContext = sensorContext;
}
@@ -56,24 +54,12 @@ public class DefaultIssuable implements Issuable {
@Override
public List<Issue> resolvedIssues() {
- List<Issue> result = Lists.newArrayList();
- for (org.sonar.core.issue.DefaultIssue issue : cache.byComponent(component.key())) {
- if (issue.resolution() != null) {
- result.add(issue);
- }
- }
- return result;
+ return Collections.emptyList();
}
@Override
public List<Issue> issues() {
- List<Issue> result = Lists.newArrayList();
- for (org.sonar.core.issue.DefaultIssue issue : cache.byComponent(component.key())) {
- if (issue.resolution() == null) {
- result.add(issue);
- }
- }
- return result;
+ return Collections.emptyList();
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java
index 6425abaca60..62767bd49e2 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java
@@ -31,17 +31,15 @@ import org.sonar.batch.sensor.DefaultSensorContext;
*/
public class IssuableFactory extends PerspectiveBuilder<Issuable> {
- private final IssueCache cache;
private final SensorContext sensorContext;
- public IssuableFactory(IssueCache cache, DefaultSensorContext sensorContext) {
+ public IssuableFactory(DefaultSensorContext sensorContext) {
super(Issuable.class);
- this.cache = cache;
this.sensorContext = sensorContext;
}
@Override
public Issuable loadPerspective(Class<Issuable> perspectiveClass, BatchComponent component) {
- return new DefaultIssuable(component, cache, sensorContext);
+ return new DefaultIssuable(component, sensorContext);
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java
index 70bd4d54d5b..203e495d01a 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java
@@ -20,8 +20,8 @@
package org.sonar.batch.issue;
import org.sonar.api.batch.BatchSide;
+import org.sonar.api.issue.Issue;
import org.sonar.api.issue.batch.IssueFilter;
-import org.sonar.core.issue.DefaultIssue;
@BatchSide
public class IssueFilters {
@@ -46,7 +46,7 @@ public class IssueFilters {
this(new org.sonar.api.issue.IssueFilter[0], new IssueFilter[0]);
}
- public boolean accept(DefaultIssue issue) {
+ public boolean accept(Issue issue) {
if (new DefaultIssueFilterChain(filters).accept(issue)) {
// Apply deprecated rules only if filter chain accepts the current issue
for (org.sonar.api.issue.IssueFilter filter : exclusionFilters) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
index a27b4c272a3..d74fef6dcb0 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
@@ -21,13 +21,23 @@ package org.sonar.batch.issue;
import com.google.common.base.Strings;
import javax.annotation.Nullable;
+import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.Rule;
import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.batch.rule.Severity;
+import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.resources.Project;
import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.KeyValueFormat;
import org.sonar.api.utils.MessageException;
+import org.sonar.batch.index.BatchComponent;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.protocol.Constants;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.report.ReportPublisher;
import org.sonar.core.issue.DefaultIssue;
/**
@@ -37,35 +47,88 @@ public class ModuleIssues {
private final ActiveRules activeRules;
private final Rules rules;
- private final IssueCache cache;
private final Project project;
private final IssueFilters filters;
+ private final ReportPublisher reportPublisher;
+ private final BatchComponentCache componentCache;
+ private final BatchReport.Issue.Builder builder = BatchReport.Issue.newBuilder();
- public ModuleIssues(ActiveRules activeRules, Rules rules, IssueCache cache, Project project, IssueFilters filters) {
+ public ModuleIssues(ActiveRules activeRules, Rules rules, Project project, IssueFilters filters, ReportPublisher reportPublisher, BatchComponentCache componentCache) {
this.activeRules = activeRules;
this.rules = rules;
- this.cache = cache;
this.project = project;
this.filters = filters;
+ this.reportPublisher = reportPublisher;
+ this.componentCache = componentCache;
}
- public boolean initAndAddIssue(DefaultIssue issue) {
- RuleKey ruleKey = issue.ruleKey();
+ public boolean initAndAddIssue(Issue issue) {
+ BatchComponent component;
+ InputPath inputPath = issue.locations().get(0).inputPath();
+ if (inputPath != null) {
+ component = componentCache.get(inputPath);
+ } else {
+ component = componentCache.get(project);
+ }
+ DefaultIssue defaultIssue = toDefaultIssue(project.getKey(), component.key(), issue);
+ RuleKey ruleKey = defaultIssue.ruleKey();
Rule rule = rules.find(ruleKey);
- validateRule(issue, rule);
+ validateRule(defaultIssue, rule);
ActiveRule activeRule = activeRules.find(ruleKey);
if (activeRule == null) {
// rule does not exist or is not enabled -> ignore the issue
return false;
}
- updateIssue(issue, rule, activeRule);
- if (filters.accept(issue)) {
- cache.put(issue);
+ updateIssue(defaultIssue, rule, activeRule);
+ if (filters.accept(defaultIssue)) {
+ write(component, defaultIssue);
return true;
}
return false;
}
+ public void write(BatchComponent component, DefaultIssue issue) {
+ reportPublisher.getWriter().appendComponentIssue(component.batchId(), toReportIssue(builder, issue));
+ }
+
+ private static BatchReport.Issue toReportIssue(BatchReport.Issue.Builder builder, DefaultIssue issue) {
+ builder.clear();
+ // non-null fields
+ builder.setSeverity(Constants.Severity.valueOf(issue.severity()));
+ builder.setRuleRepository(issue.ruleKey().repository());
+ builder.setRuleKey(issue.ruleKey().rule());
+ builder.setAttributes(KeyValueFormat.format(issue.attributes()));
+
+ // nullable fields
+ Integer line = issue.line();
+ if (line != null) {
+ builder.setLine(line);
+ }
+ String message = issue.message();
+ if (message != null) {
+ builder.setMsg(message);
+ }
+ Double effortToFix = issue.effortToFix();
+ if (effortToFix != null) {
+ builder.setEffortToFix(effortToFix);
+ }
+ return builder.build();
+ }
+
+ public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) {
+ Severity overriddenSeverity = issue.overriddenSeverity();
+ TextRange textRange = issue.locations().get(0).textRange();
+ return new org.sonar.core.issue.DefaultIssueBuilder()
+ .componentKey(componentKey)
+ .projectKey(projectKey)
+ .ruleKey(RuleKey.of(issue.ruleKey().repository(), issue.ruleKey().rule()))
+ .effortToFix(issue.effortToFix())
+ .line(textRange != null ? textRange.start().line() : null)
+ .message(issue.locations().get(0).message())
+ .severity(overriddenSeverity != null ? overriddenSeverity.name() : null)
+ .build();
+ }
+
private static void validateRule(DefaultIssue issue, @Nullable Rule rule) {
RuleKey ruleKey = issue.ruleKey();
if (rule == null) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java
index ee089357320..b5a2dbd872b 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java
@@ -27,102 +27,92 @@ import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
-import org.sonar.api.batch.BatchSide;
-import org.sonar.api.batch.InstantiationStrategy;
-import org.sonar.core.issue.DefaultIssue;
-
-import javax.annotation.Nullable;
-
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.protocol.output.BatchReport;
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
@BatchSide
public class IssueTracking {
+ private SourceHashHolder sourceHashHolder;
+
/**
* @param sourceHashHolder Null when working on resource that is not a file (directory/project)
*/
- public IssueTrackingResult track(@Nullable SourceHashHolder sourceHashHolder, Collection<ServerIssue> previousIssues, Collection<DefaultIssue> newIssues) {
+ public IssueTrackingResult track(@Nullable SourceHashHolder sourceHashHolder, Collection<ServerIssue> previousIssues, Collection<BatchReport.Issue> rawIssues) {
+ this.sourceHashHolder = sourceHashHolder;
IssueTrackingResult result = new IssueTrackingResult();
- if (sourceHashHolder != null) {
- setChecksumOnNewIssues(newIssues, sourceHashHolder);
- }
-
// Map new issues with old ones
- mapIssues(newIssues, previousIssues, sourceHashHolder, result);
+ mapIssues(rawIssues, previousIssues, sourceHashHolder, result);
return result;
}
- private void setChecksumOnNewIssues(Collection<DefaultIssue> issues, SourceHashHolder sourceHashHolder) {
- if (issues.isEmpty()) {
- return;
- }
+ private String checksum(BatchReport.Issue rawIssue) {
FileHashes hashedSource = sourceHashHolder.getHashedSource();
- for (DefaultIssue issue : issues) {
- Integer line = issue.line();
- if (line != null) {
- // Extra verification if some plugin managed to create issue on a wrong line
- Preconditions.checkState(line <= hashedSource.length(), "Invalid line number for issue %s. File has only %s line(s)", issue, hashedSource.length());
- issue.setChecksum(hashedSource.getHash(line));
- }
+ if (rawIssue.hasLine()) {
+ // Extra verification if some plugin managed to create issue on a wrong line
+ Preconditions.checkState(rawIssue.getLine() <= hashedSource.length(), "Invalid line number for issue %s. File has only %s line(s)", rawIssue, hashedSource.length());
+ return hashedSource.getHash(rawIssue.getLine());
}
+ return null;
}
@VisibleForTesting
- void mapIssues(Collection<DefaultIssue> newIssues, @Nullable Collection<ServerIssue> previousIssues, @Nullable SourceHashHolder sourceHashHolder, IssueTrackingResult result) {
+ void mapIssues(Collection<BatchReport.Issue> rawIssues, @Nullable Collection<ServerIssue> previousIssues, @Nullable SourceHashHolder sourceHashHolder,
+ IssueTrackingResult result) {
boolean hasLastScan = false;
if (previousIssues != null) {
hasLastScan = true;
- mapLastIssues(newIssues, previousIssues, result);
+ mapLastIssues(rawIssues, previousIssues, result);
}
// If each new issue matches an old one we can stop the matching mechanism
- if (result.matched().size() != newIssues.size()) {
+ if (result.matched().size() != rawIssues.size()) {
if (sourceHashHolder != null && hasLastScan) {
FileHashes hashedReference = sourceHashHolder.getHashedReference();
if (hashedReference != null) {
- mapNewissues(hashedReference, sourceHashHolder.getHashedSource(), newIssues, result);
+ mapNewissues(hashedReference, sourceHashHolder.getHashedSource(), rawIssues, result);
}
}
- mapIssuesOnSameRule(newIssues, result);
+ mapIssuesOnSameRule(rawIssues, result);
}
}
- private void mapLastIssues(Collection<DefaultIssue> newIssues, Collection<ServerIssue> previousIssues, IssueTrackingResult result) {
+ private void mapLastIssues(Collection<BatchReport.Issue> rawIssues, Collection<ServerIssue> previousIssues, IssueTrackingResult result) {
for (ServerIssue lastIssue : previousIssues) {
result.addUnmatched(lastIssue);
}
- // Match the key of the issue. (For manual issues)
- for (DefaultIssue newIssue : newIssues) {
- mapIssue(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).get(newIssue.key()), result);
- }
-
// Try first to match issues on same rule with same line and with same checksum (but not necessarily with same message)
- for (DefaultIssue newIssue : newIssues) {
- if (isNotAlreadyMapped(newIssue, result)) {
+ for (BatchReport.Issue rawIssue : rawIssues) {
+ if (isNotAlreadyMapped(rawIssue, result)) {
mapIssue(
- newIssue,
- findLastIssueWithSameLineAndChecksum(newIssue, result),
+ rawIssue,
+ findLastIssueWithSameLineAndChecksum(rawIssue, result),
result);
}
}
}
- private void mapNewissues(FileHashes hashedReference, FileHashes hashedSource, Collection<DefaultIssue> newIssues, IssueTrackingResult result) {
+ private void mapNewissues(FileHashes hashedReference, FileHashes hashedSource, Collection<BatchReport.Issue> rawIssues, IssueTrackingResult result) {
IssueTrackingBlocksRecognizer rec = new IssueTrackingBlocksRecognizer(hashedReference, hashedSource);
RollingFileHashes a = RollingFileHashes.create(hashedReference, 5);
RollingFileHashes b = RollingFileHashes.create(hashedSource, 5);
- Multimap<Integer, DefaultIssue> newIssuesByLines = newIssuesByLines(newIssues, rec, result);
+ Multimap<Integer, BatchReport.Issue> rawIssuesByLines = rawIssuesByLines(rawIssues, rec, result);
Multimap<Integer, ServerIssue> lastIssuesByLines = lastIssuesByLines(result.unmatched(), rec);
Map<Integer, HashOccurrence> map = Maps.newHashMap();
@@ -141,7 +131,7 @@ public class IssueTracking {
}
}
- for (Integer line : newIssuesByLines.keySet()) {
+ for (Integer line : rawIssuesByLines.keySet()) {
int hash = b.getHash(line);
HashOccurrence hashOccurrence = map.get(hash);
if (hashOccurrence != null) {
@@ -153,17 +143,17 @@ public class IssueTracking {
for (HashOccurrence hashOccurrence : map.values()) {
if (hashOccurrence.countA == 1 && hashOccurrence.countB == 1) {
// Guaranteed that lineA has been moved to lineB, so we can map all issues on lineA to all issues on lineB
- map(newIssuesByLines.get(hashOccurrence.lineB), lastIssuesByLines.get(hashOccurrence.lineA), result);
+ map(rawIssuesByLines.get(hashOccurrence.lineB), lastIssuesByLines.get(hashOccurrence.lineA), result);
lastIssuesByLines.removeAll(hashOccurrence.lineA);
- newIssuesByLines.removeAll(hashOccurrence.lineB);
+ rawIssuesByLines.removeAll(hashOccurrence.lineB);
}
}
// Check if remaining number of lines exceeds threshold
- if (lastIssuesByLines.keySet().size() * newIssuesByLines.keySet().size() < 250000) {
+ if (lastIssuesByLines.keySet().size() * rawIssuesByLines.keySet().size() < 250000) {
List<LinePair> possibleLinePairs = Lists.newArrayList();
for (Integer oldLine : lastIssuesByLines.keySet()) {
- for (Integer newLine : newIssuesByLines.keySet()) {
+ for (Integer newLine : rawIssuesByLines.keySet()) {
int weight = rec.computeLengthOfMaximalBlock(oldLine, newLine);
possibleLinePairs.add(new LinePair(oldLine, newLine, weight));
}
@@ -171,50 +161,54 @@ public class IssueTracking {
Collections.sort(possibleLinePairs, LINE_PAIR_COMPARATOR);
for (LinePair linePair : possibleLinePairs) {
// High probability that lineA has been moved to lineB, so we can map all Issues on lineA to all Issues on lineB
- map(newIssuesByLines.get(linePair.lineB), lastIssuesByLines.get(linePair.lineA), result);
+ map(rawIssuesByLines.get(linePair.lineB), lastIssuesByLines.get(linePair.lineA), result);
}
}
}
- private void mapIssuesOnSameRule(Collection<DefaultIssue> newIssues, IssueTrackingResult result) {
+ private void mapIssuesOnSameRule(Collection<BatchReport.Issue> rawIssues, IssueTrackingResult result) {
// Try then to match issues on same rule with same message and with same checksum
- for (DefaultIssue newIssue : newIssues) {
- if (isNotAlreadyMapped(newIssue, result)) {
+ for (BatchReport.Issue rawIssue : rawIssues) {
+ if (isNotAlreadyMapped(rawIssue, result)) {
mapIssue(
- newIssue,
- findLastIssueWithSameChecksumAndMessage(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).values()),
+ rawIssue,
+ findLastIssueWithSameChecksumAndMessage(rawIssue, result.unmatchedByKeyForRule(ruleKey(rawIssue)).values()),
result);
}
}
// Try then to match issues on same rule with same line and with same message
- for (DefaultIssue newIssue : newIssues) {
- if (isNotAlreadyMapped(newIssue, result)) {
+ for (BatchReport.Issue rawIssue : rawIssues) {
+ if (isNotAlreadyMapped(rawIssue, result)) {
mapIssue(
- newIssue,
- findLastIssueWithSameLineAndMessage(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).values()),
+ rawIssue,
+ findLastIssueWithSameLineAndMessage(rawIssue, result.unmatchedByKeyForRule(ruleKey(rawIssue)).values()),
result);
}
}
// Last check: match issue if same rule and same checksum but different line and different message
// See SONAR-2812
- for (DefaultIssue newIssue : newIssues) {
- if (isNotAlreadyMapped(newIssue, result)) {
+ for (BatchReport.Issue rawIssue : rawIssues) {
+ if (isNotAlreadyMapped(rawIssue, result)) {
mapIssue(
- newIssue,
- findLastIssueWithSameChecksum(newIssue, result.unmatchedByKeyForRule(newIssue.ruleKey()).values()),
+ rawIssue,
+ findLastIssueWithSameChecksum(rawIssue, result.unmatchedByKeyForRule(ruleKey(rawIssue)).values()),
result);
}
}
}
- private void map(Collection<DefaultIssue> newIssues, Collection<ServerIssue> previousIssues, IssueTrackingResult result) {
- for (DefaultIssue newIssue : newIssues) {
- if (isNotAlreadyMapped(newIssue, result)) {
+ private RuleKey ruleKey(BatchReport.Issue rawIssue) {
+ return RuleKey.of(rawIssue.getRuleRepository(), rawIssue.getRuleKey());
+ }
+
+ private void map(Collection<BatchReport.Issue> rawIssues, Collection<ServerIssue> previousIssues, IssueTrackingResult result) {
+ for (BatchReport.Issue rawIssue : rawIssues) {
+ if (isNotAlreadyMapped(rawIssue, result)) {
for (ServerIssue previousIssue : previousIssues) {
- if (isNotAlreadyMapped(previousIssue, result) && Objects.equal(newIssue.ruleKey(), previousIssue.ruleKey())) {
- mapIssue(newIssue, previousIssue, result);
+ if (isNotAlreadyMapped(previousIssue, result) && Objects.equal(ruleKey(rawIssue), previousIssue.ruleKey())) {
+ mapIssue(rawIssue, previousIssue, result);
break;
}
}
@@ -222,14 +216,14 @@ public class IssueTracking {
}
}
- private Multimap<Integer, DefaultIssue> newIssuesByLines(Collection<DefaultIssue> newIssues, IssueTrackingBlocksRecognizer rec, IssueTrackingResult result) {
- Multimap<Integer, DefaultIssue> newIssuesByLines = LinkedHashMultimap.create();
- for (DefaultIssue newIssue : newIssues) {
- if (isNotAlreadyMapped(newIssue, result) && rec.isValidLineInSource(newIssue.line())) {
- newIssuesByLines.put(newIssue.line(), newIssue);
+ private Multimap<Integer, BatchReport.Issue> rawIssuesByLines(Collection<BatchReport.Issue> rawIssues, IssueTrackingBlocksRecognizer rec, IssueTrackingResult result) {
+ Multimap<Integer, BatchReport.Issue> rawIssuesByLines = LinkedHashMultimap.create();
+ for (BatchReport.Issue rawIssue : rawIssues) {
+ if (isNotAlreadyMapped(rawIssue, result) && rawIssue.hasLine() && rec.isValidLineInSource(rawIssue.getLine())) {
+ rawIssuesByLines.put(rawIssue.getLine(), rawIssue);
}
}
- return newIssuesByLines;
+ return rawIssuesByLines;
}
private Multimap<Integer, ServerIssue> lastIssuesByLines(Collection<ServerIssue> previousIssues, IssueTrackingBlocksRecognizer rec) {
@@ -242,64 +236,74 @@ public class IssueTracking {
return previousIssuesByLines;
}
- private ServerIssue findLastIssueWithSameChecksum(DefaultIssue newIssue, Collection<ServerIssue> previousIssues) {
+ private ServerIssue findLastIssueWithSameChecksum(BatchReport.Issue rawIssue, Collection<ServerIssue> previousIssues) {
for (ServerIssue previousIssue : previousIssues) {
- if (isSameChecksum(newIssue, previousIssue)) {
+ if (isSameChecksum(rawIssue, previousIssue)) {
return previousIssue;
}
}
return null;
}
- private ServerIssue findLastIssueWithSameLineAndMessage(DefaultIssue newIssue, Collection<ServerIssue> previousIssues) {
+ private ServerIssue findLastIssueWithSameLineAndMessage(BatchReport.Issue rawIssue, Collection<ServerIssue> previousIssues) {
for (ServerIssue previousIssue : previousIssues) {
- if (isSameLine(newIssue, previousIssue) && isSameMessage(newIssue, previousIssue)) {
+ if (isSameLine(rawIssue, previousIssue) && isSameMessage(rawIssue, previousIssue)) {
return previousIssue;
}
}
return null;
}
- private ServerIssue findLastIssueWithSameChecksumAndMessage(DefaultIssue newIssue, Collection<ServerIssue> previousIssues) {
+ private ServerIssue findLastIssueWithSameChecksumAndMessage(BatchReport.Issue rawIssue, Collection<ServerIssue> previousIssues) {
for (ServerIssue previousIssue : previousIssues) {
- if (isSameChecksum(newIssue, previousIssue) && isSameMessage(newIssue, previousIssue)) {
+ if (isSameChecksum(rawIssue, previousIssue) && isSameMessage(rawIssue, previousIssue)) {
return previousIssue;
}
}
return null;
}
- private ServerIssue findLastIssueWithSameLineAndChecksum(DefaultIssue newIssue, IssueTrackingResult result) {
- Collection<ServerIssue> sameRuleAndSameLineAndSameChecksum = result.unmatchedForRuleAndForLineAndForChecksum(newIssue.ruleKey(), newIssue.line(), newIssue.checksum());
+ private ServerIssue findLastIssueWithSameLineAndChecksum(BatchReport.Issue rawIssue, IssueTrackingResult result) {
+ Collection<ServerIssue> sameRuleAndSameLineAndSameChecksum = result.unmatchedForRuleAndForLineAndForChecksum(ruleKey(rawIssue), line(rawIssue), checksum(rawIssue));
if (!sameRuleAndSameLineAndSameChecksum.isEmpty()) {
return sameRuleAndSameLineAndSameChecksum.iterator().next();
}
return null;
}
+ @CheckForNull
+ private Integer line(BatchReport.Issue rawIssue) {
+ return rawIssue.hasLine() ? rawIssue.getLine() : null;
+ }
+
private boolean isNotAlreadyMapped(ServerIssue previousIssue, IssueTrackingResult result) {
return result.unmatched().contains(previousIssue);
}
- private boolean isNotAlreadyMapped(DefaultIssue newIssue, IssueTrackingResult result) {
- return !result.isMatched(newIssue);
+ private boolean isNotAlreadyMapped(BatchReport.Issue rawIssue, IssueTrackingResult result) {
+ return !result.isMatched(rawIssue);
+ }
+
+ private boolean isSameChecksum(BatchReport.Issue rawIssue, ServerIssue previousIssue) {
+ return Objects.equal(previousIssue.checksum(), checksum(rawIssue));
}
- private boolean isSameChecksum(DefaultIssue newIssue, ServerIssue previousIssue) {
- return Objects.equal(previousIssue.checksum(), newIssue.checksum());
+ private boolean isSameLine(BatchReport.Issue rawIssue, ServerIssue previousIssue) {
+ return Objects.equal(previousIssue.line(), line(rawIssue));
}
- private boolean isSameLine(DefaultIssue newIssue, ServerIssue previousIssue) {
- return Objects.equal(previousIssue.line(), newIssue.line());
+ private boolean isSameMessage(BatchReport.Issue rawIssue, ServerIssue previousIssue) {
+ return Objects.equal(message(rawIssue), previousIssue.message());
}
- private boolean isSameMessage(DefaultIssue newIssue, ServerIssue previousIssue) {
- return Objects.equal(newIssue.message(), previousIssue.message());
+ @CheckForNull
+ private String message(BatchReport.Issue rawIssue) {
+ return rawIssue.hasMsg() ? rawIssue.getMsg() : null;
}
- private void mapIssue(DefaultIssue issue, @Nullable ServerIssue ref, IssueTrackingResult result) {
+ private void mapIssue(BatchReport.Issue rawIssue, @Nullable ServerIssue ref, IssueTrackingResult result) {
if (ref != null) {
- result.setMatch(issue, ref);
+ result.setMatch(rawIssue, ref);
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java
index e04cdc240b4..e55ef46b03f 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java
@@ -22,22 +22,20 @@ package org.sonar.batch.issue.tracking;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.api.rule.RuleKey;
-
-import javax.annotation.Nullable;
-
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.protocol.output.BatchReport;
class IssueTrackingResult {
private final Map<String, ServerIssue> unmatchedByKey = new HashMap<>();
private final Map<RuleKey, Map<String, ServerIssue>> unmatchedByRuleAndKey = new HashMap<>();
private final Map<RuleKey, Map<Integer, Multimap<String, ServerIssue>>> unmatchedByRuleAndLineAndChecksum = new HashMap<>();
- private final Map<DefaultIssue, ServerIssue> matched = Maps.newIdentityHashMap();
+ private final Map<BatchReport.Issue, ServerIssue> matched = Maps.newIdentityHashMap();
Collection<ServerIssue> unmatched() {
return unmatchedByKey.values();
@@ -64,15 +62,15 @@ class IssueTrackingResult {
return unmatchedForRuleAndLine.get(checksumNotNull);
}
- Collection<DefaultIssue> matched() {
+ Collection<BatchReport.Issue> matched() {
return matched.keySet();
}
- boolean isMatched(DefaultIssue issue) {
+ boolean isMatched(BatchReport.Issue issue) {
return matched.containsKey(issue);
}
- ServerIssue matching(DefaultIssue issue) {
+ ServerIssue matching(BatchReport.Issue issue) {
return matched.get(issue);
}
@@ -99,7 +97,7 @@ class IssueTrackingResult {
return line != null ? line : 0;
}
- void setMatch(DefaultIssue issue, ServerIssue matching) {
+ void setMatch(BatchReport.Issue issue, ServerIssue matching) {
matched.put(issue, matching);
RuleKey ruleKey = matching.ruleKey();
unmatchedByRuleAndKey.get(ruleKey).remove(matching.key());
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java
index 729e4d7cff1..ee316017c21 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java
@@ -21,33 +21,40 @@ package org.sonar.batch.issue.tracking;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.CheckForNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.BatchSide;
import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.BatchSide;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.issue.Issue;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.IssueChangeContext;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.ResourceUtils;
import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.KeyValueFormat;
import org.sonar.batch.index.BatchComponent;
import org.sonar.batch.index.BatchComponentCache;
import org.sonar.batch.issue.IssueCache;
import org.sonar.batch.protocol.input.ProjectRepositories;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.protocol.output.BatchReportReader;
+import org.sonar.batch.report.ReportPublisher;
import org.sonar.batch.scan.filesystem.InputPathCache;
import org.sonar.core.component.ComponentKeys;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.core.issue.workflow.IssueWorkflow;
-
-import javax.annotation.CheckForNull;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
+import org.sonar.core.util.CloseableIterator;
@BatchSide
public class LocalIssueTracking {
@@ -62,16 +69,18 @@ public class LocalIssueTracking {
private final IssueChangeContext changeContext;
private final ActiveRules activeRules;
private final InputPathCache inputPathCache;
- private final BatchComponentCache resourceCache;
+ private final BatchComponentCache componentCache;
private final ServerIssueRepository serverIssueRepository;
private final ProjectRepositories projectRepositories;
private final AnalysisMode analysisMode;
+ private final ReportPublisher reportPublisher;
+ private final Date analysisDate;
public LocalIssueTracking(BatchComponentCache resourceCache, IssueCache issueCache, IssueTracking tracking,
ServerLineHashesLoader lastLineHashes, IssueWorkflow workflow, IssueUpdater updater,
ActiveRules activeRules, InputPathCache inputPathCache, ServerIssueRepository serverIssueRepository,
- ProjectRepositories projectRepositories, AnalysisMode analysisMode) {
- this.resourceCache = resourceCache;
+ ProjectRepositories projectRepositories, AnalysisMode analysisMode, ReportPublisher reportPublisher) {
+ this.componentCache = resourceCache;
this.issueCache = issueCache;
this.tracking = tracking;
this.lastLineHashes = lastLineHashes;
@@ -81,7 +90,9 @@ public class LocalIssueTracking {
this.serverIssueRepository = serverIssueRepository;
this.projectRepositories = projectRepositories;
this.analysisMode = analysisMode;
- this.changeContext = IssueChangeContext.createScan(((Project) resourceCache.getRoot().resource()).getAnalysisDate());
+ this.reportPublisher = reportPublisher;
+ this.analysisDate = ((Project) resourceCache.getRoot().resource()).getAnalysisDate();
+ this.changeContext = IssueChangeContext.createScan(analysisDate);
this.activeRules = activeRules;
}
@@ -92,49 +103,82 @@ public class LocalIssueTracking {
}
serverIssueRepository.load();
+ BatchReportReader reader = new BatchReportReader(reportPublisher.getReportDir());
- for (BatchComponent component : resourceCache.all()) {
- trackIssues(component);
+ for (BatchComponent component : componentCache.all()) {
+ trackIssues(reader, component);
}
}
- public void trackIssues(BatchComponent component) {
-
- Collection<DefaultIssue> issues = Lists.newArrayList();
- for (Issue issue : issueCache.byComponent(component.resource().getEffectiveKey())) {
- issues.add((DefaultIssue) issue);
- }
- issueCache.clear(component.resource().getEffectiveKey());
- // issues = all the issues created by rule engines during this module scan and not excluded by filters
+ public void trackIssues(BatchReportReader reader, BatchComponent component) {
if (analysisMode.isIncremental() && !component.isFile()) {
// No need to report issues on project or directories in preview mode since it is likely to be wrong anyway
return;
}
+ // raw issues = all the issues created by rule engines during this module scan and not excluded by filters
+ Set<BatchReport.Issue> rawIssues = Sets.newIdentityHashSet();
+ try (CloseableIterator<BatchReport.Issue> it = reader.readComponentIssues(component.batchId())) {
+ while (it.hasNext()) {
+ rawIssues.add(it.next());
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("Can't read issues for " + component.key(), e);
+ }
+
// all the issues that are not closed in db before starting this module scan, including manual issues
Collection<ServerIssue> serverIssues = loadServerIssues(component);
SourceHashHolder sourceHashHolder = loadSourceHashes(component);
- IssueTrackingResult trackingResult = tracking.track(sourceHashHolder, serverIssues, issues);
+ IssueTrackingResult trackingResult = tracking.track(sourceHashHolder, serverIssues, rawIssues);
- // unmatched = issues that have been resolved + issues on disabled/removed rules + manual issues
- addUnmatched(trackingResult.unmatched(), sourceHashHolder, issues);
+ List<DefaultIssue> trackedIssues = Lists.newArrayList();
+ // unmatched from server = issues that have been resolved + issues on disabled/removed rules + manual issues
+ addUnmatchedFromServer(trackingResult.unmatched(), sourceHashHolder, trackedIssues);
- mergeMatched(trackingResult);
+ mergeMatched(component, trackingResult, trackedIssues, rawIssues);
+
+ // Unmatched raw issues = new issues
+ addUnmatchedRawIssues(component, rawIssues, trackedIssues);
if (ResourceUtils.isRootProject(component.resource())) {
// issues that relate to deleted components
- addIssuesOnDeletedComponents(issues);
+ addIssuesOnDeletedComponents(trackedIssues);
}
- for (DefaultIssue issue : issues) {
+ for (DefaultIssue issue : trackedIssues) {
workflow.doAutomaticTransition(issue, changeContext);
issueCache.put(issue);
}
}
+ private void addUnmatchedRawIssues(BatchComponent component, Set<org.sonar.batch.protocol.output.BatchReport.Issue> rawIssues, List<DefaultIssue> trackedIssues) {
+ for (BatchReport.Issue rawIssue : rawIssues) {
+
+ DefaultIssue tracked = toTracked(component, rawIssue);
+ tracked.setNew(true);
+ tracked.setCreationDate(analysisDate);
+
+ trackedIssues.add(tracked);
+ }
+ }
+
+ private DefaultIssue toTracked(BatchComponent component, BatchReport.Issue rawIssue) {
+ DefaultIssue trackedIssue = new org.sonar.core.issue.DefaultIssueBuilder()
+ .componentKey(component.key())
+ .projectKey("unused")
+ .ruleKey(RuleKey.of(rawIssue.getRuleRepository(), rawIssue.getRuleKey()))
+ .effortToFix(rawIssue.hasEffortToFix() ? rawIssue.getEffortToFix() : null)
+ .line(rawIssue.hasLine() ? rawIssue.getLine() : null)
+ .message(rawIssue.hasMsg() ? rawIssue.getMsg() : null)
+ .severity(rawIssue.getSeverity().name())
+ .build();
+ trackedIssue.setAttributes(rawIssue.hasAttributes() ? KeyValueFormat.parse(rawIssue.getAttributes()) : Collections.<String, String>emptyMap());
+ return trackedIssue;
+ }
+
@CheckForNull
private SourceHashHolder loadSourceHashes(BatchComponent component) {
SourceHashHolder sourceHashHolder = null;
@@ -157,32 +201,36 @@ public class LocalIssueTracking {
}
@VisibleForTesting
- protected void mergeMatched(IssueTrackingResult result) {
- for (DefaultIssue issue : result.matched()) {
- org.sonar.batch.protocol.input.BatchInput.ServerIssue ref = ((ServerIssueFromWs) result.matching(issue)).getDto();
+ protected void mergeMatched(BatchComponent component, IssueTrackingResult result, List<DefaultIssue> trackedIssues, Collection<BatchReport.Issue> rawIssues) {
+ for (BatchReport.Issue rawIssue : result.matched()) {
+ rawIssues.remove(rawIssue);
+ org.sonar.batch.protocol.input.BatchInput.ServerIssue ref = ((ServerIssueFromWs) result.matching(rawIssue)).getDto();
+
+ DefaultIssue tracked = toTracked(component, rawIssue);
// invariant fields
- issue.setKey(ref.getKey());
+ tracked.setKey(ref.getKey());
// non-persisted fields
- issue.setNew(false);
- issue.setBeingClosed(false);
- issue.setOnDisabledRule(false);
+ tracked.setNew(false);
+ tracked.setBeingClosed(false);
+ tracked.setOnDisabledRule(false);
// fields to update with old values
- issue.setResolution(ref.hasResolution() ? ref.getResolution() : null);
- issue.setStatus(ref.getStatus());
- issue.setAssignee(ref.hasAssigneeLogin() ? ref.getAssigneeLogin() : null);
- issue.setCreationDate(new Date(ref.getCreationDate()));
+ tracked.setResolution(ref.hasResolution() ? ref.getResolution() : null);
+ tracked.setStatus(ref.getStatus());
+ tracked.setAssignee(ref.hasAssigneeLogin() ? ref.getAssigneeLogin() : null);
+ tracked.setCreationDate(new Date(ref.getCreationDate()));
if (ref.getManualSeverity()) {
// Severity overriden by user
- issue.setSeverity(ref.getSeverity().name());
+ tracked.setSeverity(ref.getSeverity().name());
}
+ trackedIssues.add(tracked);
}
}
- private void addUnmatched(Collection<ServerIssue> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<DefaultIssue> issues) {
+ private void addUnmatchedFromServer(Collection<ServerIssue> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<DefaultIssue> issues) {
for (ServerIssue unmatchedIssue : unmatchedIssues) {
org.sonar.batch.protocol.input.BatchInput.ServerIssue unmatchedPreviousIssue = ((ServerIssueFromWs) unmatchedIssue).getDto();
DefaultIssue unmatched = toUnmatchedIssue(unmatchedPreviousIssue);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
index 10341e8dcbe..1b02c9090d5 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
@@ -36,8 +36,10 @@ import org.slf4j.LoggerFactory;
import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputPath;
import org.sonar.api.batch.fs.TextPointer;
import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.sensor.duplication.Duplication;
import org.sonar.api.batch.sensor.highlighting.TypeOfText;
@@ -121,10 +123,27 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
}
}
- public List<Issue> issues() {
+ public List<Issue> trackedIssues() {
return issues;
}
+ public List<BatchReport.Issue> issuesFor(InputPath inputPath) {
+ List<BatchReport.Issue> result = Lists.newArrayList();
+ int ref = reportComponents.get(key(inputPath)).getRef();
+ try (CloseableIterator<BatchReport.Issue> it = reader.readComponentIssues(ref)) {
+ while (it.hasNext()) {
+ result.add(it.next());
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("Can't read issues for " + inputPath.absolutePath(), e);
+ }
+ return result;
+ }
+
+ private String key(InputPath inputPath) {
+ return inputPath instanceof InputFile ? ((DefaultInputFile) inputPath).key() : ((DefaultInputDir) inputPath).key();
+ }
+
public Collection<InputFile> inputFiles() {
return inputFiles.values();
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java
deleted file mode 100644
index 24e1a95abc7..00000000000
--- a/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java
+++ /dev/null
@@ -1,84 +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.batch.report;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
-import org.sonar.api.utils.KeyValueFormat;
-import org.sonar.batch.index.BatchComponent;
-import org.sonar.batch.index.BatchComponentCache;
-import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.protocol.Constants;
-import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.batch.protocol.output.BatchReportWriter;
-import org.sonar.core.issue.DefaultIssue;
-
-public class IssuesPublisher implements ReportPublisherStep {
-
- private final BatchComponentCache componentCache;
- private final IssueCache issueCache;
-
- public IssuesPublisher(BatchComponentCache componentCache, IssueCache issueCache) {
- this.componentCache = componentCache;
- this.issueCache = issueCache;
-
- }
-
- @Override
- public void publish(BatchReportWriter writer) {
- for (BatchComponent resource : componentCache.all()) {
- String componentKey = resource.resource().getEffectiveKey();
- Iterable<DefaultIssue> issues = issueCache.byComponent(componentKey);
- writer.writeComponentIssues(resource.batchId(), Iterables.transform(issues, new Function<DefaultIssue, BatchReport.Issue>() {
- private BatchReport.Issue.Builder builder = BatchReport.Issue.newBuilder();
-
- @Override
- public BatchReport.Issue apply(DefaultIssue input) {
- return toReportIssue(builder, input);
- }
- }));
- }
- }
-
- private BatchReport.Issue toReportIssue(BatchReport.Issue.Builder builder, DefaultIssue issue) {
- builder.clear();
- // non-null fields
- builder.setSeverity(Constants.Severity.valueOf(issue.severity()));
- builder.setRuleRepository(issue.ruleKey().repository());
- builder.setRuleKey(issue.ruleKey().rule());
- builder.setAttributes(KeyValueFormat.format(issue.attributes()));
-
- // nullable fields
- Integer line = issue.line();
- if (line != null) {
- builder.setLine(line);
- }
- String message = issue.message();
- if (message != null) {
- builder.setMsg(message);
- }
- Double effortToFix = issue.effortToFix();
- if (effortToFix != null) {
- builder.setEffortToFix(effortToFix);
- }
- return builder.build();
- }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
index ea086f5a068..28367ee042c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
@@ -59,7 +59,6 @@ import org.sonar.batch.report.ActiveRulesPublisher;
import org.sonar.batch.report.ComponentsPublisher;
import org.sonar.batch.report.CoveragePublisher;
import org.sonar.batch.report.DuplicationsPublisher;
-import org.sonar.batch.report.IssuesPublisher;
import org.sonar.batch.report.MeasuresPublisher;
import org.sonar.batch.report.MetadataPublisher;
import org.sonar.batch.report.ReportPublisher;
@@ -189,7 +188,6 @@ public class ProjectScanContainer extends ComponentContainer {
MetadataPublisher.class,
ActiveRulesPublisher.class,
ComponentsPublisher.class,
- IssuesPublisher.class,
MeasuresPublisher.class,
DuplicationsPublisher.class,
CoveragePublisher.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java b/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
index 1ab8482079a..adacbb99546 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
@@ -30,12 +30,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputPath;
import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.measure.MetricFinder;
import org.sonar.api.batch.rule.ActiveRules;
-import org.sonar.api.batch.rule.Severity;
import org.sonar.api.batch.sensor.coverage.CoverageType;
import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
import org.sonar.api.batch.sensor.duplication.Duplication;
@@ -52,7 +50,6 @@ import org.sonar.api.measures.Metric;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
-import org.sonar.api.rule.RuleKey;
import org.sonar.api.source.Symbol;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.api.utils.SonarException;
@@ -67,8 +64,6 @@ import org.sonar.batch.report.ReportPublisher;
import org.sonar.batch.scan.measure.MeasureCache;
import org.sonar.batch.sensor.coverage.CoverageExclusions;
import org.sonar.batch.source.DefaultSymbol;
-import org.sonar.core.component.ComponentKeys;
-import org.sonar.core.issue.DefaultIssue;
public class DefaultSensorStorage implements SensorStorage {
@@ -194,28 +189,7 @@ public class DefaultSensorStorage implements SensorStorage {
@Override
public void store(Issue issue) {
- String componentKey;
- InputPath inputPath = issue.locations().get(0).inputPath();
- if (inputPath != null) {
- componentKey = ComponentKeys.createEffectiveKey(project.getKey(), inputPath);
- } else {
- componentKey = project.getKey();
- }
- moduleIssues.initAndAddIssue(toDefaultIssue(project.getKey(), componentKey, issue));
- }
-
- public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) {
- Severity overriddenSeverity = issue.overriddenSeverity();
- TextRange textRange = issue.locations().get(0).textRange();
- return new org.sonar.core.issue.DefaultIssueBuilder()
- .componentKey(componentKey)
- .projectKey(projectKey)
- .ruleKey(RuleKey.of(issue.ruleKey().repository(), issue.ruleKey().rule()))
- .effortToFix(issue.effortToFix())
- .line(textRange != null ? textRange.start().line() : null)
- .message(issue.locations().get(0).message())
- .severity(overriddenSeverity != null ? overriddenSeverity.name() : null)
- .build();
+ moduleIssues.initAndAddIssue(issue);
}
private File getFile(InputFile file) {
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java
deleted file mode 100644
index 5df84297370..00000000000
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.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.batch.issue;
-
-import java.util.Arrays;
-import java.util.List;
-import org.junit.Test;
-import org.sonar.api.batch.sensor.SensorContext;
-import org.sonar.api.issue.Issue;
-import org.sonar.batch.index.BatchComponent;
-import org.sonar.core.issue.DefaultIssue;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class DefaultIssuableTest {
-
- IssueCache cache = mock(IssueCache.class);
- BatchComponent component = mock(BatchComponent.class);
-
- @Test
- public void test_unresolved_issues() throws Exception {
- when(component.key()).thenReturn("struts:org.apache.Action");
- DefaultIssue resolved = new DefaultIssue().setResolution(Issue.RESOLUTION_FALSE_POSITIVE);
- DefaultIssue unresolved = new DefaultIssue();
- when(cache.byComponent("struts:org.apache.Action")).thenReturn(Arrays.asList(resolved, unresolved));
-
- DefaultIssuable perspective = new DefaultIssuable(component, cache, mock(SensorContext.class));
-
- List<Issue> issues = perspective.issues();
- assertThat(issues).containsOnly(unresolved);
- }
-
- @Test
- public void test_resolved_issues() throws Exception {
- when(component.key()).thenReturn("struts:org.apache.Action");
- DefaultIssue resolved = new DefaultIssue().setResolution(Issue.RESOLUTION_FALSE_POSITIVE);
- DefaultIssue unresolved = new DefaultIssue();
- when(cache.byComponent("struts:org.apache.Action")).thenReturn(Arrays.asList(resolved, unresolved));
-
- DefaultIssuable perspective = new DefaultIssuable(component, cache, mock(SensorContext.class));
-
- List<Issue> issues = perspective.resolvedIssues();
- assertThat(issues).containsOnly(resolved);
- }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java
index ddabedba240..5525d4ea049 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java
@@ -20,7 +20,6 @@
package org.sonar.batch.issue;
import org.junit.Test;
-import org.mockito.Mockito;
import org.sonar.api.issue.Issuable;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
@@ -34,12 +33,11 @@ import static org.mockito.Mockito.mock;
public class IssuableFactoryTest {
ModuleIssues moduleIssues = mock(ModuleIssues.class);
- IssueCache cache = mock(IssueCache.class, Mockito.RETURNS_MOCKS);
DefaultProjectTree projectTree = mock(DefaultProjectTree.class);
@Test
public void file_should_be_issuable() {
- IssuableFactory factory = new IssuableFactory(cache, mock(DefaultSensorContext.class));
+ IssuableFactory factory = new IssuableFactory(mock(DefaultSensorContext.class));
BatchComponent component = new BatchComponent(1, File.create("foo/bar.c").setEffectiveKey("foo/bar.c"), null);
Issuable issuable = factory.loadPerspective(Issuable.class, component);
@@ -49,7 +47,7 @@ public class IssuableFactoryTest {
@Test
public void project_should_be_issuable() {
- IssuableFactory factory = new IssuableFactory(cache, mock(DefaultSensorContext.class));
+ IssuableFactory factory = new IssuableFactory(mock(DefaultSensorContext.class));
BatchComponent component = new BatchComponent(1, new Project("Foo").setEffectiveKey("foo"), null);
Issuable issuable = factory.loadPerspective(Issuable.class, component);
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
index a602f1f6549..fb9cf921e2f 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
@@ -19,25 +19,36 @@
*/
package org.sonar.batch.issue;
-import java.util.Calendar;
+import java.io.StringReader;
import java.util.Date;
-import org.apache.commons.lang.time.DateUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.FileMetadata;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.RulesBuilder;
+import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
+import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
+import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.utils.MessageException;
-import org.sonar.core.issue.DefaultIssue;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.report.ReportPublisher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -48,11 +59,7 @@ public class ModuleIssuesTest {
static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle");
static final String SQUID_RULE_NAME = "Avoid Cycle";
- @Mock
- IssueCache cache;
-
- @Mock
- Project project;
+ Project project = new Project("foo").setAnalysisDate(new Date());
@Mock
IssueFilters filters;
@@ -62,18 +69,21 @@ public class ModuleIssuesTest {
ModuleIssues moduleIssues;
+ BatchComponentCache componentCache = new BatchComponentCache();
+ InputFile file = new DefaultInputFile("foo", "src/Foo.php").initMetadata(new FileMetadata().readMetadata(new StringReader("Foo\nBar\nBiz\n")));
+ ReportPublisher reportPublisher = mock(ReportPublisher.class, RETURNS_DEEP_STUBS);
+
@Before
- public void setUp() {
- when(project.getAnalysisDate()).thenReturn(new Date());
- when(project.getEffectiveKey()).thenReturn("org.apache:struts-core");
- when(project.getRoot()).thenReturn(project);
+ public void prepare() {
+ componentCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputPath(file);
}
@Test
public void fail_on_unknown_rule() {
initModuleIssues();
- DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY);
-
+ DefaultIssue issue = new DefaultIssue()
+ .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY);
try {
moduleIssues.initAndAddIssue(issue);
fail();
@@ -81,15 +91,16 @@ public class ModuleIssuesTest {
assertThat(e).isInstanceOf(MessageException.class);
}
- verifyZeroInteractions(cache);
+ verifyZeroInteractions(reportPublisher);
}
@Test
public void fail_if_rule_has_no_name_and_issue_has_no_message() {
ruleBuilder.add(SQUID_RULE_KEY).setInternalKey(SQUID_RULE_KEY.rule());
initModuleIssues();
- DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY).setMessage("");
-
+ DefaultIssue issue = new DefaultIssue()
+ .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message(""))
+ .forRule(SQUID_RULE_KEY);
try {
moduleIssues.initAndAddIssue(issue);
fail();
@@ -97,19 +108,20 @@ public class ModuleIssuesTest {
assertThat(e).isInstanceOf(MessageException.class);
}
- verifyZeroInteractions(cache);
+ verifyZeroInteractions(reportPublisher);
}
@Test
public void ignore_null_active_rule() {
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
initModuleIssues();
-
- DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY);
+ DefaultIssue issue = new DefaultIssue()
+ .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY);
boolean added = moduleIssues.initAndAddIssue(issue);
assertThat(added).isFalse();
- verifyZeroInteractions(cache);
+ verifyZeroInteractions(reportPublisher);
}
@Test
@@ -118,11 +130,13 @@ public class ModuleIssuesTest {
activeRulesBuilder.create(SQUID_RULE_KEY).activate();
initModuleIssues();
- DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY);
+ DefaultIssue issue = new DefaultIssue()
+ .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY);
boolean added = moduleIssues.initAndAddIssue(issue);
assertThat(added).isFalse();
- verifyZeroInteractions(cache);
+ verifyZeroInteractions(reportPublisher);
}
@Test
@@ -131,22 +145,19 @@ public class ModuleIssuesTest {
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
initModuleIssues();
- Date analysisDate = new Date();
- when(project.getAnalysisDate()).thenReturn(analysisDate);
-
DefaultIssue issue = new DefaultIssue()
- .setKey("ABCDE")
- .setRuleKey(SQUID_RULE_KEY)
- .setSeverity(Severity.CRITICAL);
- when(filters.accept(issue)).thenReturn(true);
+ .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY)
+ .overrideSeverity(org.sonar.api.batch.rule.Severity.CRITICAL);
+
+ when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true);
boolean added = moduleIssues.initAndAddIssue(issue);
assertThat(added).isTrue();
- ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
- verify(cache).put(argument.capture());
- assertThat(argument.getValue().severity()).isEqualTo(Severity.CRITICAL);
- assertThat(argument.getValue().creationDate()).isEqualTo(DateUtils.truncate(analysisDate, Calendar.SECOND));
+ ArgumentCaptor<BatchReport.Issue> argument = ArgumentCaptor.forClass(BatchReport.Issue.class);
+ verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture());
+ assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.batch.protocol.Constants.Severity.CRITICAL);
}
@Test
@@ -155,17 +166,15 @@ public class ModuleIssuesTest {
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
initModuleIssues();
- Date analysisDate = new Date();
- when(project.getAnalysisDate()).thenReturn(analysisDate);
-
- DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY).setSeverity(null);
- when(filters.accept(issue)).thenReturn(true);
+ DefaultIssue issue = new DefaultIssue()
+ .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY);
+ when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true);
moduleIssues.initAndAddIssue(issue);
- ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
- verify(cache).put(argument.capture());
- assertThat(argument.getValue().severity()).isEqualTo(Severity.INFO);
- assertThat(argument.getValue().creationDate()).isEqualTo(DateUtils.truncate(analysisDate, Calendar.SECOND));
+ ArgumentCaptor<BatchReport.Issue> argument = ArgumentCaptor.forClass(BatchReport.Issue.class);
+ verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture());
+ assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.batch.protocol.Constants.Severity.INFO);
}
@Test
@@ -174,22 +183,17 @@ public class ModuleIssuesTest {
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).setName(SQUID_RULE_NAME).activate();
initModuleIssues();
- Date analysisDate = new Date();
- when(project.getAnalysisDate()).thenReturn(analysisDate);
-
DefaultIssue issue = new DefaultIssue()
- .setKey("ABCDE")
- .setRuleKey(SQUID_RULE_KEY)
- .setSeverity(Severity.CRITICAL)
- .setMessage("");
- when(filters.accept(issue)).thenReturn(true);
+ .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message(""))
+ .forRule(SQUID_RULE_KEY);
+ when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true);
boolean added = moduleIssues.initAndAddIssue(issue);
assertThat(added).isTrue();
- ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
- verify(cache).put(argument.capture());
- assertThat(argument.getValue().message()).isEqualTo("Avoid Cycle");
+ ArgumentCaptor<BatchReport.Issue> argument = ArgumentCaptor.forClass(BatchReport.Issue.class);
+ verify(reportPublisher.getWriter()).appendComponentIssue(eq(1), argument.capture());
+ assertThat(argument.getValue().getMsg()).isEqualTo("Avoid Cycle");
}
@Test
@@ -199,23 +203,22 @@ public class ModuleIssuesTest {
initModuleIssues();
DefaultIssue issue = new DefaultIssue()
- .setKey("ABCDE")
- .setRuleKey(SQUID_RULE_KEY)
- .setSeverity(Severity.CRITICAL);
+ .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message(""))
+ .forRule(SQUID_RULE_KEY);
- when(filters.accept(issue)).thenReturn(false);
+ when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(false);
boolean added = moduleIssues.initAndAddIssue(issue);
assertThat(added).isFalse();
- verifyZeroInteractions(cache);
+ verifyZeroInteractions(reportPublisher);
}
/**
* Every rules and active rules has to be added in builders before creating ModuleIssues
*/
private void initModuleIssues() {
- moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), cache, project, filters);
+ moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), project, filters, reportPublisher, componentCache);
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java
index f73d1c60f46..660b861bece 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java
@@ -19,8 +19,6 @@
*/
package org.sonar.batch.mediumtest.deprecated;
-import org.sonar.xoo.rule.XooRulesDefinition;
-
import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.io.IOException;
@@ -33,6 +31,7 @@ import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.TaskResult;
import org.sonar.batch.protocol.input.ActiveRule;
import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
@@ -84,13 +83,16 @@ public class DeprecatedApiMediumTest {
.build())
.start();
- assertThat(result.issues()).extracting("componentKey", "message", "line").containsOnly(
- tuple("com.foo.project:src/sample.xoo", "Issue created using deprecated API", null),
- tuple("com.foo.project:src/sample.xoo", "Issue created using deprecated API", 1),
- tuple("com.foo.project:src/package/sample.xoo", "Issue created using deprecated API", null),
- tuple("com.foo.project:src/package/sample.xoo", "Issue created using deprecated API", 1),
- tuple("com.foo.project:src", "Issue created using deprecated API", null),
- tuple("com.foo.project:src/package", "Issue created using deprecated API", null));
+ assertThat(result.issuesFor(result.inputFile("src/sample.xoo"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0),
+ tuple("Issue created using deprecated API", 1));
+ assertThat(result.issuesFor(result.inputFile("src/package/sample.xoo"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0),
+ tuple("Issue created using deprecated API", 1));
+ assertThat(result.issuesFor(result.inputDir("src"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0));
+ assertThat(result.issuesFor(result.inputDir("src/package"))).extracting("msg", "line").containsOnly(
+ tuple("Issue created using deprecated API", 0));
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java
index 2beb8f133b5..42ef0b87433 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java
@@ -19,23 +19,22 @@
*/
package org.sonar.batch.mediumtest.fs;
-import org.sonar.xoo.rule.XooRulesDefinition;
-
import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import org.sonar.api.CoreProperties;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.TaskResult;
import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.output.BatchReport.Issue;
import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Date;
+import org.sonar.xoo.rule.XooRulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
@@ -48,7 +47,6 @@ public class ProjectBuilderMediumTest {
.registerPlugin("xoo", new XooPlugin())
.addRules(new XooRulesDefinition())
.addDefaultQProfile("xoo", "Sonar Way")
- .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PREVIEW))
.setPreviousAnalysisDate(new Date())
.activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo"))
.build();
@@ -88,19 +86,19 @@ public class ProjectBuilderMediumTest {
.put("sonar.xoo.enableProjectBuilder", "true")
.build())
.start();
-
- assertThat(result.issues()).hasSize(10);
+ List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+ assertThat(issues).hasSize(10);
boolean foundIssueAtLine1 = false;
- for (org.sonar.api.issue.Issue issue : result.issues()) {
- if (issue.line() == 1) {
+ for (Issue issue : issues) {
+ if (issue.getLine() == 1) {
foundIssueAtLine1 = true;
- assertThat(issue.componentKey()).isEqualTo("com.foo.project:module1:src/sample.xoo");
- assertThat(issue.message()).isEqualTo("This issue is generated on each line");
- assertThat(issue.effortToFix()).isNull();
+ assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line");
+ assertThat(issue.hasEffortToFix()).isFalse();
}
}
assertThat(foundIssueAtLine1).isTrue();
+
}
@Test
@@ -130,15 +128,15 @@ public class ProjectBuilderMediumTest {
.build())
.start();
- assertThat(result.issues()).hasSize(10);
+ List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+ assertThat(issues).hasSize(10);
boolean foundIssueAtLine1 = false;
- for (org.sonar.api.issue.Issue issue : result.issues()) {
- if (issue.line() == 1) {
+ for (Issue issue : issues) {
+ if (issue.getLine() == 1) {
foundIssueAtLine1 = true;
- assertThat(issue.componentKey()).isEqualTo("com.foo.project:module1:my-branch:src/sample.xoo");
- assertThat(issue.message()).isEqualTo("This issue is generated on each line");
- assertThat(issue.effortToFix()).isNull();
+ assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line");
+ assertThat(issue.hasEffortToFix()).isFalse();
}
}
assertThat(foundIssueAtLine1).isTrue();
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/RandomFsAccessMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/RandomFsAccessMediumTest.java
index f27f72c5769..cfbd881e224 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/RandomFsAccessMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/RandomFsAccessMediumTest.java
@@ -19,9 +19,11 @@
*/
package org.sonar.batch.mediumtest.fs;
-import org.sonar.xoo.rule.XooRulesDefinition;
-
import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
@@ -32,11 +34,9 @@ import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.Benchmark;
import org.sonar.batch.mediumtest.TaskResult;
import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.output.BatchReport.Issue;
import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
+import org.sonar.xoo.rule.XooRulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
@@ -92,7 +92,8 @@ public class RandomFsAccessMediumTest {
.build())
.start();
- assertThat(result.issues()).hasSize(ISSUE_COUNT);
+ List<Issue> issues = result.issuesFor(result.inputFile("src/sample1.xoo"));
+ assertThat(issues).hasSize(10);
bench.expectLessThanOrEqualTo("Time to create " + ISSUE_COUNT + " issues on random files using FileSystem query", System.currentTimeMillis() - start, 2000);
}
@@ -121,7 +122,8 @@ public class RandomFsAccessMediumTest {
.build())
.start();
- assertThat(result.issues()).hasSize(ISSUE_COUNT);
+ List<Issue> issues = result.issuesFor(result.inputFile("src/sample1.xoo"));
+ assertThat(issues).hasSize(10);
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java
index d5bd3b44c9a..fe31dbe7d25 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java
@@ -19,10 +19,10 @@
*/
package org.sonar.batch.mediumtest.issues;
-import org.sonar.batch.protocol.input.Rule;
-
-import org.sonar.xoo.rule.XooRulesDefinition;
import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
@@ -31,10 +31,10 @@ import org.junit.rules.TemporaryFolder;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.TaskResult;
import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.input.Rule;
+import org.sonar.batch.protocol.output.BatchReport.Issue;
import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.io.IOException;
+import org.sonar.xoo.rule.XooRulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
@@ -85,20 +85,19 @@ public class ChecksMediumTest {
.build())
.start();
- assertThat(result.issues()).hasSize(2);
+ List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+ assertThat(issues).hasSize(2);
boolean foundIssueAtLine1 = false;
boolean foundIssueAtLine2 = false;
- for (org.sonar.api.issue.Issue issue : result.issues()) {
- if (issue.line() == 1) {
+ for (Issue issue : issues) {
+ if (issue.getLine() == 1) {
foundIssueAtLine1 = true;
- assertThat(issue.componentKey()).isEqualTo("com.foo.project:src/sample.xoo");
- assertThat(issue.message()).isEqualTo("A template rule");
+ assertThat(issue.getMsg()).isEqualTo("A template rule");
}
- if (issue.line() == 2) {
+ if (issue.getLine() == 2) {
foundIssueAtLine2 = true;
- assertThat(issue.componentKey()).isEqualTo("com.foo.project:src/sample.xoo");
- assertThat(issue.message()).isEqualTo("Another template rule");
+ assertThat(issue.getMsg()).isEqualTo("Another template rule");
}
}
assertThat(foundIssueAtLine1).isTrue();
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java
index 1399a135f72..6979b4e6e2c 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java
@@ -19,25 +19,23 @@
*/
package org.sonar.batch.mediumtest.issues;
-import org.sonar.api.issue.Issue;
-import org.sonar.batch.bootstrapper.IssueListener;
-import org.sonar.xoo.rule.XooRulesDefinition;
import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.rule.Severity;
+import org.sonar.batch.bootstrapper.IssueListener;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.TaskResult;
import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.output.BatchReport.Issue;
import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.LinkedList;
-import java.util.List;
+import org.sonar.xoo.rule.XooRulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
@@ -52,44 +50,15 @@ public class IssuesMediumTest {
.addRules(new XooRulesDefinition())
.activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo"))
.build();
-
- public BatchMediumTester testerPreview = BatchMediumTester.builder()
- .registerPlugin("xoo", new XooPlugin())
- .addDefaultQProfile("xoo", "Sonar Way")
- .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "preview"))
- .addRules(new XooRulesDefinition())
- .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo"))
- .build();
@Before
public void prepare() {
tester.start();
- testerPreview.start();
}
@After
public void stop() {
tester.stop();
- testerPreview.stop();
- }
-
- @Test
- public void testIssueCallback() throws Exception {
- File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
- File tmpDir = temp.newFolder();
- FileUtils.copyDirectory(projectDir, tmpDir);
- IssueRecorder issueListener = new IssueRecorder();
-
- TaskResult result = testerPreview
- .newScanTask(new File(tmpDir, "sonar-project.properties"))
- .setIssueListener(issueListener)
- .property("sonar.analysis.mode", "preview")
- .start();
-
- assertThat(result.issues()).hasSize(14);
- assertThat(issueListener.issueList).hasSize(14);
-
- assertThat(result.issues()).containsExactlyElementsOf(issueListener.issueList);
}
@Test
@@ -104,7 +73,7 @@ public class IssuesMediumTest {
.setIssueListener(issueListener)
.start();
- assertThat(result.issues()).hasSize(14);
+ assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(8);
assertThat(issueListener.issueList).hasSize(0);
}
@@ -118,7 +87,8 @@ public class IssuesMediumTest {
.newScanTask(new File(tmpDir, "sonar-project.properties"))
.start();
- assertThat(result.issues()).hasSize(14);
+ List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+ assertThat(issues).hasSize(8 /* lines */);
}
@Test
@@ -132,7 +102,8 @@ public class IssuesMediumTest {
.property("sonar.xoo.internalKey", "OneIssuePerLine.internal")
.start();
- assertThat(result.issues()).hasSize(14 /* 8 + 6 lines */+ 2 /* 2 files */);
+ List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+ assertThat(issues).hasSize(8 /* lines */ + 1 /* file */);
}
@Test
@@ -146,7 +117,8 @@ public class IssuesMediumTest {
.property("sonar.oneIssuePerLine.forceSeverity", "CRITICAL")
.start();
- assertThat(result.issues().iterator().next().severity()).isEqualTo(Severity.CRITICAL.name());
+ List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+ assertThat(issues.get(0).getSeverity()).isEqualTo(org.sonar.batch.protocol.Constants.Severity.CRITICAL);
}
@Test
@@ -161,7 +133,8 @@ public class IssuesMediumTest {
.property("sonar.issue.ignore.allfile.1.fileRegexp", "object")
.start();
- assertThat(result.issues()).hasSize(8);
+ assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(8 /* lines */);
+ assertThat(result.issuesFor(result.inputFile("xources/hello/helloscala.xoo"))).isEmpty();
}
@Test
@@ -186,25 +159,25 @@ public class IssuesMediumTest {
.build())
.start();
- assertThat(result.issues()).hasSize(10);
+ List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo"));
+ assertThat(issues).hasSize(10);
boolean foundIssueAtLine1 = false;
- for (org.sonar.api.issue.Issue issue : result.issues()) {
- if (issue.line() == 1) {
+ for (Issue issue : issues) {
+ if (issue.getLine() == 1) {
foundIssueAtLine1 = true;
- assertThat(issue.componentKey()).isEqualTo("com.foo.project:src/sample.xoo");
- assertThat(issue.message()).isEqualTo("This issue is generated on each line");
- assertThat(issue.effortToFix()).isNull();
+ assertThat(issue.getMsg()).isEqualTo("This issue is generated on each line");
+ assertThat(issue.hasEffortToFix()).isFalse();
}
}
assertThat(foundIssueAtLine1).isTrue();
}
private class IssueRecorder implements IssueListener {
- List<Issue> issueList = new LinkedList<>();
+ List<org.sonar.api.issue.Issue> issueList = new LinkedList<>();
@Override
- public void handle(Issue issue) {
+ public void handle(org.sonar.api.issue.Issue issue) {
issueList.add(issue);
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
index 2d389a5d8cf..d181918a16b 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
@@ -19,9 +19,9 @@
*/
package org.sonar.batch.mediumtest.issues;
-import org.sonar.xoo.rule.XooRulesDefinition;
-
import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
@@ -31,9 +31,7 @@ import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.TaskResult;
import org.sonar.batch.protocol.input.ActiveRule;
import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.io.IOException;
+import org.sonar.xoo.rule.XooRulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
@@ -84,9 +82,7 @@ public class IssuesOnDirMediumTest {
.build())
.start();
- assertThat(result.issues()).hasSize(2);
- assertThat(result.issues().iterator().next().componentKey()).isEqualTo("com.foo.project:src");
-
+ assertThat(result.issuesFor(result.inputDir("src"))).hasSize(2);
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java
index 02b61ca0cdb..a9c1317b014 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java
@@ -19,12 +19,7 @@
*/
package org.sonar.batch.mediumtest.issues;
-import org.sonar.xoo.rule.XooRulesDefinition;
-
-import org.sonar.batch.protocol.input.Rule;
-
import java.io.File;
-
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
@@ -33,7 +28,10 @@ import org.junit.rules.TemporaryFolder;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.TaskResult;
import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.input.Rule;
import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
import static org.assertj.core.api.Assertions.assertThat;
public class MultilineIssuesMediumTest {
@@ -69,7 +67,7 @@ public class MultilineIssuesMediumTest {
.newScanTask(new File(tmpDir, "sonar-project.properties"))
.start();
- assertThat(result.issues()).hasSize(1);
+ assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(1);
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/EmptyFileTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/EmptyFileTest.java
index 3f7be697caf..37cb1fd994d 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/EmptyFileTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/EmptyFileTest.java
@@ -75,7 +75,7 @@ public class EmptyFileTest {
.property("sonar.xoo.internalKey", "my/internal/key")
.start();
- assertThat(result.issues()).hasSize(11);
+ assertThat(result.trackedIssues()).hasSize(11);
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/IncrementalModeMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/IncrementalModeMediumTest.java
index 86697dcea42..6d1685a3d1f 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/IncrementalModeMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/IncrementalModeMediumTest.java
@@ -19,9 +19,11 @@
*/
package org.sonar.batch.mediumtest.preview;
-import org.sonar.xoo.rule.XooRulesDefinition;
-
import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.junit.After;
@@ -29,20 +31,17 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.CoreProperties;
-import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.TaskResult;
import org.sonar.batch.protocol.Constants.Severity;
import org.sonar.batch.protocol.input.ActiveRule;
import org.sonar.batch.protocol.input.FileData;
import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import org.sonar.xoo.rule.XooRulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.groups.Tuple.tuple;
public class IncrementalModeMediumTest {
@@ -143,21 +142,14 @@ public class IncrementalModeMediumTest {
.build())
.start();
- int newIssues = 0;
- int openIssues = 0;
- int resolvedIssue = 0;
- for (Issue issue : result.issues()) {
- if (issue.isNew()) {
- newIssues++;
- } else if (issue.resolution() != null) {
- resolvedIssue++;
- } else {
- openIssues++;
- }
- }
- assertThat(newIssues).isEqualTo(4);
- assertThat(openIssues).isEqualTo(2);
- assertThat(resolvedIssue).isEqualTo(1);
+ assertThat(result.trackedIssues()).extracting("ruleKey", "line", "message", "status", "resolution", "new").containsOnly(
+ tuple(RuleKey.of("xoo", "OneIssuePerLine"), 1, "This issue is generated on each line", "OPEN", null, false),
+ tuple(RuleKey.of("xoo", "OneIssuePerLine"), 2, "This issue is generated on each line", "OPEN", null, true),
+ tuple(RuleKey.of("xoo", "OneIssuePerLine"), 3, "This issue is generated on each line", "OPEN", null, true),
+ tuple(RuleKey.of("xoo", "OneIssuePerLine"), 4, "This issue is generated on each line", "OPEN", null, true),
+ tuple(RuleKey.of("xoo", "OneIssuePerLine"), 5, "This issue is generated on each line", "OPEN", null, true),
+ tuple(RuleKey.of("manual", "MyManualIssue"), 4, null, "OPEN", null, false),
+ tuple(RuleKey.of("xoo", "OneIssuePerFile"), null, "An issue that is no more detected", "CLOSED", "REMOVED", false));
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java
index e09631ac9fd..3ba49c04107 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java
@@ -19,12 +19,16 @@
*/
package org.sonar.batch.mediumtest.preview;
-import org.sonar.batch.protocol.input.Rule;
-
-import org.sonar.xoo.rule.XooRulesDefinition;
import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -32,17 +36,16 @@ import org.junit.rules.TemporaryFolder;
import org.sonar.api.CoreProperties;
import org.sonar.api.issue.Issue;
import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.bootstrapper.IssueListener;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.batch.mediumtest.issues.IssuesMediumTest;
import org.sonar.batch.protocol.Constants.Severity;
import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.input.Rule;
import org.sonar.batch.scan.report.ConsoleReport;
import org.sonar.xoo.XooPlugin;
-
-import java.io.File;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import org.sonar.xoo.rule.XooRulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
@@ -131,9 +134,16 @@ public class PreviewAndReportsMediumTest {
tester.stop();
}
+ private File copyProject(String path) throws Exception {
+ File projectDir = temp.newFolder();
+ File originalProjectDir = new File(PreviewAndReportsMediumTest.class.getResource(path).toURI());
+ FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar")));
+ return projectDir;
+ }
+
@Test
public void testIssueTracking() throws Exception {
- File projectDir = new File(PreviewAndReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File projectDir = copyProject("/mediumtest/xoo/sample");
TaskResult result = tester
.newScanTask(new File(projectDir, "sonar-project.properties"))
@@ -142,7 +152,7 @@ public class PreviewAndReportsMediumTest {
int newIssues = 0;
int openIssues = 0;
int resolvedIssue = 0;
- for (Issue issue : result.issues()) {
+ for (Issue issue : result.trackedIssues()) {
if (issue.isNew()) {
newIssues++;
} else if (issue.resolution() != null) {
@@ -158,7 +168,7 @@ public class PreviewAndReportsMediumTest {
@Test
public void testConsoleReport() throws Exception {
- File projectDir = new File(PreviewAndReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File projectDir = copyProject("/mediumtest/xoo/sample");
tester
.newScanTask(new File(projectDir, "sonar-project.properties"))
@@ -170,7 +180,7 @@ public class PreviewAndReportsMediumTest {
@Test
public void testPostJob() throws Exception {
- File projectDir = new File(PreviewAndReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File projectDir = copyProject("/mediumtest/xoo/sample");
tester
.newScanTask(new File(projectDir, "sonar-project.properties"))
@@ -191,7 +201,7 @@ public class PreviewAndReportsMediumTest {
@Test
public void testHtmlReport() throws Exception {
- File projectDir = new File(PreviewAndReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File projectDir = copyProject("/mediumtest/xoo/sample");
tester
.newScanTask(new File(projectDir, "sonar-project.properties"))
@@ -225,4 +235,30 @@ public class PreviewAndReportsMediumTest {
assertThat(FileUtils.readFileToString(new File(baseDir, ".sonar/issues-report/issues-report-light.html"))).contains("No file analyzed");
}
+ @Test
+ public void testIssueCallback() throws Exception {
+ File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File tmpDir = temp.newFolder();
+ FileUtils.copyDirectory(projectDir, tmpDir);
+ IssueRecorder issueListener = new IssueRecorder();
+
+ TaskResult result = tester
+ .newScanTask(new File(tmpDir, "sonar-project.properties"))
+ .setIssueListener(issueListener)
+ .start();
+
+ assertThat(result.trackedIssues()).hasSize(14);
+ assertThat(issueListener.issueList).hasSize(14);
+ assertThat(result.trackedIssues()).containsExactlyElementsOf(issueListener.issueList);
+ }
+
+ private class IssueRecorder implements IssueListener {
+ List<org.sonar.api.issue.Issue> issueList = new LinkedList<>();
+
+ @Override
+ public void handle(org.sonar.api.issue.Issue issue) {
+ issueList.add(issue);
+ }
+ }
+
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java
deleted file mode 100644
index 3e0f79030bf..00000000000
--- a/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java
+++ /dev/null
@@ -1,106 +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.batch.report;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.resources.Project;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.batch.index.BatchComponentCache;
-import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.protocol.output.BatchReportReader;
-import org.sonar.batch.protocol.output.BatchReportWriter;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.FieldDiffs;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class IssuesPublisherTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- IssueCache issueCache;
- ProjectDefinition projectDef;
- Project project;
-
- IssuesPublisher underTest;
-
- @Before
- public void prepare() {
- projectDef = ProjectDefinition.create().setKey("foo");
- project = new Project("foo").setAnalysisDate(new Date(1234567L));
- BatchComponentCache componentCache = new BatchComponentCache();
- org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
- componentCache.add(project, null);
- componentCache.add(sampleFile, project);
- issueCache = mock(IssueCache.class);
- when(issueCache.byComponent(anyString())).thenReturn(Collections.<DefaultIssue>emptyList());
- underTest = new IssuesPublisher(componentCache, issueCache);
- }
-
- @Test
- public void write_issues() throws Exception {
- DefaultIssue issue1 = new DefaultIssue();
- issue1.setKey("uuid");
- issue1.setSeverity("MAJOR");
- issue1.setRuleKey(RuleKey.of("repo", "rule"));
- DefaultIssue issue2 = new DefaultIssue();
- issue2.setKey("uuid2");
- issue2.setSeverity("MAJOR");
- issue2.setRuleKey(RuleKey.of("repo", "rule"));
- issue2.setLine(2);
- issue2.setMessage("msg");
- issue2.setEffortToFix(2d);
- issue2.setResolution("FIXED");
- issue2.setStatus("RESOLVED");
- issue2.setChecksum("checksum");
- issue2.setReporter("reporter");
- issue2.setAssignee("assignee");
- issue2.setActionPlanKey("action");
- issue2.setAuthorLogin("author");
- issue2.setCurrentChange(new FieldDiffs().setUserLogin("foo"));
- issue2.setCreationDate(new Date());
- issue2.setUpdateDate(new Date());
- issue2.setCloseDate(new Date());
- issue2.setSelectedAt(1234L);
- when(issueCache.byComponent("foo:src/Foo.php")).thenReturn(Arrays.asList(issue1, issue2));
-
- File outputDir = temp.newFolder();
- BatchReportWriter writer = new BatchReportWriter(outputDir);
-
- underTest.publish(writer);
-
- BatchReportReader reader = new BatchReportReader(outputDir);
- assertThat(reader.readComponentIssues(1)).hasSize(0);
- assertThat(reader.readComponentIssues(2)).hasSize(2);
- }
-
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java b/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
index aa2e32f96e6..5d8a9a5cdf3 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
@@ -19,25 +19,18 @@
*/
package org.sonar.batch.sensor;
-import java.io.StringReader;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;
-import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultFileSystem;
-import org.sonar.api.batch.fs.internal.DefaultInputDir;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.fs.internal.FileMetadata;
import org.sonar.api.batch.measure.MetricFinder;
import org.sonar.api.batch.rule.ActiveRules;
-import org.sonar.api.batch.rule.Severity;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
-import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
-import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.api.config.Settings;
import org.sonar.api.measures.CoreMetrics;
@@ -45,7 +38,6 @@ import org.sonar.api.measures.Measure;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
-import org.sonar.api.rule.RuleKey;
import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.index.BatchComponentCache;
import org.sonar.batch.issue.ModuleIssues;
@@ -57,7 +49,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class DefaultSensorStorageTest {
@@ -143,66 +134,4 @@ public class DefaultSensorStorageTest {
assertThat(m.getMetric()).isEqualTo(CoreMetrics.NCLOC);
}
- @Test
- public void shouldAddIssueOnFile() {
- InputFile file = new DefaultInputFile("foo", "src/Foo.php").initMetadata(new FileMetadata().readMetadata(new StringReader("Foo\nBar\nBiz\n")));
-
- ArgumentCaptor<org.sonar.core.issue.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.core.issue.DefaultIssue.class);
-
- sensorStorage.store(new DefaultIssue()
- .addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo"))
- .forRule(RuleKey.of("foo", "bar"))
- .effortToFix(10.0));
-
- verify(moduleIssues).initAndAddIssue(argumentCaptor.capture());
-
- org.sonar.core.issue.DefaultIssue issue = argumentCaptor.getValue();
- assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
- assertThat(issue.message()).isEqualTo("Foo");
- assertThat(issue.line()).isEqualTo(3);
- assertThat(issue.severity()).isNull();
- assertThat(issue.effortToFix()).isEqualTo(10.0);
- }
-
- @Test
- public void shouldAddIssueOnDirectory() {
- InputDir dir = new DefaultInputDir("foo", "src");
-
- ArgumentCaptor<org.sonar.core.issue.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.core.issue.DefaultIssue.class);
-
- sensorStorage.store(new DefaultIssue()
- .addLocation(new DefaultIssueLocation().onDir(dir).message("Foo"))
- .forRule(RuleKey.of("foo", "bar"))
- .effortToFix(10.0));
-
- verify(moduleIssues).initAndAddIssue(argumentCaptor.capture());
-
- org.sonar.core.issue.DefaultIssue issue = argumentCaptor.getValue();
- assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
- assertThat(issue.message()).isEqualTo("Foo");
- assertThat(issue.line()).isNull();
- assertThat(issue.severity()).isNull();
- assertThat(issue.effortToFix()).isEqualTo(10.0);
- }
-
- @Test
- public void shouldAddIssueOnProject() {
- ArgumentCaptor<org.sonar.core.issue.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.core.issue.DefaultIssue.class);
-
- sensorStorage.store(new DefaultIssue()
- .addLocation(new DefaultIssueLocation().onProject().message("Foo"))
- .forRule(RuleKey.of("foo", "bar"))
- .overrideSeverity(Severity.BLOCKER)
- .effortToFix(10.0));
-
- verify(moduleIssues).initAndAddIssue(argumentCaptor.capture());
-
- org.sonar.core.issue.DefaultIssue issue = argumentCaptor.getValue();
- assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
- assertThat(issue.message()).isEqualTo("Foo");
- assertThat(issue.line()).isNull();
- assertThat(issue.severity()).isEqualTo("BLOCKER");
- assertThat(issue.effortToFix()).isEqualTo(10.0);
- }
-
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java
index 428d49d56f0..7cf03cb0579 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java
@@ -133,16 +133,14 @@ public interface Issuable extends Perspective {
boolean addIssue(Issue issue);
/**
- * Unresolved issues, including the issues reported by end-users.
- * <p/>
- * {@link org.sonar.api.batch.Decorator}s calling this method must be annotated with {@code @DependsUpon(DecoratorBarriers.ISSUES_TRACKED)}.
+ * @deprecated since 5.2 no more decorators on batch side
*/
+ @Deprecated
List<Issue> issues();
/**
- * Issues marked as resolved during this scan.
- * <p/>
- * {@link org.sonar.api.batch.Decorator}s calling this method must be annotated with {@code @DependsUpon(DecoratorBarriers.ISSUES_TRACKED)}.
+ * @deprecated since 5.2 no more decorators on batch side
*/
+ @Deprecated
List<Issue> resolvedIssues();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java
index a1fda4b1671..654d40e3a31 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java
@@ -19,12 +19,12 @@
*/
package org.sonar.api.issue;
-import org.sonar.api.batch.BatchSide;
import org.sonar.api.ExtensionPoint;
+import org.sonar.api.batch.BatchSide;
/**
* @since 3.6
- * @deprecated since 4.0
+ * @deprecated since 4.0 use {@link org.sonar.api.issue.batch.IssueFilter}
*/
@Deprecated
@BatchSide