Also rework new issue API on batch side.tags/5.2-RC1
@@ -40,8 +40,8 @@ public class TemplateRuleCheck implements Check { | |||
NewIssue newIssue = sensorContext.newIssue(); | |||
newIssue | |||
.forRule(ruleKey) | |||
.addLocation(newIssue.newLocation() | |||
.onFile(file) | |||
.at(newIssue.newLocation() | |||
.on(file) | |||
.at(file.selectLine(line))) | |||
.save(); | |||
} |
@@ -58,8 +58,8 @@ public class CreateIssueByInternalKeySensor implements Sensor { | |||
NewIssue newIssue = context.newIssue(); | |||
newIssue | |||
.forRule(rule.ruleKey()) | |||
.addLocation(newIssue.newLocation() | |||
.onFile(file) | |||
.at(newIssue.newLocation() | |||
.on(file) | |||
.message("This issue is generated on each file")) | |||
.save(); | |||
} |
@@ -34,6 +34,7 @@ import org.sonar.api.batch.sensor.Sensor; | |||
import org.sonar.api.batch.sensor.SensorContext; | |||
import org.sonar.api.batch.sensor.SensorDescriptor; | |||
import org.sonar.api.batch.sensor.issue.NewIssue; | |||
import org.sonar.api.batch.sensor.issue.NewIssueLocation; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.xoo.Xoo; | |||
@@ -76,7 +77,7 @@ public class MultilineIssuesSensor implements Sensor { | |||
while (m.find()) { | |||
Integer issueId = Integer.parseInt(m.group(1)); | |||
Integer issueLocationId = Integer.parseInt(m.group(2)); | |||
TextPointer newPointer = file.newPointer(currentLine, m.start()); | |||
TextPointer newPointer = file.newPointer(currentLine, m.end()); | |||
if (!startPositions.containsKey(issueId)) { | |||
startPositions.put(issueId, new HashMap<Integer, TextPointer>()); | |||
} | |||
@@ -100,10 +101,14 @@ public class MultilineIssuesSensor implements Sensor { | |||
for (Map.Entry<Integer, Map<Integer, TextPointer>> entry : startPositions.entrySet()) { | |||
NewIssue newIssue = context.newIssue().forRule(ruleKey); | |||
for (Map.Entry<Integer, TextPointer> location : entry.getValue().entrySet()) { | |||
newIssue.addLocation(newIssue.newLocation() | |||
.onFile(file) | |||
.at(file.newRange(location.getValue(), endPositions.get(entry.getKey()).get(location.getKey()))) | |||
.message("Multiline issue")); | |||
NewIssueLocation newLocation = newIssue.newLocation() | |||
.on(file) | |||
.at(file.newRange(location.getValue(), endPositions.get(entry.getKey()).get(location.getKey()))); | |||
if (location.getKey() == 1) { | |||
newIssue.at(newLocation.message("Primary location")); | |||
} else { | |||
newIssue.addLocation(newLocation.message("Location #" + location.getKey())); | |||
} | |||
} | |||
newIssue.save(); | |||
} |
@@ -54,8 +54,8 @@ public class OneIssueOnDirPerFileSensor implements Sensor { | |||
NewIssue newIssue = context.newIssue(); | |||
newIssue | |||
.forRule(ruleKey) | |||
.addLocation(newIssue.newLocation() | |||
.onDir(inputDir) | |||
.at(newIssue.newLocation() | |||
.on(inputDir) | |||
.message("This issue is generated for file " + file.relativePath())) | |||
.save(); | |||
} |
@@ -61,8 +61,8 @@ public class OneIssuePerLineSensor implements Sensor { | |||
NewIssue newIssue = context.newIssue(); | |||
newIssue | |||
.forRule(ruleKey) | |||
.addLocation(newIssue.newLocation() | |||
.onFile(file) | |||
.at(newIssue.newLocation() | |||
.on(file) | |||
.at(file.selectLine(line)) | |||
.message("This issue is generated on each line")) | |||
.effortToFix(context.settings().getDouble(EFFORT_TO_FIX_PROPERTY)) |
@@ -66,8 +66,8 @@ public class RandomAccessSensor implements Sensor { | |||
NewIssue newIssue = context.newIssue(); | |||
newIssue | |||
.forRule(ruleKey) | |||
.addLocation(newIssue.newLocation() | |||
.onFile(file) | |||
.at(newIssue.newLocation() | |||
.on(file) | |||
.at(file.selectLine(1)) | |||
.message("This issue is generated on each file")) | |||
.save(); |
@@ -103,8 +103,9 @@ message Issue { | |||
optional Severity severity = 5; | |||
optional double effort_to_fix = 6; | |||
optional string attributes = 7; | |||
repeated IssueLocation locations = 8; | |||
repeated ExecutionFlow execution_flows = 9; | |||
optional IssueLocation primary_location = 9; | |||
repeated IssueLocation additional_location = 10; | |||
repeated ExecutionFlow execution_flow = 11; | |||
} | |||
message IssueLocation { | |||
@@ -115,7 +116,7 @@ message IssueLocation { | |||
} | |||
message ExecutionFlow { | |||
repeated IssueLocation locations = 1; | |||
repeated IssueLocation location = 1; | |||
} | |||
message Changesets { |
@@ -23,9 +23,7 @@ import java.util.ArrayList; | |||
import java.util.Collection; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.batch.fs.InputPath; | |||
import org.sonar.api.resources.Qualifiers; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.resources.Resource; | |||
import org.sonar.api.resources.ResourceUtils; | |||
@@ -35,7 +33,7 @@ public class BatchComponent { | |||
private final Resource r; | |||
private final BatchComponent parent; | |||
private final Collection<BatchComponent> children = new ArrayList<>(); | |||
private InputPath inputPath; | |||
private InputComponent inputComponent; | |||
public BatchComponent(int batchId, Resource r, @Nullable BatchComponent parent) { | |||
this.batchId = batchId; | |||
@@ -68,21 +66,16 @@ public class BatchComponent { | |||
} | |||
public boolean isFile() { | |||
return Qualifiers.isFile(r) || StringUtils.equals(Qualifiers.UNIT_TEST_FILE, r.getQualifier()); | |||
return this.inputComponent.isFile(); | |||
} | |||
public boolean isDir() { | |||
return Qualifiers.isDirectory(r); | |||
} | |||
public BatchComponent setInputPath(InputPath inputPath) { | |||
this.inputPath = inputPath; | |||
public BatchComponent setInputComponent(InputComponent inputComponent) { | |||
this.inputComponent = inputComponent; | |||
return this; | |||
} | |||
@CheckForNull | |||
public InputPath inputPath() { | |||
return inputPath; | |||
public InputComponent inputComponent() { | |||
return inputComponent; | |||
} | |||
public boolean isProjectOrModule() { |
@@ -37,6 +37,7 @@ import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.batch.SonarIndex; | |||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.measure.MetricFinder; | |||
import org.sonar.api.design.Dependency; | |||
import org.sonar.api.measures.Measure; | |||
@@ -82,7 +83,8 @@ public class DefaultIndex extends SonarIndex { | |||
void doStart(Project rootProject) { | |||
Bucket bucket = new Bucket(rootProject); | |||
addBucket(rootProject, bucket); | |||
componentCache.add(rootProject, null); | |||
BatchComponent component = componentCache.add(rootProject, null); | |||
component.setInputComponent(new DefaultInputModule(rootProject.getEffectiveKey())); | |||
currentProject = rootProject; | |||
for (Project module : rootProject.getModules()) { | |||
@@ -269,11 +271,7 @@ public class DefaultIndex extends SonarIndex { | |||
return null; | |||
} | |||
Resource parent = null; | |||
if (!ResourceUtils.isLibrary(resource)) { | |||
// a library has no parent | |||
parent = (Resource) ObjectUtils.defaultIfNull(parentReference, currentProject); | |||
} | |||
Resource parent = (Resource) ObjectUtils.defaultIfNull(parentReference, currentProject); | |||
Bucket parentBucket = getBucket(parent); | |||
if (parentBucket == null && parent != null) { | |||
@@ -290,7 +288,10 @@ public class DefaultIndex extends SonarIndex { | |||
addBucket(resource, bucket); | |||
Resource parentResource = parentBucket != null ? parentBucket.getResource() : null; | |||
componentCache.add(resource, parentResource); | |||
BatchComponent component = componentCache.add(resource, parentResource); | |||
if (ResourceUtils.isProject(resource)) { | |||
component.setInputComponent(new DefaultInputModule(resource.getEffectiveKey())); | |||
} | |||
return bucket; | |||
} |
@@ -43,7 +43,7 @@ public class DefaultIssuable implements Issuable { | |||
@Override | |||
public IssueBuilder newIssueBuilder() { | |||
DefaultIssue newIssue = (DefaultIssue) sensorContext.newIssue(); | |||
return new DeprecatedIssueBuilderWrapper(component, newIssue); | |||
return new DeprecatedIssueBuilderWrapper(component.inputComponent(), newIssue); | |||
} | |||
@Override |
@@ -19,10 +19,9 @@ | |||
*/ | |||
package org.sonar.batch.issue; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import com.google.common.base.Preconditions; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.batch.fs.InputDir; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.TextRange; | |||
import org.sonar.api.batch.rule.Severity; | |||
@@ -33,17 +32,15 @@ import org.sonar.api.issue.Issuable; | |||
import org.sonar.api.issue.Issuable.IssueBuilder; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.batch.index.BatchComponent; | |||
public class DeprecatedIssueBuilderWrapper implements Issuable.IssueBuilder { | |||
private final DefaultIssue newIssue; | |||
private final BatchComponent primaryComponent; | |||
private final InputComponent primaryComponent; | |||
private TextRange primaryRange = null; | |||
private String primaryMessage = null; | |||
private List<NewIssueLocation> locations = new ArrayList<>(); | |||
public DeprecatedIssueBuilderWrapper(BatchComponent primaryComponent, DefaultIssue newIssue) { | |||
public DeprecatedIssueBuilderWrapper(InputComponent primaryComponent, DefaultIssue newIssue) { | |||
this.primaryComponent = primaryComponent; | |||
this.newIssue = newIssue; | |||
} | |||
@@ -56,9 +53,10 @@ public class DeprecatedIssueBuilderWrapper implements Issuable.IssueBuilder { | |||
@Override | |||
public IssueBuilder line(@Nullable Integer line) { | |||
Preconditions.checkState(newIssue.primaryLocation() == null, "Do not use line() and at() for the same issue"); | |||
if (primaryComponent.isFile()) { | |||
if (line != null) { | |||
this.primaryRange = ((InputFile) primaryComponent.inputPath()).selectLine(line.intValue()); | |||
this.primaryRange = ((InputFile) primaryComponent).selectLine(line.intValue()); | |||
} | |||
return this; | |||
} else { | |||
@@ -68,6 +66,7 @@ public class DeprecatedIssueBuilderWrapper implements Issuable.IssueBuilder { | |||
@Override | |||
public IssueBuilder message(String message) { | |||
Preconditions.checkState(newIssue.primaryLocation() == null, "Do not use message() and at() for the same issue"); | |||
this.primaryMessage = message; | |||
return this; | |||
} | |||
@@ -77,9 +76,16 @@ public class DeprecatedIssueBuilderWrapper implements Issuable.IssueBuilder { | |||
return new DefaultIssueLocation(); | |||
} | |||
@Override | |||
public IssueBuilder at(NewIssueLocation primaryLocation) { | |||
Preconditions.checkState(primaryMessage == null && primaryRange == null, "Do not use message() or line() and at() for the same issue"); | |||
newIssue.at(primaryLocation); | |||
return this; | |||
} | |||
@Override | |||
public IssueBuilder addLocation(NewIssueLocation location) { | |||
locations.add(location); | |||
newIssue.addLocation(location); | |||
return this; | |||
} | |||
@@ -113,27 +119,16 @@ public class DeprecatedIssueBuilderWrapper implements Issuable.IssueBuilder { | |||
@Override | |||
public Issue build() { | |||
if (primaryMessage != null || primaryRange != null || locations.isEmpty()) { | |||
NewIssueLocation newLocation = newIssue.newLocation(); | |||
if (newIssue.primaryLocation() == null) { | |||
NewIssueLocation newLocation = newIssue.newLocation().on(primaryComponent); | |||
if (primaryMessage != null) { | |||
newLocation.message(primaryMessage); | |||
} | |||
if (primaryComponent.isProjectOrModule()) { | |||
newLocation.onProject(); | |||
} else if (primaryComponent.isFile()) { | |||
newLocation.onFile((InputFile) primaryComponent.inputPath()); | |||
if (primaryRange != null) { | |||
newLocation.at(primaryRange); | |||
} | |||
} else if (primaryComponent.isDir()) { | |||
newLocation.onDir((InputDir) primaryComponent.inputPath()); | |||
if (primaryComponent.isFile() && primaryRange != null) { | |||
newLocation.at(primaryRange); | |||
} | |||
newIssue.addLocation(newLocation); | |||
} | |||
for (NewIssueLocation issueLocation : locations) { | |||
newIssue.addLocation(issueLocation); | |||
newIssue.at(newLocation); | |||
} | |||
return new DeprecatedIssueWrapper(newIssue); | |||
} | |||
@@ -45,7 +45,7 @@ public class DeprecatedIssueWrapper implements Issue { | |||
@Override | |||
public String key() { | |||
return newIssue.key(); | |||
return null; | |||
} | |||
@Override |
@@ -19,34 +19,48 @@ | |||
*/ | |||
package org.sonar.batch.issue; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.sonar.api.batch.BatchSide; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.api.issue.IssueComment; | |||
import org.sonar.api.issue.batch.IssueFilter; | |||
import org.sonar.api.resources.Project; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.Duration; | |||
import org.sonar.api.utils.KeyValueFormat; | |||
import org.sonar.batch.protocol.output.BatchReport; | |||
@BatchSide | |||
public class IssueFilters { | |||
private final org.sonar.api.issue.IssueFilter[] exclusionFilters; | |||
private final IssueFilter[] filters; | |||
private final Project project; | |||
public IssueFilters(org.sonar.api.issue.IssueFilter[] exclusionFilters, IssueFilter[] filters) { | |||
public IssueFilters(Project project, org.sonar.api.issue.IssueFilter[] exclusionFilters, IssueFilter[] filters) { | |||
this.project = project; | |||
this.exclusionFilters = exclusionFilters; | |||
this.filters = filters; | |||
} | |||
public IssueFilters(org.sonar.api.issue.IssueFilter[] exclusionFilters) { | |||
this(exclusionFilters, new IssueFilter[0]); | |||
public IssueFilters(Project project, org.sonar.api.issue.IssueFilter[] exclusionFilters) { | |||
this(project, exclusionFilters, new IssueFilter[0]); | |||
} | |||
public IssueFilters(IssueFilter[] filters) { | |||
this(new org.sonar.api.issue.IssueFilter[0], filters); | |||
public IssueFilters(Project project, IssueFilter[] filters) { | |||
this(project, new org.sonar.api.issue.IssueFilter[0], filters); | |||
} | |||
public IssueFilters() { | |||
this(new org.sonar.api.issue.IssueFilter[0], new IssueFilter[0]); | |||
public IssueFilters(Project project) { | |||
this(project, new org.sonar.api.issue.IssueFilter[0], new IssueFilter[0]); | |||
} | |||
public boolean accept(Issue issue) { | |||
public boolean accept(String componentKey, BatchReport.Issue rawIssue) { | |||
Issue issue = toIssueForIssueFilter(componentKey, rawIssue); | |||
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) { | |||
@@ -59,4 +73,145 @@ public class IssueFilters { | |||
return false; | |||
} | |||
} | |||
private Issue toIssueForIssueFilter(final String componentKey, final BatchReport.Issue rawIssue) { | |||
return new Issue() { | |||
@Override | |||
public String key() { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public String componentKey() { | |||
return componentKey; | |||
} | |||
@Override | |||
public RuleKey ruleKey() { | |||
return RuleKey.of(rawIssue.getRuleRepository(), rawIssue.getRuleKey()); | |||
} | |||
@Override | |||
public String language() { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public String severity() { | |||
return rawIssue.getSeverity().name(); | |||
} | |||
@Override | |||
public String message() { | |||
return rawIssue.getMsg(); | |||
} | |||
@Override | |||
public Integer line() { | |||
return rawIssue.hasLine() ? rawIssue.getLine() : null; | |||
} | |||
@Override | |||
public Double effortToFix() { | |||
return rawIssue.hasEffortToFix() ? rawIssue.getEffortToFix() : null; | |||
} | |||
@Override | |||
public String status() { | |||
return Issue.STATUS_OPEN; | |||
} | |||
@Override | |||
public String resolution() { | |||
return null; | |||
} | |||
@Override | |||
public String reporter() { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public String assignee() { | |||
return null; | |||
} | |||
@Override | |||
public Date creationDate() { | |||
return project.getAnalysisDate(); | |||
} | |||
@Override | |||
public Date updateDate() { | |||
return null; | |||
} | |||
@Override | |||
public Date closeDate() { | |||
return null; | |||
} | |||
@Override | |||
public String attribute(String key) { | |||
return attributes().get(key); | |||
} | |||
@Override | |||
public Map<String, String> attributes() { | |||
return rawIssue.hasAttributes() ? KeyValueFormat.parse(rawIssue.getAttributes()) : Collections.<String, String>emptyMap(); | |||
} | |||
@Override | |||
public String authorLogin() { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public String actionPlanKey() { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public List<IssueComment> comments() { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public boolean isNew() { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public Duration debt() { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public String projectKey() { | |||
return project.getEffectiveKey(); | |||
} | |||
@Override | |||
public String projectUuid() { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public String componentUuid() { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public Collection<String> tags() { | |||
throw unsupported(); | |||
} | |||
private UnsupportedOperationException unsupported() { | |||
return new UnsupportedOperationException("Not available for issues filters"); | |||
} | |||
}; | |||
} | |||
} |
@@ -20,25 +20,24 @@ | |||
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.fs.internal.DefaultInputComponent; | |||
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.batch.sensor.issue.Issue.ExecutionFlow; | |||
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.Constants.Severity; | |||
import org.sonar.batch.protocol.output.BatchReport; | |||
import org.sonar.batch.protocol.output.BatchReport.IssueLocation; | |||
import org.sonar.batch.protocol.output.BatchReport.IssueLocation.Builder; | |||
import org.sonar.batch.report.ReportPublisher; | |||
import org.sonar.core.issue.DefaultIssue; | |||
/** | |||
* Initialize the issues raised during scan. | |||
@@ -47,108 +46,126 @@ public class ModuleIssues { | |||
private final ActiveRules activeRules; | |||
private final Rules rules; | |||
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(); | |||
private final Builder locationBuilder = IssueLocation.newBuilder(); | |||
private final org.sonar.batch.protocol.output.BatchReport.TextRange.Builder textRangeBuilder = org.sonar.batch.protocol.output.BatchReport.TextRange.newBuilder(); | |||
private final BatchReport.ExecutionFlow.Builder flowBuilder = BatchReport.ExecutionFlow.newBuilder(); | |||
public ModuleIssues(ActiveRules activeRules, Rules rules, Project project, IssueFilters filters, ReportPublisher reportPublisher, BatchComponentCache componentCache) { | |||
public ModuleIssues(ActiveRules activeRules, Rules rules, IssueFilters filters, ReportPublisher reportPublisher, BatchComponentCache componentCache) { | |||
this.activeRules = activeRules; | |||
this.rules = rules; | |||
this.project = project; | |||
this.filters = filters; | |||
this.reportPublisher = reportPublisher; | |||
this.componentCache = componentCache; | |||
} | |||
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(defaultIssue, rule); | |||
ActiveRule activeRule = activeRules.find(ruleKey); | |||
String key = ((DefaultInputComponent) issue.primaryLocation().inputComponent()).key(); | |||
BatchComponent component = componentCache.get(key); | |||
Rule rule = validateRule(issue); | |||
ActiveRule activeRule = activeRules.find(issue.ruleKey()); | |||
if (activeRule == null) { | |||
// rule does not exist or is not enabled -> ignore the issue | |||
return false; | |||
} | |||
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)); | |||
} | |||
String primaryMessage = Strings.isNullOrEmpty(issue.primaryLocation().message()) ? rule.name() : issue.primaryLocation().message(); | |||
org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity(); | |||
Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRule.severity()); | |||
private static BatchReport.Issue toReportIssue(BatchReport.Issue.Builder builder, DefaultIssue issue) { | |||
builder.clear(); | |||
locationBuilder.clear(); | |||
// non-null fields | |||
builder.setSeverity(Constants.Severity.valueOf(issue.severity())); | |||
builder.setSeverity(severity); | |||
builder.setRuleRepository(issue.ruleKey().repository()); | |||
builder.setRuleKey(issue.ruleKey().rule()); | |||
builder.setAttributes(KeyValueFormat.format(issue.attributes())); | |||
builder.setMsg(primaryMessage); | |||
locationBuilder.setMsg(primaryMessage); | |||
// nullable fields | |||
Integer line = issue.line(); | |||
if (line != null) { | |||
builder.setLine(line); | |||
} | |||
String message = issue.message(); | |||
if (message != null) { | |||
builder.setMsg(message); | |||
locationBuilder.setComponentRef(component.batchId()); | |||
TextRange primaryTextRange = issue.primaryLocation().textRange(); | |||
applyTextRange(primaryTextRange); | |||
if (primaryTextRange != null) { | |||
builder.setLine(primaryTextRange.start().line()); | |||
} | |||
builder.setPrimaryLocation(locationBuilder.build()); | |||
Double effortToFix = issue.effortToFix(); | |||
if (effortToFix != null) { | |||
builder.setEffortToFix(effortToFix); | |||
} | |||
return builder.build(); | |||
applyAdditionalLocations(issue); | |||
applyExecutionFlows(issue); | |||
BatchReport.Issue rawIssue = builder.build(); | |||
if (filters.accept(key, rawIssue)) { | |||
write(component, rawIssue); | |||
return true; | |||
} | |||
return false; | |||
} | |||
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 void applyAdditionalLocations(Issue issue) { | |||
for (org.sonar.api.batch.sensor.issue.IssueLocation additionalLocation : issue.locations()) { | |||
locationBuilder.clear(); | |||
String locationComponentKey = ((DefaultInputComponent) additionalLocation.inputComponent()).key(); | |||
locationBuilder.setComponentRef(componentCache.get(locationComponentKey).batchId()); | |||
String message = additionalLocation.message(); | |||
if (message != null) { | |||
locationBuilder.setMsg(message); | |||
} | |||
applyTextRange(additionalLocation.textRange()); | |||
builder.addAdditionalLocation(locationBuilder.build()); | |||
} | |||
} | |||
private void applyExecutionFlows(Issue issue) { | |||
for (ExecutionFlow executionFlow : issue.executionFlows()) { | |||
flowBuilder.clear(); | |||
for (org.sonar.api.batch.sensor.issue.IssueLocation location : executionFlow.locations()) { | |||
locationBuilder.clear(); | |||
String locationComponentKey = ((DefaultInputComponent) location.inputComponent()).key(); | |||
locationBuilder.setComponentRef(componentCache.get(locationComponentKey).batchId()); | |||
String message = location.message(); | |||
if (message != null) { | |||
locationBuilder.setMsg(message); | |||
} | |||
applyTextRange(location.textRange()); | |||
flowBuilder.addLocation(locationBuilder.build()); | |||
} | |||
builder.addExecutionFlow(flowBuilder.build()); | |||
} | |||
} | |||
private void applyTextRange(TextRange primaryTextRange) { | |||
if (primaryTextRange != null) { | |||
textRangeBuilder.clear(); | |||
textRangeBuilder.setStartLine(primaryTextRange.start().line()); | |||
textRangeBuilder.setStartOffset(primaryTextRange.start().lineOffset()); | |||
textRangeBuilder.setEndLine(primaryTextRange.end().line()); | |||
textRangeBuilder.setEndOffset(primaryTextRange.end().lineOffset()); | |||
locationBuilder.setTextRange(textRangeBuilder.build()); | |||
} | |||
} | |||
private static void validateRule(DefaultIssue issue, @Nullable Rule rule) { | |||
private Rule validateRule(Issue issue) { | |||
RuleKey ruleKey = issue.ruleKey(); | |||
Rule rule = rules.find(ruleKey); | |||
if (rule == null) { | |||
throw MessageException.of(String.format("The rule '%s' does not exist.", ruleKey)); | |||
} | |||
if (Strings.isNullOrEmpty(rule.name()) && Strings.isNullOrEmpty(issue.message())) { | |||
if (Strings.isNullOrEmpty(rule.name()) && Strings.isNullOrEmpty(issue.primaryLocation().message())) { | |||
throw MessageException.of(String.format("The rule '%s' has no name and the related issue has no message.", ruleKey)); | |||
} | |||
return rule; | |||
} | |||
private void updateIssue(DefaultIssue issue, @Nullable Rule rule, ActiveRule activeRule) { | |||
if (Strings.isNullOrEmpty(issue.message())) { | |||
issue.setMessage(rule.name()); | |||
} | |||
if (project != null) { | |||
issue.setCreationDate(project.getAnalysisDate()); | |||
issue.setUpdateDate(project.getAnalysisDate()); | |||
} | |||
if (issue.severity() == null) { | |||
issue.setSeverity(activeRule.severity()); | |||
} | |||
public void write(BatchComponent component, BatchReport.Issue rawIssue) { | |||
reportPublisher.getWriter().appendComponentIssue(component.batchId(), rawIssue); | |||
} | |||
} |
@@ -48,7 +48,6 @@ 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; | |||
@@ -68,7 +67,6 @@ public class LocalIssueTracking { | |||
private final IssueUpdater updater; | |||
private final IssueChangeContext changeContext; | |||
private final ActiveRules activeRules; | |||
private final InputPathCache inputPathCache; | |||
private final BatchComponentCache componentCache; | |||
private final ServerIssueRepository serverIssueRepository; | |||
private final ProjectRepositories projectRepositories; | |||
@@ -78,7 +76,7 @@ public class LocalIssueTracking { | |||
public LocalIssueTracking(BatchComponentCache resourceCache, IssueCache issueCache, IssueTracking tracking, | |||
ServerLineHashesLoader lastLineHashes, IssueWorkflow workflow, IssueUpdater updater, | |||
ActiveRules activeRules, InputPathCache inputPathCache, ServerIssueRepository serverIssueRepository, | |||
ActiveRules activeRules, ServerIssueRepository serverIssueRepository, | |||
ProjectRepositories projectRepositories, AnalysisMode analysisMode, ReportPublisher reportPublisher) { | |||
this.componentCache = resourceCache; | |||
this.issueCache = issueCache; | |||
@@ -86,7 +84,6 @@ public class LocalIssueTracking { | |||
this.lastLineHashes = lastLineHashes; | |||
this.workflow = workflow; | |||
this.updater = updater; | |||
this.inputPathCache = inputPathCache; | |||
this.serverIssueRepository = serverIssueRepository; | |||
this.projectRepositories = projectRepositories; | |||
this.analysisMode = analysisMode; | |||
@@ -183,7 +180,7 @@ public class LocalIssueTracking { | |||
private SourceHashHolder loadSourceHashes(BatchComponent component) { | |||
SourceHashHolder sourceHashHolder = null; | |||
if (component.isFile()) { | |||
DefaultInputFile file = (DefaultInputFile) inputPathCache.getInputPath(component); | |||
DefaultInputFile file = (DefaultInputFile) component.inputComponent(); | |||
if (file == null) { | |||
throw new IllegalStateException("Resource " + component.resource() + " was not found in InputPath cache"); | |||
} |
@@ -99,7 +99,7 @@ public class ServerIssueRepository { | |||
if (!component.isFile()) { | |||
throw new UnsupportedOperationException("Incremental mode should only get issues on files"); | |||
} | |||
InputFile inputFile = (InputFile) inputPathCache.getInputPath(component); | |||
InputFile inputFile = (InputFile) component.inputComponent(); | |||
if (inputFile.status() == Status.ADDED) { | |||
return Collections.emptyList(); | |||
} |
@@ -140,7 +140,7 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver { | |||
return result; | |||
} | |||
private String key(InputPath inputPath) { | |||
private static String key(InputPath inputPath) { | |||
return inputPath instanceof InputFile ? ((DefaultInputFile) inputPath).key() : ((DefaultInputDir) inputPath).key(); | |||
} | |||
@@ -22,19 +22,18 @@ package org.sonar.batch.postjob; | |||
import com.google.common.base.Function; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.collect.Iterables; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.batch.AnalysisMode; | |||
import org.sonar.api.batch.fs.InputPath; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.batch.postjob.PostJobContext; | |||
import org.sonar.api.batch.postjob.issue.Issue; | |||
import org.sonar.api.batch.rule.Severity; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.batch.index.BatchComponent; | |||
import org.sonar.batch.index.BatchComponentCache; | |||
import org.sonar.batch.issue.IssueCache; | |||
import javax.annotation.Nullable; | |||
import org.sonar.core.issue.DefaultIssue; | |||
public class DefaultPostJobContext implements PostJobContext { | |||
@@ -104,9 +103,9 @@ public class DefaultPostJobContext implements PostJobContext { | |||
} | |||
@Override | |||
public InputPath inputPath() { | |||
public InputComponent inputComponent() { | |||
BatchComponent component = resourceCache.get(wrapped.componentKey()); | |||
return component != null ? component.inputPath() : null; | |||
return component != null ? component.inputComponent() : null; | |||
} | |||
@Override |
@@ -74,7 +74,7 @@ public class ComponentsPublisher implements ReportPublisherStep { | |||
if (batchComponent.isFile()) { | |||
builder.setIsTest(ResourceUtils.isUnitTestFile(r)); | |||
builder.setLines(((InputFile) batchComponent.inputPath()).lines()); | |||
builder.setLines(((InputFile) batchComponent.inputComponent()).lines()); | |||
} | |||
String name = getName(r); | |||
if (name != null) { |
@@ -54,42 +54,48 @@ public class CoveragePublisher implements ReportPublisherStep { | |||
} | |||
Map<Integer, Coverage.Builder> coveragePerLine = new LinkedHashMap<>(); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine, new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setUtHits(Integer.parseInt(value) > 0); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.CONDITIONS_BY_LINE_KEY, coveragePerLine, new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setConditions(Integer.parseInt(value)); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setUtCoveredConditions(Integer.parseInt(value)); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine, new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setItHits(Integer.parseInt(value) > 0); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setItCoveredConditions(Integer.parseInt(value)); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputPath()).lines(), CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setOverallCoveredConditions(Integer.parseInt(value)); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine, | |||
new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setUtHits(Integer.parseInt(value) > 0); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.CONDITIONS_BY_LINE_KEY, coveragePerLine, | |||
new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setConditions(Integer.parseInt(value)); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, | |||
new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setUtCoveredConditions(Integer.parseInt(value)); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine, | |||
new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setItHits(Integer.parseInt(value) > 0); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, | |||
new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setItCoveredConditions(Integer.parseInt(value)); | |||
} | |||
}); | |||
applyLineMeasure(resource.key(), ((InputFile) resource.inputComponent()).lines(), CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, | |||
new MeasureOperation() { | |||
@Override | |||
public void apply(String value, Coverage.Builder builder) { | |||
builder.setOverallCoveredConditions(Integer.parseInt(value)); | |||
} | |||
}); | |||
writer.writeComponentCoverage(resource.batchId(), Iterables.transform(coveragePerLine.values(), BuildCoverage.INSTANCE)); | |||
} | |||
@@ -122,7 +128,7 @@ public class CoveragePublisher implements ReportPublisherStep { | |||
void apply(String value, Coverage.Builder builder); | |||
} | |||
private enum BuildCoverage implements Function<Coverage.Builder, Coverage>{ | |||
private enum BuildCoverage implements Function<Coverage.Builder, Coverage> { | |||
INSTANCE; | |||
@Override |
@@ -50,7 +50,7 @@ public class SourcePublisher implements ReportPublisherStep { | |||
continue; | |||
} | |||
DefaultInputFile inputFile = (DefaultInputFile) resource.inputPath(); | |||
DefaultInputFile inputFile = (DefaultInputFile) resource.inputComponent(); | |||
File iofile = writer.getSourceFile(resource.batchId()); | |||
int line = 0; | |||
try (FileOutputStream output = new FileOutputStream(iofile); BOMInputStream bomIn = new BOMInputStream(new FileInputStream(inputFile.file()), |
@@ -119,7 +119,7 @@ public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { | |||
continue; | |||
} | |||
DefaultInputFile inputFile = (DefaultInputFile) component.inputPath(); | |||
DefaultInputFile inputFile = (DefaultInputFile) component.inputComponent(); | |||
if (inputFile.type() != Type.TEST) { | |||
continue; | |||
} |
@@ -59,10 +59,10 @@ public class ComponentIndexer { | |||
Resource sonarFile = File.create(inputFile.relativePath(), languages.get(languageKey), unitTest); | |||
sonarIndex.index(sonarFile); | |||
BatchComponent file = componentCache.get(sonarFile); | |||
file.setInputPath(inputFile); | |||
file.setInputComponent(inputFile); | |||
Resource sonarDir = file.parent().resource(); | |||
InputDir inputDir = fs.inputDir(inputFile.file().getParentFile()); | |||
componentCache.get(sonarDir).setInputPath(inputDir); | |||
componentCache.get(sonarDir).setInputComponent(inputDir); | |||
} | |||
} | |||
} |
@@ -21,20 +21,16 @@ package org.sonar.batch.scan.filesystem; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.Iterables; | |||
import org.sonar.api.batch.BatchSide; | |||
import org.sonar.api.batch.fs.InputDir; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.InputPath; | |||
import org.sonar.batch.index.BatchComponent; | |||
import javax.annotation.CheckForNull; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import java.util.SortedMap; | |||
import java.util.TreeMap; | |||
import javax.annotation.CheckForNull; | |||
import org.sonar.api.batch.BatchSide; | |||
import org.sonar.api.batch.fs.InputDir; | |||
import org.sonar.api.batch.fs.InputFile; | |||
/** | |||
* Cache of all files and dirs. This cache is shared amongst all project modules. Inclusion and | |||
@@ -130,14 +126,4 @@ public class InputPathCache { | |||
return null; | |||
} | |||
@CheckForNull | |||
public InputPath getInputPath(BatchComponent component) { | |||
if (component.isFile()) { | |||
return getFile(component.parent().parent().resource().getEffectiveKey(), component.resource().getPath()); | |||
} else if (component.isDir()) { | |||
return getDir(component.parent().parent().resource().getEffectiveKey(), component.resource().getPath()); | |||
} | |||
return null; | |||
} | |||
} |
@@ -19,6 +19,10 @@ | |||
*/ | |||
package org.sonar.batch.scan.report; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.commons.lang.StringEscapeUtils; | |||
import org.slf4j.Logger; | |||
@@ -29,11 +33,6 @@ import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.batch.index.BatchComponent; | |||
import org.sonar.batch.scan.filesystem.InputPathCache; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.List; | |||
@BatchSide | |||
public class SourceProvider { | |||
@@ -52,7 +51,7 @@ public class SourceProvider { | |||
return Collections.emptyList(); | |||
} | |||
try { | |||
InputFile inputFile = (InputFile) inputPathCache.getInputPath(component); | |||
InputFile inputFile = (InputFile) component.inputComponent(); | |||
List<String> lines = FileUtils.readLines(inputFile.file(), fs.encoding()); | |||
List<String> escapedLines = new ArrayList<>(lines.size()); | |||
for (String line : lines) { |
@@ -40,7 +40,7 @@ public class HighlightableBuilder extends PerspectiveBuilder<Highlightable> { | |||
@Override | |||
public Highlightable loadPerspective(Class<Highlightable> perspectiveClass, BatchComponent component) { | |||
if (component.isFile()) { | |||
InputFile path = (InputFile) component.inputPath(); | |||
InputFile path = (InputFile) component.inputComponent(); | |||
return new DefaultHighlightable((DefaultInputFile) path, sensorStorage); | |||
} | |||
return null; |
@@ -41,7 +41,7 @@ public class SymbolizableBuilder extends PerspectiveBuilder<Symbolizable> { | |||
@Override | |||
public Symbolizable loadPerspective(Class<Symbolizable> perspectiveClass, BatchComponent component) { | |||
if (component.isFile()) { | |||
InputFile path = (InputFile) component.inputPath(); | |||
InputFile path = (InputFile) component.inputComponent(); | |||
return new DefaultSymbolizable((DefaultInputFile) path, sensorStorage); | |||
} | |||
return null; |
@@ -40,7 +40,7 @@ public class TestPlanBuilder extends PerspectiveBuilder<MutableTestPlan> { | |||
@Override | |||
public MutableTestPlan loadPerspective(Class<MutableTestPlan> perspectiveClass, BatchComponent component) { | |||
if (component.isFile()) { | |||
InputFile inputFile = (InputFile) component.inputPath(); | |||
InputFile inputFile = (InputFile) component.inputComponent(); | |||
if (inputFile.type() == Type.TEST) { | |||
if (!testPlanByFile.containsKey(inputFile)) { | |||
testPlanByFile.put(inputFile, new DefaultTestPlan()); |
@@ -37,7 +37,7 @@ public class TestableBuilder extends PerspectiveBuilder<MutableTestable> { | |||
@Override | |||
public MutableTestable loadPerspective(Class<MutableTestable> perspectiveClass, BatchComponent component) { | |||
if (component.isFile()) { | |||
InputFile inputFile = (InputFile) component.inputPath(); | |||
InputFile inputFile = (InputFile) component.inputComponent(); | |||
if (inputFile.type() == Type.MAIN) { | |||
return new DefaultTestable((DefaultInputFile) inputFile); | |||
} |
@@ -21,7 +21,8 @@ package org.sonar.batch.issue; | |||
import org.junit.Test; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.api.resources.Project; | |||
import org.sonar.batch.protocol.output.BatchReport; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Matchers.any; | |||
@@ -38,19 +39,19 @@ public class IssueFiltersTest { | |||
org.sonar.api.issue.IssueFilter ko = mock(org.sonar.api.issue.IssueFilter.class); | |||
when(ko.accept(any(Issue.class))).thenReturn(false); | |||
IssueFilters filters = new IssueFilters(new org.sonar.api.issue.IssueFilter[] {ok, ko}); | |||
assertThat(filters.accept(new DefaultIssue())).isFalse(); | |||
IssueFilters filters = new IssueFilters(new Project("foo"), new org.sonar.api.issue.IssueFilter[] {ok, ko}); | |||
assertThat(filters.accept("foo:src/Foo.java", BatchReport.Issue.newBuilder().build())).isFalse(); | |||
filters = new IssueFilters(new org.sonar.api.issue.IssueFilter[] {ok}); | |||
assertThat(filters.accept(new DefaultIssue())).isTrue(); | |||
filters = new IssueFilters(new Project("foo"), new org.sonar.api.issue.IssueFilter[] {ok}); | |||
assertThat(filters.accept("foo:src/Foo.java", BatchReport.Issue.newBuilder().build())).isTrue(); | |||
filters = new IssueFilters(new org.sonar.api.issue.IssueFilter[] {ko}); | |||
assertThat(filters.accept(new DefaultIssue())).isFalse(); | |||
filters = new IssueFilters(new Project("foo"), new org.sonar.api.issue.IssueFilter[] {ko}); | |||
assertThat(filters.accept("foo:src/Foo.java", BatchReport.Issue.newBuilder().build())).isFalse(); | |||
} | |||
@Test | |||
public void should_always_accept_if_no_filters() { | |||
IssueFilters filters = new IssueFilters(); | |||
assertThat(filters.accept(new DefaultIssue())).isTrue(); | |||
IssueFilters filters = new IssueFilters(new Project("foo")); | |||
assertThat(filters.accept("foo:src/Foo.java", BatchReport.Issue.newBuilder().build())).isTrue(); | |||
} | |||
} |
@@ -20,7 +20,6 @@ | |||
package org.sonar.batch.issue; | |||
import java.io.StringReader; | |||
import java.util.Date; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.junit.runner.RunWith; | |||
@@ -35,7 +34,6 @@ 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; | |||
@@ -46,6 +44,7 @@ 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.anyString; | |||
import static org.mockito.Matchers.eq; | |||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS; | |||
import static org.mockito.Mockito.mock; | |||
@@ -59,8 +58,6 @@ public class ModuleIssuesTest { | |||
static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle"); | |||
static final String SQUID_RULE_NAME = "Avoid Cycle"; | |||
Project project = new Project("foo").setAnalysisDate(new Date()); | |||
@Mock | |||
IssueFilters filters; | |||
@@ -75,14 +72,14 @@ public class ModuleIssuesTest { | |||
@Before | |||
public void prepare() { | |||
componentCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputPath(file); | |||
componentCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputComponent(file); | |||
} | |||
@Test | |||
public void fail_on_unknown_rule() { | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() | |||
.addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo")) | |||
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) | |||
.forRule(SQUID_RULE_KEY); | |||
try { | |||
moduleIssues.initAndAddIssue(issue); | |||
@@ -99,7 +96,7 @@ public class ModuleIssuesTest { | |||
ruleBuilder.add(SQUID_RULE_KEY).setInternalKey(SQUID_RULE_KEY.rule()); | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() | |||
.addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("")) | |||
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("")) | |||
.forRule(SQUID_RULE_KEY); | |||
try { | |||
moduleIssues.initAndAddIssue(issue); | |||
@@ -116,7 +113,7 @@ public class ModuleIssuesTest { | |||
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() | |||
.addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo")) | |||
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) | |||
.forRule(SQUID_RULE_KEY); | |||
boolean added = moduleIssues.initAndAddIssue(issue); | |||
@@ -131,7 +128,7 @@ public class ModuleIssuesTest { | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() | |||
.addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo")) | |||
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) | |||
.forRule(SQUID_RULE_KEY); | |||
boolean added = moduleIssues.initAndAddIssue(issue); | |||
@@ -146,11 +143,11 @@ public class ModuleIssuesTest { | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() | |||
.addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo")) | |||
.at(new DefaultIssueLocation().on(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); | |||
when(filters.accept(anyString(), any(BatchReport.Issue.class))).thenReturn(true); | |||
boolean added = moduleIssues.initAndAddIssue(issue); | |||
@@ -167,9 +164,9 @@ public class ModuleIssuesTest { | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() | |||
.addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("Foo")) | |||
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) | |||
.forRule(SQUID_RULE_KEY); | |||
when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true); | |||
when(filters.accept(anyString(), any(BatchReport.Issue.class))).thenReturn(true); | |||
moduleIssues.initAndAddIssue(issue); | |||
ArgumentCaptor<BatchReport.Issue> argument = ArgumentCaptor.forClass(BatchReport.Issue.class); | |||
@@ -184,9 +181,9 @@ public class ModuleIssuesTest { | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() | |||
.addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("")) | |||
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("")) | |||
.forRule(SQUID_RULE_KEY); | |||
when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(true); | |||
when(filters.accept(anyString(), any(BatchReport.Issue.class))).thenReturn(true); | |||
boolean added = moduleIssues.initAndAddIssue(issue); | |||
@@ -203,10 +200,10 @@ public class ModuleIssuesTest { | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() | |||
.addLocation(new DefaultIssueLocation().onFile(file).at(file.selectLine(3)).message("")) | |||
.at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("")) | |||
.forRule(SQUID_RULE_KEY); | |||
when(filters.accept(any(org.sonar.core.issue.DefaultIssue.class))).thenReturn(false); | |||
when(filters.accept(anyString(), any(BatchReport.Issue.class))).thenReturn(false); | |||
boolean added = moduleIssues.initAndAddIssue(issue); | |||
@@ -218,7 +215,7 @@ public class ModuleIssuesTest { | |||
* Every rules and active rules has to be added in builders before creating ModuleIssues | |||
*/ | |||
private void initModuleIssues() { | |||
moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), project, filters, reportPublisher, componentCache); | |||
moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), filters, reportPublisher, componentCache); | |||
} | |||
} |
@@ -20,6 +20,7 @@ | |||
package org.sonar.batch.mediumtest.issues; | |||
import java.io.File; | |||
import java.util.List; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
@@ -29,6 +30,8 @@ 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.batch.protocol.output.BatchReport.IssueLocation; | |||
import org.sonar.xoo.XooPlugin; | |||
import org.sonar.xoo.rule.XooRulesDefinition; | |||
@@ -47,9 +50,19 @@ public class MultilineIssuesMediumTest { | |||
.activateRule(new ActiveRule("xoo", "MultilineIssue", null, "Multinile Issue", "MAJOR", null, "xoo")) | |||
.build(); | |||
private TaskResult result; | |||
@Before | |||
public void prepare() { | |||
public void prepare() throws Exception { | |||
tester.start(); | |||
File projectDir = new File(MultilineIssuesMediumTest.class.getResource("/mediumtest/xoo/sample-multiline").toURI()); | |||
File tmpDir = temp.newFolder(); | |||
FileUtils.copyDirectory(projectDir, tmpDir); | |||
result = tester | |||
.newScanTask(new File(tmpDir, "sonar-project.properties")) | |||
.start(); | |||
} | |||
@After | |||
@@ -59,16 +72,54 @@ public class MultilineIssuesMediumTest { | |||
@Test | |||
public void testIssueRange() throws Exception { | |||
File projectDir = new File(MultilineIssuesMediumTest.class.getResource("/mediumtest/xoo/sample-multiline").toURI()); | |||
File tmpDir = temp.newFolder(); | |||
FileUtils.copyDirectory(projectDir, tmpDir); | |||
List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Single.xoo")); | |||
assertThat(issues).hasSize(1); | |||
Issue issue = issues.get(0); | |||
assertThat(issue.getLine()).isEqualTo(6); | |||
assertThat(issue.getMsg()).isEqualTo("Primary location"); | |||
IssueLocation primaryLocation = issue.getPrimaryLocation(); | |||
assertThat(primaryLocation.getMsg()).isEqualTo("Primary location"); | |||
assertThat(primaryLocation.getTextRange().getStartLine()).isEqualTo(6); | |||
assertThat(primaryLocation.getTextRange().getStartOffset()).isEqualTo(25); | |||
assertThat(primaryLocation.getTextRange().getEndLine()).isEqualTo(6); | |||
assertThat(primaryLocation.getTextRange().getEndOffset()).isEqualTo(52); | |||
} | |||
TaskResult result = tester | |||
.newScanTask(new File(tmpDir, "sonar-project.properties")) | |||
.start(); | |||
@Test | |||
public void testMultilineIssueRange() throws Exception { | |||
List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Multiline.xoo")); | |||
assertThat(issues).hasSize(1); | |||
Issue issue = issues.get(0); | |||
assertThat(issue.getLine()).isEqualTo(6); | |||
assertThat(issue.getMsg()).isEqualTo("Primary location"); | |||
IssueLocation primaryLocation = issue.getPrimaryLocation(); | |||
assertThat(primaryLocation.getMsg()).isEqualTo("Primary location"); | |||
assertThat(primaryLocation.getTextRange().getStartLine()).isEqualTo(6); | |||
assertThat(primaryLocation.getTextRange().getStartOffset()).isEqualTo(25); | |||
assertThat(primaryLocation.getTextRange().getEndLine()).isEqualTo(7); | |||
assertThat(primaryLocation.getTextRange().getEndOffset()).isEqualTo(23); | |||
} | |||
assertThat(result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"))).hasSize(1); | |||
@Test | |||
public void testMultipleIssueLocation() throws Exception { | |||
List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/Multiple.xoo")); | |||
assertThat(issues).hasSize(1); | |||
Issue issue = issues.get(0); | |||
assertThat(issue.getLine()).isEqualTo(6); | |||
assertThat(issue.getMsg()).isEqualTo("Primary location"); | |||
IssueLocation primaryLocation = issue.getPrimaryLocation(); | |||
assertThat(primaryLocation.getMsg()).isEqualTo("Primary location"); | |||
assertThat(primaryLocation.getTextRange().getStartLine()).isEqualTo(6); | |||
assertThat(primaryLocation.getTextRange().getStartOffset()).isEqualTo(25); | |||
assertThat(primaryLocation.getTextRange().getEndLine()).isEqualTo(6); | |||
assertThat(primaryLocation.getTextRange().getEndOffset()).isEqualTo(52); | |||
assertThat(issue.getAdditionalLocationList()).hasSize(1); | |||
IssueLocation additionalLocation = issue.getAdditionalLocation(0); | |||
assertThat(additionalLocation.getMsg()).isEqualTo("Location #2"); | |||
assertThat(additionalLocation.getTextRange().getStartLine()).isEqualTo(7); | |||
assertThat(additionalLocation.getTextRange().getStartOffset()).isEqualTo(25); | |||
assertThat(additionalLocation.getTextRange().getEndLine()).isEqualTo(7); | |||
assertThat(additionalLocation.getTextRange().getEndOffset()).isEqualTo(52); | |||
} | |||
} |
@@ -247,8 +247,8 @@ public class PreviewAndReportsMediumTest { | |||
.setIssueListener(issueListener) | |||
.start(); | |||
assertThat(result.trackedIssues()).hasSize(14); | |||
assertThat(issueListener.issueList).hasSize(14); | |||
assertThat(result.trackedIssues()).hasSize(17); | |||
assertThat(issueListener.issueList).hasSize(17); | |||
assertThat(result.trackedIssues()).containsExactlyElementsOf(issueListener.issueList); | |||
} | |||
@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.batch.postjob; | |||
import java.util.Arrays; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.sonar.api.batch.AnalysisMode; | |||
@@ -26,12 +27,10 @@ import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.postjob.issue.Issue; | |||
import org.sonar.api.batch.rule.Severity; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.api.resources.File; | |||
import org.sonar.batch.index.BatchComponentCache; | |||
import org.sonar.batch.issue.IssueCache; | |||
import java.util.Arrays; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
@@ -77,11 +76,11 @@ public class DefaultPostJobContextTest { | |||
assertThat(issue.line()).isEqualTo(1); | |||
assertThat(issue.message()).isEqualTo("msg"); | |||
assertThat(issue.severity()).isEqualTo(Severity.BLOCKER); | |||
assertThat(issue.inputPath()).isNull(); | |||
assertThat(issue.inputComponent()).isNull(); | |||
InputFile inputPath = mock(InputFile.class); | |||
resourceCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputPath(inputPath); | |||
assertThat(issue.inputPath()).isEqualTo(inputPath); | |||
resourceCache.add(File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"), null).setInputComponent(inputPath); | |||
assertThat(issue.inputComponent()).isEqualTo(inputPath); | |||
} | |||
} |
@@ -25,7 +25,9 @@ import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | |||
import org.sonar.api.batch.fs.internal.DefaultInputDir; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.resources.Directory; | |||
import org.sonar.api.resources.Java; | |||
import org.sonar.api.resources.Project; | |||
@@ -55,33 +57,33 @@ public class ComponentsPublisherTest { | |||
Project root = new Project("foo").setName("Root project").setDescription("Root description") | |||
.setAnalysisDate(DateUtils.parseDate(("2012-12-12"))); | |||
root.setId(1).setUuid("PROJECT_UUID"); | |||
resourceCache.add(root, null); | |||
resourceCache.add(root, null).setInputComponent(new DefaultInputModule("foo")); | |||
Project module1 = new Project("module1").setName("Module1").setDescription("Module description"); | |||
module1.setParent(root); | |||
module1.setId(2).setUuid("MODULE_UUID"); | |||
resourceCache.add(module1, root); | |||
resourceCache.add(module1, root).setInputComponent(new DefaultInputModule("module1")); | |||
rootDef.addSubProject(ProjectDefinition.create().setKey("module1")); | |||
Directory dir = Directory.create("src"); | |||
dir.setEffectiveKey("module1:src"); | |||
dir.setId(3).setUuid("DIR_UUID"); | |||
resourceCache.add(dir, module1); | |||
resourceCache.add(dir, module1).setInputComponent(new DefaultInputDir("foo", "src")); | |||
org.sonar.api.resources.File file = org.sonar.api.resources.File.create("src/Foo.java", Java.INSTANCE, false); | |||
file.setEffectiveKey("module1:src/Foo.java"); | |||
file.setId(4).setUuid("FILE_UUID"); | |||
resourceCache.add(file, dir).setInputPath(new DefaultInputFile("module1", "src/Foo.java").setLines(2)); | |||
resourceCache.add(file, dir).setInputComponent(new DefaultInputFile("module1", "src/Foo.java").setLines(2)); | |||
org.sonar.api.resources.File fileWithoutLang = org.sonar.api.resources.File.create("src/make", null, false); | |||
fileWithoutLang.setEffectiveKey("module1:src/make"); | |||
fileWithoutLang.setId(5).setUuid("FILE_WITHOUT_LANG_UUID"); | |||
resourceCache.add(fileWithoutLang, dir).setInputPath(new DefaultInputFile("module1", "src/make").setLines(10)); | |||
resourceCache.add(fileWithoutLang, dir).setInputComponent(new DefaultInputFile("module1", "src/make").setLines(10)); | |||
org.sonar.api.resources.File testFile = org.sonar.api.resources.File.create("test/FooTest.java", Java.INSTANCE, true); | |||
testFile.setEffectiveKey("module1:test/FooTest.java"); | |||
testFile.setId(6).setUuid("TEST_FILE_UUID"); | |||
resourceCache.add(testFile, dir).setInputPath(new DefaultInputFile("module1", "test/FooTest.java").setLines(4)); | |||
resourceCache.add(testFile, dir).setInputComponent(new DefaultInputFile("module1", "test/FooTest.java").setLines(4)); | |||
ImmutableProjectReactor reactor = new ImmutableProjectReactor(rootDef); | |||
@@ -122,14 +124,14 @@ public class ComponentsPublisherTest { | |||
Project root = new Project("foo:my_branch").setName("Root project") | |||
.setAnalysisDate(DateUtils.parseDate(("2012-12-12"))); | |||
root.setId(1).setUuid("PROJECT_UUID"); | |||
resourceCache.add(root, null); | |||
resourceCache.add(root, null).setInputComponent(new DefaultInputModule("foo")); | |||
rootDef.properties().put(CoreProperties.LINKS_HOME_PAGE, "http://home"); | |||
rootDef.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "my_branch"); | |||
Project module1 = new Project("module1:my_branch").setName("Module1"); | |||
module1.setParent(root); | |||
module1.setId(2).setUuid("MODULE_UUID"); | |||
resourceCache.add(module1, root); | |||
resourceCache.add(module1, root).setInputComponent(new DefaultInputModule("module1")); | |||
ProjectDefinition moduleDef = ProjectDefinition.create().setKey("module1"); | |||
moduleDef.properties().put(CoreProperties.LINKS_CI, "http://ci"); | |||
rootDef.addSubProject(moduleDef); | |||
@@ -137,12 +139,12 @@ public class ComponentsPublisherTest { | |||
Directory dir = Directory.create("src"); | |||
dir.setEffectiveKey("module1:my_branch:my_branch:src"); | |||
dir.setId(3).setUuid("DIR_UUID"); | |||
resourceCache.add(dir, module1); | |||
resourceCache.add(dir, module1).setInputComponent(new DefaultInputDir("foo", "src")); | |||
org.sonar.api.resources.File file = org.sonar.api.resources.File.create("src/Foo.java", Java.INSTANCE, false); | |||
file.setEffectiveKey("module1:my_branch:my_branch:src/Foo.java"); | |||
file.setId(4).setUuid("FILE_UUID"); | |||
resourceCache.add(file, dir).setInputPath(new DefaultInputFile("module1", "src/Foo.java").setLines(2)); | |||
resourceCache.add(file, dir).setInputComponent(new DefaultInputFile("module1", "src/Foo.java").setLines(2)); | |||
ImmutableProjectReactor reactor = new ImmutableProjectReactor(rootDef); | |||
@@ -28,6 +28,7 @@ import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.measures.Measure; | |||
import org.sonar.api.resources.Project; | |||
@@ -58,8 +59,8 @@ public class CoveragePublisherTest { | |||
Project p = new Project("foo").setAnalysisDate(new Date(1234567L)); | |||
BatchComponentCache resourceCache = new BatchComponentCache(); | |||
sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); | |||
resourceCache.add(p, null); | |||
resourceCache.add(sampleFile, null).setInputPath(new DefaultInputFile("foo", "src/Foo.php").setLines(5)); | |||
resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo")); | |||
resourceCache.add(sampleFile, null).setInputComponent(new DefaultInputFile("foo", "src/Foo.php").setLines(5)); | |||
measureCache = mock(MeasureCache.class); | |||
when(measureCache.byMetric(anyString(), anyString())).thenReturn(Collections.<Measure>emptyList()); | |||
publisher = new CoveragePublisher(resourceCache, measureCache); |
@@ -19,10 +19,15 @@ | |||
*/ | |||
package org.sonar.batch.report; | |||
import java.io.File; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.sensor.duplication.Duplication; | |||
import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication; | |||
import org.sonar.api.resources.Project; | |||
@@ -31,11 +36,6 @@ import org.sonar.batch.index.BatchComponentCache; | |||
import org.sonar.batch.protocol.output.BatchReport; | |||
import org.sonar.batch.protocol.output.BatchReportReader; | |||
import org.sonar.batch.protocol.output.BatchReportWriter; | |||
import java.io.File; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import org.sonar.core.util.CloseableIterator; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -55,11 +55,11 @@ public class DuplicationsPublisherTest { | |||
public void prepare() { | |||
BatchComponentCache resourceCache = new BatchComponentCache(); | |||
Project p = new Project("foo"); | |||
resourceCache.add(p, null); | |||
resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo")); | |||
org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); | |||
resourceCache.add(sampleFile, null); | |||
resourceCache.add(sampleFile, null).setInputComponent(new DefaultInputFile("foo", "src/Foo.php").setLines(5)); | |||
org.sonar.api.resources.Resource sampleFile2 = org.sonar.api.resources.File.create("src/Foo2.php").setEffectiveKey("foo:src/Foo2.php"); | |||
resourceCache.add(sampleFile2, null); | |||
resourceCache.add(sampleFile2, null).setInputComponent(new DefaultInputFile("foo", "src/Foo2.php").setLines(5)); | |||
duplicationCache = mock(DuplicationCache.class); | |||
when(duplicationCache.byComponent(anyString())).thenReturn(Collections.<DefaultDuplication>emptyList()); | |||
publisher = new DuplicationsPublisher(resourceCache, duplicationCache); |
@@ -29,6 +29,7 @@ import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.resources.Project; | |||
import org.sonar.api.resources.Qualifiers; | |||
import org.sonar.batch.index.BatchComponentCache; | |||
@@ -55,10 +56,10 @@ public class SourcePublisherTest { | |||
BatchComponentCache resourceCache = new BatchComponentCache(); | |||
sampleFile = org.sonar.api.resources.File.create("src/Foo.php"); | |||
sampleFile.setEffectiveKey("foo:src/Foo.php"); | |||
resourceCache.add(p, null); | |||
resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo")); | |||
File baseDir = temp.newFolder(); | |||
sourceFile = new File(baseDir, "src/Foo.php"); | |||
resourceCache.add(sampleFile, null).setInputPath( | |||
resourceCache.add(sampleFile, null).setInputComponent( | |||
new DefaultInputFile("foo", "src/Foo.php").setLines(5).setModuleBaseDir(baseDir.toPath()).setCharset(StandardCharsets.ISO_8859_1)); | |||
publisher = new SourcePublisher(resourceCache); | |||
File outputDir = temp.newFolder(); |
@@ -106,7 +106,7 @@ public class DefaultSensorStorageTest { | |||
ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class); | |||
Resource sonarFile = File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); | |||
resourceCache.add(sonarFile, null).setInputPath(file); | |||
resourceCache.add(sonarFile, null).setInputComponent(file); | |||
when(measureCache.put(eq(sonarFile), argumentCaptor.capture())).thenReturn(null); | |||
sensorStorage.store(new DefaultMeasure() | |||
.onFile(file) |
@@ -20,6 +20,8 @@ | |||
package org.sonar.batch.source; | |||
import org.junit.Test; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.sensor.internal.SensorStorage; | |||
import org.sonar.api.resources.File; | |||
import org.sonar.api.resources.Project; | |||
@@ -35,7 +37,7 @@ public class HighlightableBuilderTest { | |||
@Test | |||
public void should_load_default_perspective() { | |||
Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c"); | |||
BatchComponent component = new BatchComponent(1, file, null); | |||
BatchComponent component = new BatchComponent(1, file, null).setInputComponent(new DefaultInputFile("foo", "foo.c")); | |||
HighlightableBuilder builder = new HighlightableBuilder(mock(SensorStorage.class)); | |||
Highlightable perspective = builder.loadPerspective(Highlightable.class, component); | |||
@@ -45,7 +47,7 @@ public class HighlightableBuilderTest { | |||
@Test | |||
public void project_should_not_be_highlightable() { | |||
BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null); | |||
BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null).setInputComponent(new DefaultInputModule("struts")); | |||
HighlightableBuilder builder = new HighlightableBuilder(mock(SensorStorage.class)); | |||
Highlightable perspective = builder.loadPerspective(Highlightable.class, component); |
@@ -21,6 +21,8 @@ | |||
package org.sonar.batch.source; | |||
import org.junit.Test; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.component.Perspective; | |||
import org.sonar.api.resources.File; | |||
import org.sonar.api.resources.Project; | |||
@@ -37,7 +39,7 @@ public class SymbolizableBuilderTest { | |||
@Test | |||
public void should_load_perspective() { | |||
Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c"); | |||
BatchComponent component = new BatchComponent(1, file, null); | |||
BatchComponent component = new BatchComponent(1, file, null).setInputComponent(new DefaultInputFile("foo", "foo.c")); | |||
SymbolizableBuilder perspectiveBuilder = new SymbolizableBuilder(mock(DefaultSensorStorage.class)); | |||
Perspective perspective = perspectiveBuilder.loadPerspective(Symbolizable.class, component); | |||
@@ -47,7 +49,7 @@ public class SymbolizableBuilderTest { | |||
@Test | |||
public void project_should_not_be_highlightable() { | |||
BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null); | |||
BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null).setInputComponent(new DefaultInputModule("struts")); | |||
SymbolizableBuilder builder = new SymbolizableBuilder(mock(DefaultSensorStorage.class)); | |||
Perspective perspective = builder.loadPerspective(Symbolizable.class, component); |
@@ -1,2 +0,0 @@ | |||
ncloc:3 | |||
complexity:1 |
@@ -0,0 +1,9 @@ | |||
package hello; | |||
public class HelloJava { | |||
public static void main(String[] args) { | |||
{xoo-start-issue:1:1}System.out | |||
.println("Hello"){xoo-end-issue:1:1}; | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
package hello; | |||
public class HelloJava { | |||
public static void main(String[] args) { | |||
{xoo-start-issue:1:1}System.out.println("Hello"){xoo-end-issue:1:1}; | |||
{xoo-start-issue:1:2}System.out.println("World"){xoo-end-issue:1:2}; | |||
} | |||
} |
@@ -85,6 +85,11 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public IssueBuilder at(NewIssueLocation location) { | |||
throw unsupported(); | |||
} | |||
@Override | |||
public IssueBuilder addLocation(NewIssueLocation location) { | |||
throw unsupported(); |
@@ -0,0 +1,36 @@ | |||
/* | |||
* 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.api.batch.fs; | |||
/** | |||
* Common interface for all input components. | |||
* | |||
* @since 5.2 | |||
* @see InputFile | |||
* @see InputDir | |||
*/ | |||
public interface InputComponent { | |||
/** | |||
* Is the component an {@link InputFile} | |||
*/ | |||
boolean isFile(); | |||
} |
@@ -0,0 +1,29 @@ | |||
/* | |||
* 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.api.batch.fs; | |||
/** | |||
* Used to create issues and measures on modules. | |||
* | |||
* @since 5.2 | |||
*/ | |||
public interface InputModule extends InputComponent { | |||
} |
@@ -20,7 +20,6 @@ | |||
package org.sonar.api.batch.fs; | |||
import java.io.File; | |||
import java.io.Serializable; | |||
import java.nio.file.Path; | |||
/** | |||
@@ -30,7 +29,7 @@ import java.nio.file.Path; | |||
* @see InputFile | |||
* @see InputDir | |||
*/ | |||
public interface InputPath extends Serializable { | |||
public interface InputPath extends InputComponent { | |||
/** | |||
* @see InputFile#relativePath() |
@@ -0,0 +1,53 @@ | |||
/* | |||
* 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.api.batch.fs.internal; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
/** | |||
* @since 5.2 | |||
*/ | |||
public abstract class DefaultInputComponent implements InputComponent { | |||
public abstract String key(); | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) { | |||
return true; | |||
} | |||
if (o == null || this.getClass() != o.getClass()) { | |||
return false; | |||
} | |||
DefaultInputComponent that = (DefaultInputComponent) o; | |||
return key().equals(that.key()); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return key().hashCode(); | |||
} | |||
@Override | |||
public String toString() { | |||
return "[key=" + key() + "]"; | |||
} | |||
} |
@@ -19,16 +19,15 @@ | |||
*/ | |||
package org.sonar.api.batch.fs.internal; | |||
import org.sonar.api.batch.fs.InputDir; | |||
import org.sonar.api.utils.PathUtils; | |||
import java.io.File; | |||
import java.nio.file.Path; | |||
import org.sonar.api.batch.fs.InputDir; | |||
import org.sonar.api.utils.PathUtils; | |||
/** | |||
* @since 4.5 | |||
*/ | |||
public class DefaultInputDir implements InputDir { | |||
public class DefaultInputDir extends DefaultInputComponent implements InputDir { | |||
private final String relativePath; | |||
private final String moduleKey; | |||
@@ -66,6 +65,7 @@ public class DefaultInputDir implements InputDir { | |||
return moduleKey; | |||
} | |||
@Override | |||
public String key() { | |||
return new StringBuilder().append(moduleKey).append(":").append(relativePath).toString(); | |||
} | |||
@@ -78,6 +78,11 @@ public class DefaultInputDir implements InputDir { | |||
return this; | |||
} | |||
@Override | |||
public boolean isFile() { | |||
return false; | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) { |
@@ -39,7 +39,7 @@ import org.sonar.api.utils.PathUtils; | |||
/** | |||
* @since 4.2 | |||
*/ | |||
public class DefaultInputFile implements InputFile, org.sonar.api.resources.InputFile { | |||
public class DefaultInputFile extends DefaultInputComponent implements InputFile, org.sonar.api.resources.InputFile { | |||
private final String relativePath; | |||
private final String moduleKey; | |||
@@ -114,6 +114,7 @@ public class DefaultInputFile implements InputFile, org.sonar.api.resources.Inpu | |||
/** | |||
* Component key. | |||
*/ | |||
@Override | |||
public String key() { | |||
return new StringBuilder().append(moduleKey).append(":").append(relativePath).toString(); | |||
} | |||
@@ -327,4 +328,9 @@ public class DefaultInputFile implements InputFile, org.sonar.api.resources.Inpu | |||
return new BufferedInputStream(new FileInputStream(file())); | |||
} | |||
@Override | |||
public boolean isFile() { | |||
return true; | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* 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.api.batch.fs.internal; | |||
import org.sonar.api.batch.fs.InputModule; | |||
/** | |||
* @since 5.2 | |||
*/ | |||
public class DefaultInputModule extends DefaultInputComponent implements InputModule { | |||
private final String moduleKey; | |||
public DefaultInputModule(String moduleKey) { | |||
this.moduleKey = moduleKey; | |||
} | |||
@Override | |||
public String key() { | |||
return moduleKey; | |||
} | |||
@Override | |||
public boolean isFile() { | |||
return false; | |||
} | |||
} |
@@ -20,12 +20,11 @@ | |||
package org.sonar.api.batch.postjob.issue; | |||
import com.google.common.annotations.Beta; | |||
import org.sonar.api.batch.fs.InputPath; | |||
import javax.annotation.CheckForNull; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.batch.rule.Severity; | |||
import org.sonar.api.rule.RuleKey; | |||
import javax.annotation.CheckForNull; | |||
/** | |||
* Represents an issue state at the end of the batch analysis. Only available after local issue tracking in preview mode. | |||
* | |||
@@ -50,10 +49,10 @@ public interface Issue { | |||
String componentKey(); | |||
/** | |||
* The {@link InputPath} this issue belongs to. Returns null if issue is global to the project or if component was deleted (for resolved issues). | |||
* The {@link InputComponent} this issue belongs to. Returns null if component was deleted (for resolved issues). | |||
*/ | |||
@CheckForNull | |||
InputPath inputPath(); | |||
InputComponent inputComponent(); | |||
/** | |||
* Line of the issue. Null for global issues and issues on directories. Can also be null |
@@ -21,6 +21,7 @@ package org.sonar.api.batch.sensor.issue; | |||
import com.google.common.annotations.Beta; | |||
import java.util.List; | |||
import java.util.Map; | |||
import javax.annotation.CheckForNull; | |||
import org.sonar.api.batch.rule.Severity; | |||
import org.sonar.api.batch.sensor.Sensor; | |||
@@ -59,7 +60,13 @@ public interface Issue { | |||
Severity overriddenSeverity(); | |||
/** | |||
* List of locations for this issue. Returns at least one location. | |||
* Primary locations for this issue. | |||
* @since 5.2 | |||
*/ | |||
IssueLocation primaryLocation(); | |||
/** | |||
* List of additional locations for this issue. | |||
* @since 5.2 | |||
*/ | |||
List<IssueLocation> locations(); | |||
@@ -70,4 +77,10 @@ public interface Issue { | |||
*/ | |||
List<ExecutionFlow> executionFlows(); | |||
/** | |||
* Key/value pair of attributes that are attached to the issue. | |||
* @since 5.2 | |||
*/ | |||
Map<String, String> attributes(); | |||
} |
@@ -21,7 +21,7 @@ package org.sonar.api.batch.sensor.issue; | |||
import com.google.common.annotations.Beta; | |||
import javax.annotation.CheckForNull; | |||
import org.sonar.api.batch.fs.InputPath; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.batch.fs.TextRange; | |||
/** | |||
@@ -33,10 +33,9 @@ import org.sonar.api.batch.fs.TextRange; | |||
public interface IssueLocation { | |||
/** | |||
* The {@link InputPath} this location belongs to. Returns null if location is global to the project. | |||
* The {@link InputComponent} this location belongs to. | |||
*/ | |||
@CheckForNull | |||
InputPath inputPath(); | |||
InputComponent inputComponent(); | |||
/** | |||
* Range of the issue. Null for global issues and issues on directories. Can also be null |
@@ -51,7 +51,13 @@ public interface NewIssue { | |||
/** | |||
* @since 5.2 | |||
* Register a new location for this issue. First registered location is considered as primary location. | |||
* Primary for this issue. | |||
*/ | |||
NewIssue at(NewIssueLocation primaryLocation); | |||
/** | |||
* @since 5.2 | |||
* Register an additional location for this issue. | |||
*/ | |||
NewIssue addLocation(NewIssueLocation location); | |||
@@ -67,6 +73,12 @@ public interface NewIssue { | |||
*/ | |||
NewIssueLocation newLocation(); | |||
/** | |||
* @since 5.2 | |||
* Attach a new attribute to the issue. Not used by SQ but can be reused later for integration needs (for example it is returned by WS). | |||
*/ | |||
NewIssue addAttribute(String key, String value); | |||
/** | |||
* Save the issue. If rule key is unknown or rule not enabled in the current quality profile then a warning is logged but no exception | |||
* is thrown. |
@@ -20,7 +20,7 @@ | |||
package org.sonar.api.batch.sensor.issue; | |||
import com.google.common.annotations.Beta; | |||
import org.sonar.api.batch.fs.InputDir; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.TextRange; | |||
@@ -38,22 +38,12 @@ public interface NewIssueLocation { | |||
int MESSAGE_MAX_SIZE = 4000; | |||
/** | |||
* The {@link InputFile} the issue location belongs to. For global issues call {@link #onProject()}. | |||
* The {@link InputComponent} the issue location belongs to. Mandatory. | |||
*/ | |||
NewIssueLocation onFile(InputFile file); | |||
NewIssueLocation on(InputComponent component); | |||
/** | |||
* The {@link InputDir} the issue location belongs to. For global issues call {@link #onProject()}. | |||
*/ | |||
NewIssueLocation onDir(InputDir inputDir); | |||
/** | |||
* Tell that the issue location is global to the project. | |||
*/ | |||
NewIssueLocation onProject(); | |||
/** | |||
* Position in the file. Only valid when {@link #onFile(InputFile)} has been called. | |||
* Position in the file. Only applicable when {@link #on(InputComponent)} has been called with an InputFile. | |||
* See {@link InputFile#newRange(org.sonar.api.batch.fs.TextPointer, org.sonar.api.batch.fs.TextPointer)} | |||
*/ | |||
NewIssueLocation at(TextRange location); |
@@ -21,11 +21,13 @@ package org.sonar.api.batch.sensor.issue.internal; | |||
import com.google.common.base.Function; | |||
import com.google.common.base.Preconditions; | |||
import com.google.common.base.Strings; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.ImmutableMap; | |||
import com.google.common.collect.Lists; | |||
import java.util.ArrayList; | |||
import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.batch.rule.Severity; | |||
import org.sonar.api.batch.sensor.internal.DefaultStorable; | |||
@@ -35,25 +37,23 @@ import org.sonar.api.batch.sensor.issue.IssueLocation; | |||
import org.sonar.api.batch.sensor.issue.NewIssue; | |||
import org.sonar.api.batch.sensor.issue.NewIssueLocation; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.internal.Uuids; | |||
public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { | |||
private String key; | |||
private RuleKey ruleKey; | |||
private Double effortToFix; | |||
private Severity overriddenSeverity; | |||
private IssueLocation primaryLocation; | |||
private List<IssueLocation> locations = new ArrayList<>(); | |||
private List<List<IssueLocation>> executionFlows = new ArrayList<>(); | |||
private final Map<String, String> attributes = new LinkedHashMap<>(); | |||
public DefaultIssue() { | |||
super(null); | |||
this.key = Uuids.create(); | |||
} | |||
public DefaultIssue(SensorStorage storage) { | |||
super(storage); | |||
this.key = Uuids.create(); | |||
} | |||
@Override | |||
@@ -79,6 +79,14 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { | |||
return new DefaultIssueLocation(); | |||
} | |||
@Override | |||
public DefaultIssue at(NewIssueLocation primaryLocation) { | |||
Preconditions.checkArgument(primaryLocation != null, "Cannot use a location that is null"); | |||
Preconditions.checkState(this.primaryLocation == null, "at() already called"); | |||
this.primaryLocation = (DefaultIssueLocation) primaryLocation; | |||
return this; | |||
} | |||
@Override | |||
public DefaultIssue addLocation(NewIssueLocation location) { | |||
locations.add((DefaultIssueLocation) location); | |||
@@ -95,6 +103,17 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { | |||
return null; | |||
} | |||
@Override | |||
public DefaultIssue addAttribute(String key, String value) { | |||
attributes.put(key, value); | |||
return this; | |||
} | |||
@Override | |||
public Map<String, String> attributes() { | |||
return ImmutableMap.copyOf(attributes); | |||
} | |||
@Override | |||
public RuleKey ruleKey() { | |||
return this.ruleKey; | |||
@@ -110,8 +129,9 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { | |||
return this.effortToFix; | |||
} | |||
public String key() { | |||
return this.key; | |||
@Override | |||
public IssueLocation primaryLocation() { | |||
return primaryLocation; | |||
} | |||
@Override | |||
@@ -137,34 +157,8 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { | |||
@Override | |||
public void doSave() { | |||
Preconditions.checkNotNull(this.ruleKey, "ruleKey is mandatory on issue"); | |||
Preconditions.checkState(!Strings.isNullOrEmpty(key), "Fail to generate issue key"); | |||
Preconditions.checkState(!locations.isEmpty(), "At least one location is mandatory on every issue"); | |||
Preconditions.checkState(primaryLocation != null, "Primary location is mandatory on every issue"); | |||
storage.store(this); | |||
} | |||
/** | |||
* For testing only. | |||
*/ | |||
public DefaultIssue withKey(String key) { | |||
this.key = key; | |||
return this; | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) { | |||
return true; | |||
} | |||
if (o == null || getClass() != o.getClass()) { | |||
return false; | |||
} | |||
DefaultIssue that = (DefaultIssue) o; | |||
return !key.equals(that.key); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return key.hashCode(); | |||
} | |||
} |
@@ -20,10 +20,7 @@ | |||
package org.sonar.api.batch.sensor.issue.internal; | |||
import com.google.common.base.Preconditions; | |||
import javax.annotation.CheckForNull; | |||
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.InputComponent; | |||
import org.sonar.api.batch.fs.TextRange; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.sensor.issue.IssueLocation; | |||
@@ -31,46 +28,23 @@ import org.sonar.api.batch.sensor.issue.NewIssueLocation; | |||
public class DefaultIssueLocation implements NewIssueLocation, IssueLocation { | |||
private static final String INPUT_DIR_SHOULD_BE_NON_NULL = "InputDir should be non null"; | |||
private static final String INPUT_FILE_SHOULD_BE_NON_NULL = "InputFile should be non null"; | |||
private static final String ON_FILE_OR_ON_DIR_ALREADY_CALLED = "onFile or onDir already called"; | |||
private static final String ON_PROJECT_ALREADY_CALLED = "onProject already called"; | |||
private boolean onProject = false; | |||
private InputPath path; | |||
private InputComponent component; | |||
private TextRange textRange; | |||
private String message; | |||
@Override | |||
public NewIssueLocation onFile(InputFile file) { | |||
Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); | |||
Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); | |||
Preconditions.checkNotNull(file, INPUT_FILE_SHOULD_BE_NON_NULL); | |||
this.path = file; | |||
return this; | |||
} | |||
@Override | |||
public NewIssueLocation onDir(InputDir dir) { | |||
Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); | |||
Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); | |||
Preconditions.checkNotNull(dir, INPUT_DIR_SHOULD_BE_NON_NULL); | |||
this.path = dir; | |||
return this; | |||
} | |||
@Override | |||
public NewIssueLocation onProject() { | |||
Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); | |||
Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); | |||
this.onProject = true; | |||
public NewIssueLocation on(InputComponent component) { | |||
Preconditions.checkArgument(component != null, "Component can't be null"); | |||
Preconditions.checkState(this.component == null, "on() already called"); | |||
this.component = component; | |||
return this; | |||
} | |||
@Override | |||
public NewIssueLocation at(TextRange location) { | |||
Preconditions.checkState(this.path != null && this.path instanceof InputFile, "at() should be called after onFile."); | |||
DefaultInputFile file = (DefaultInputFile) this.path; | |||
Preconditions.checkState(this.component != null, "at() should be called after on()"); | |||
Preconditions.checkState(this.component.isFile(), "at() should be called only for an InputFile."); | |||
DefaultInputFile file = (DefaultInputFile) this.component; | |||
file.validate(location); | |||
this.textRange = location; | |||
return this; | |||
@@ -86,9 +60,8 @@ public class DefaultIssueLocation implements NewIssueLocation, IssueLocation { | |||
} | |||
@Override | |||
@CheckForNull | |||
public InputPath inputPath() { | |||
return this.path; | |||
public InputComponent inputComponent() { | |||
return this.component; | |||
} | |||
@Override |
@@ -67,7 +67,7 @@ public interface Issuable extends Perspective { | |||
/** | |||
* Optional line index, starting from 1. It must not be zero or negative. | |||
* @deprecated since 5.2 use {@link #addLocation(NewIssueLocation)} | |||
* @deprecated since 5.2 use {@link #at(NewIssueLocation)} | |||
*/ | |||
@Deprecated | |||
IssueBuilder line(@Nullable Integer line); | |||
@@ -76,7 +76,7 @@ public interface Issuable extends Perspective { | |||
* Optional, but recommended, plain-text message. | |||
* <p/> | |||
* Formats like Markdown or HTML are not supported. Size must not be greater than {@link Issue#MESSAGE_MAX_SIZE} characters. | |||
* @deprecated since 5.2 use {@link #addLocation(NewIssueLocation)} | |||
* @deprecated since 5.2 use {@link #at(NewIssueLocation)} | |||
*/ | |||
@Deprecated | |||
IssueBuilder message(@Nullable String message); | |||
@@ -87,6 +87,12 @@ public interface Issuable extends Perspective { | |||
*/ | |||
NewIssueLocation newLocation(); | |||
/** | |||
* @since 5.2 | |||
* Register primary location for this issue. | |||
*/ | |||
IssueBuilder at(NewIssueLocation primaryLocation); | |||
/** | |||
* @since 5.2 | |||
* Register a new secondary location for this issue. |
@@ -89,12 +89,12 @@ public class SensorContextTesterTest { | |||
assertThat(tester.allIssues()).isEmpty(); | |||
NewIssue newIssue = tester.newIssue(); | |||
newIssue | |||
.addLocation(newIssue.newLocation().onFile(new DefaultInputFile("foo", "src/Foo.java"))) | |||
.at(newIssue.newLocation().on(new DefaultInputFile("foo", "src/Foo.java"))) | |||
.forRule(RuleKey.of("repo", "rule")) | |||
.save(); | |||
newIssue = tester.newIssue(); | |||
newIssue | |||
.addLocation(newIssue.newLocation().onFile(new DefaultInputFile("foo", "src/Foo.java"))) | |||
.at(newIssue.newLocation().on(new DefaultInputFile("foo", "src/Foo.java"))) | |||
.forRule(RuleKey.of("repo", "rule")) | |||
.save(); | |||
assertThat(tester.allIssues()).hasSize(2); |
@@ -35,19 +35,19 @@ public class DefaultIssueLocationTest { | |||
private DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php").initMetadata(new FileMetadata().readMetadata(new StringReader("Foo\nBar\n"))); | |||
@Test | |||
public void not_allowed_to_call_onFile_and_onProject() { | |||
public void not_allowed_to_call_on_twice() { | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("onProject already called"); | |||
thrown.expectMessage("on() already called"); | |||
new DefaultIssueLocation() | |||
.onProject() | |||
.onFile(inputFile) | |||
.on(inputFile) | |||
.on(inputFile) | |||
.message("Wrong way!"); | |||
} | |||
@Test | |||
public void prevent_too_long_messages() { | |||
new DefaultIssueLocation() | |||
.onFile(inputFile) | |||
.on(inputFile) | |||
.message(StringUtils.repeat("a", 4000)); | |||
thrown.expect(IllegalArgumentException.class); | |||
@@ -55,7 +55,7 @@ public class DefaultIssueLocationTest { | |||
thrown.expectMessage("aaa] size is 4001"); | |||
new DefaultIssueLocation() | |||
.onFile(inputFile) | |||
.on(inputFile) | |||
.message(StringUtils.repeat("a", 4001)); | |||
} |
@@ -23,6 +23,7 @@ import java.io.StringReader; | |||
import org.junit.Test; | |||
import org.sonar.api.batch.fs.internal.DefaultInputDir; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputModule; | |||
import org.sonar.api.batch.fs.internal.FileMetadata; | |||
import org.sonar.api.batch.rule.Severity; | |||
import org.sonar.api.batch.sensor.internal.SensorStorage; | |||
@@ -40,18 +41,18 @@ public class DefaultIssueTest { | |||
public void build_file_issue() { | |||
SensorStorage storage = mock(SensorStorage.class); | |||
DefaultIssue issue = new DefaultIssue(storage) | |||
.addLocation(new DefaultIssueLocation() | |||
.onFile(inputFile) | |||
.at(new DefaultIssueLocation() | |||
.on(inputFile) | |||
.at(inputFile.selectLine(1)) | |||
.message("Wrong way!")) | |||
.forRule(RuleKey.of("repo", "rule")) | |||
.effortToFix(10.0); | |||
assertThat(issue.locations().get(0).inputPath()).isEqualTo(inputFile); | |||
assertThat(issue.primaryLocation().inputComponent()).isEqualTo(inputFile); | |||
assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); | |||
assertThat(issue.locations().get(0).textRange().start().line()).isEqualTo(1); | |||
assertThat(issue.primaryLocation().textRange().start().line()).isEqualTo(1); | |||
assertThat(issue.effortToFix()).isEqualTo(10.0); | |||
assertThat(issue.locations().get(0).message()).isEqualTo("Wrong way!"); | |||
assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!"); | |||
issue.save(); | |||
@@ -62,16 +63,16 @@ public class DefaultIssueTest { | |||
public void build_directory_issue() { | |||
SensorStorage storage = mock(SensorStorage.class); | |||
DefaultIssue issue = new DefaultIssue(storage) | |||
.addLocation(new DefaultIssueLocation() | |||
.onDir(new DefaultInputDir("foo", "src")) | |||
.at(new DefaultIssueLocation() | |||
.on(new DefaultInputDir("foo", "src")) | |||
.message("Wrong way!")) | |||
.forRule(RuleKey.of("repo", "rule")) | |||
.overrideSeverity(Severity.BLOCKER); | |||
assertThat(issue.locations().get(0).inputPath()).isEqualTo(new DefaultInputDir("foo", "src")); | |||
assertThat(issue.primaryLocation().inputComponent()).isEqualTo(new DefaultInputDir("foo", "src")); | |||
assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); | |||
assertThat(issue.locations().get(0).textRange()).isNull(); | |||
assertThat(issue.locations().get(0).message()).isEqualTo("Wrong way!"); | |||
assertThat(issue.primaryLocation().textRange()).isNull(); | |||
assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!"); | |||
assertThat(issue.overriddenSeverity()).isEqualTo(Severity.BLOCKER); | |||
issue.save(); | |||
@@ -83,17 +84,17 @@ public class DefaultIssueTest { | |||
public void build_project_issue() { | |||
SensorStorage storage = mock(SensorStorage.class); | |||
DefaultIssue issue = new DefaultIssue(storage) | |||
.addLocation(new DefaultIssueLocation() | |||
.onProject() | |||
.at(new DefaultIssueLocation() | |||
.on(new DefaultInputModule("foo")) | |||
.message("Wrong way!")) | |||
.forRule(RuleKey.of("repo", "rule")) | |||
.effortToFix(10.0); | |||
assertThat(issue.locations().get(0).inputPath()).isNull(); | |||
assertThat(issue.primaryLocation().inputComponent()).isEqualTo(new DefaultInputModule("foo")); | |||
assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); | |||
assertThat(issue.locations().get(0).textRange()).isNull(); | |||
assertThat(issue.primaryLocation().textRange()).isNull(); | |||
assertThat(issue.effortToFix()).isEqualTo(10.0); | |||
assertThat(issue.locations().get(0).message()).isEqualTo("Wrong way!"); | |||
assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!"); | |||
issue.save(); | |||