Avoid intermediate storage in persistittags/5.2-RC1
@@ -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); |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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) { |
@@ -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) { |
@@ -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); | |||
} | |||
} | |||
@@ -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()); |
@@ -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); |
@@ -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(); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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, |
@@ -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) { |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
@@ -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); | |||
} | |||
} |
@@ -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,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(); |
@@ -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); | |||
} | |||
@@ -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(); |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
@@ -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); | |||
} | |||
} |
@@ -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)); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} |
@@ -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 |