瀏覽代碼

Write raw issues directly in protobuf report

Avoid intermediate storage in persistit
tags/5.2-RC1
Julien HENRY 8 年之前
父節點
當前提交
5b93180b18
共有 29 個檔案被更改,包括 526 行新增755 行删除
  1. 13
    0
      sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java
  2. 4
    18
      sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java
  3. 2
    4
      sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java
  4. 2
    2
      sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java
  5. 72
    9
      sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
  6. 92
    88
      sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java
  7. 9
    11
      sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java
  8. 90
    42
      sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java
  9. 20
    1
      sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
  10. 0
    84
      sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java
  11. 0
    2
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
  12. 1
    27
      sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
  13. 0
    64
      sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java
  14. 2
    4
      sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java
  15. 63
    60
      sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
  16. 11
    9
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/deprecated/DeprecatedApiMediumTest.java
  17. 19
    21
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/ProjectBuilderMediumTest.java
  18. 10
    8
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/RandomFsAccessMediumTest.java
  19. 13
    14
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ChecksMediumTest.java
  20. 24
    51
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java
  21. 4
    8
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
  22. 4
    6
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java
  23. 1
    1
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/EmptyFileTest.java
  24. 15
    23
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/IncrementalModeMediumTest.java
  25. 49
    13
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java
  26. 0
    106
      sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java
  27. 0
    71
      sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
  28. 4
    6
      sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java
  29. 2
    2
      sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java

+ 13
- 0
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);

+ 4
- 18
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();
}

}

+ 2
- 4
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);
}
}

+ 2
- 2
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) {

+ 72
- 9
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) {

+ 92
- 88
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);
}
}


+ 9
- 11
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());

+ 90
- 42
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);

+ 20
- 1
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();
}

+ 0
- 84
sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java 查看文件

@@ -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();
}

}

+ 0
- 2
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,

+ 1
- 27
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) {

+ 0
- 64
sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java 查看文件

@@ -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);
}
}

+ 2
- 4
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);


+ 63
- 60
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);
}

}

+ 11
- 9
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));

}


+ 19
- 21
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();

+ 10
- 8
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);

}


+ 13
- 14
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();

+ 24
- 51
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);
}
}

+ 4
- 8
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);
}

}

+ 4
- 6
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);

}


+ 1
- 1
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);
}

}

+ 15
- 23
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));
}

}

+ 49
- 13
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);
}
}

}

+ 0
- 106
sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java 查看文件

@@ -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);
}

}

+ 0
- 71
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);
}

}

+ 4
- 6
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();
}

+ 2
- 2
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

Loading…
取消
儲存