aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-scanner-engine/src
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2018-04-06 11:33:23 +0200
committerSonarTech <sonartech@sonarsource.com>2018-04-26 20:20:49 +0200
commit7bd31bc52d296c558803ee633c49851afe13e9ff (patch)
tree00bf40713bda81ae39861a9bf24fe843f43945ec /sonar-scanner-engine/src
parent95b338ed1b91e2fd4684ac0fe0b4f7f9ac736bb0 (diff)
downloadsonarqube-7bd31bc52d296c558803ee633c49851afe13e9ff.tar.gz
sonarqube-7bd31bc52d296c558803ee633c49851afe13e9ff.zip
SONAR-10543 Sensor Java API should allow to add external rule engine issues
Diffstat (limited to 'sonar-scanner-engine/src')
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java49
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java15
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java8
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java14
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ModuleIssuesTest.java18
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java23
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java1
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java14
8 files changed, 137 insertions, 5 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java
index ef7bda53f16..40167c3c769 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java
@@ -20,6 +20,8 @@
package org.sonar.scanner.issue;
import com.google.common.base.Strings;
+import java.util.Collection;
+import java.util.function.Consumer;
import javax.annotation.concurrent.ThreadSafe;
import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.fs.internal.DefaultInputComponent;
@@ -27,6 +29,7 @@ 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.sensor.issue.ExternalIssue;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.Issue.Flow;
import org.sonar.api.rule.RuleKey;
@@ -73,6 +76,12 @@ public class ModuleIssues {
return false;
}
+ public void initAndAddExternalIssue(ExternalIssue issue) {
+ DefaultInputComponent inputComponent = (DefaultInputComponent) issue.primaryLocation().inputComponent();
+ ScannerReport.ExternalIssue rawExternalIssue = createReportExternalIssue(issue, inputComponent.batchId());
+ write(inputComponent.batchId(), rawExternalIssue);
+ }
+
private static ScannerReport.Issue createReportIssue(Issue issue, int componentRef, String ruleName, String activeRuleSeverity) {
String primaryMessage = Strings.isNullOrEmpty(issue.primaryLocation().message()) ? ruleName : issue.primaryLocation().message();
org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity();
@@ -97,14 +106,41 @@ public class ModuleIssues {
if (gap != null) {
builder.setGap(gap);
}
- applyFlows(componentRef, builder, locationBuilder, textRangeBuilder, issue);
+ applyFlows(builder::addFlow, locationBuilder, textRangeBuilder, issue.flows());
return builder.build();
}
- private static void applyFlows(int componentRef, ScannerReport.Issue.Builder builder, ScannerReport.IssueLocation.Builder locationBuilder,
- ScannerReport.TextRange.Builder textRangeBuilder, Issue issue) {
+ private static ScannerReport.ExternalIssue createReportExternalIssue(ExternalIssue issue, int componentRef) {
+ String primaryMessage = issue.primaryLocation().message();
+ Severity severity = Severity.valueOf(issue.severity().name());
+
+ ScannerReport.ExternalIssue.Builder builder = ScannerReport.ExternalIssue.newBuilder();
+ ScannerReport.IssueLocation.Builder locationBuilder = IssueLocation.newBuilder();
+ ScannerReport.TextRange.Builder textRangeBuilder = ScannerReport.TextRange.newBuilder();
+ // non-null fields
+ builder.setSeverity(severity);
+ builder.setRuleRepository(issue.ruleKey().repository());
+ builder.setRuleKey(issue.ruleKey().rule());
+ builder.setMsg(primaryMessage);
+ locationBuilder.setMsg(primaryMessage);
+
+ locationBuilder.setComponentRef(componentRef);
+ TextRange primaryTextRange = issue.primaryLocation().textRange();
+ if (primaryTextRange != null) {
+ builder.setTextRange(toProtobufTextRange(textRangeBuilder, primaryTextRange));
+ }
+ Long effort = issue.remediationEffort();
+ if (effort != null) {
+ builder.setEffort(effort);
+ }
+ applyFlows(builder::addFlow, locationBuilder, textRangeBuilder, issue.flows());
+ return builder.build();
+ }
+
+ private static void applyFlows(Consumer<ScannerReport.Flow> consumer, ScannerReport.IssueLocation.Builder locationBuilder,
+ ScannerReport.TextRange.Builder textRangeBuilder, Collection<Flow> flows) {
ScannerReport.Flow.Builder flowBuilder = ScannerReport.Flow.newBuilder();
- for (Flow flow : issue.flows()) {
+ for (Flow flow : flows) {
if (flow.locations().isEmpty()) {
return;
}
@@ -123,7 +159,7 @@ public class ModuleIssues {
}
flowBuilder.addLocation(locationBuilder.build());
}
- builder.addFlow(flowBuilder.build());
+ consumer.accept(flowBuilder.build());
}
}
@@ -152,4 +188,7 @@ public class ModuleIssues {
reportPublisher.getWriter().appendComponentIssue(batchId, rawIssue);
}
+ public void write(int batchId, ScannerReport.ExternalIssue rawIssue) {
+ reportPublisher.getWriter().appendComponentExternalIssue(batchId, rawIssue);
+ }
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java
index 232fbc88c06..80a33078927 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java
@@ -123,6 +123,11 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
int ref = reportComponents.get(inputComponent.key()).getRef();
return issuesFor(ref);
}
+
+ public List<ScannerReport.ExternalIssue> externalIssuesFor(InputComponent inputComponent) {
+ int ref = reportComponents.get(inputComponent.key()).getRef();
+ return externalIssuesFor(ref);
+ }
public List<ScannerReport.Issue> issuesFor(Component reportComponent) {
int ref = reportComponent.getRef();
@@ -138,6 +143,16 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
}
return result;
}
+
+ private List<ScannerReport.ExternalIssue> externalIssuesFor(int ref) {
+ List<ScannerReport.ExternalIssue> result = Lists.newArrayList();
+ try (CloseableIterator<ScannerReport.ExternalIssue> it = reader.readComponentExternalIssues(ref)) {
+ while (it.hasNext()) {
+ result.add(it.next());
+ }
+ }
+ return result;
+ }
public Collection<InputFile> inputFiles() {
return inputFiles.values();
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java
index 6972e241500..ff9a7ba1685 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java
@@ -37,7 +37,9 @@ import org.sonar.api.batch.sensor.error.NewAnalysisError;
import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
import org.sonar.api.batch.sensor.internal.SensorStorage;
+import org.sonar.api.batch.sensor.issue.NewExternalIssue;
import org.sonar.api.batch.sensor.issue.NewIssue;
+import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
import org.sonar.api.batch.sensor.measure.NewMeasure;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
@@ -131,6 +133,11 @@ public class DefaultSensorContext implements SensorContext {
}
@Override
+ public NewExternalIssue newExternalIssue() {
+ return new DefaultExternalIssue(sensorStorage);
+ }
+
+ @Override
public NewHighlighting newHighlighting() {
if (analysisMode.isIssues()) {
return NO_OP_NEW_HIGHLIGHTING;
@@ -182,4 +189,5 @@ public class DefaultSensorContext implements SensorContext {
DefaultInputFile file = (DefaultInputFile) inputFile;
file.setPublished(true);
}
+
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
index 6b5a7137d94..1ccd219c600 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
@@ -41,6 +41,7 @@ import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
import org.sonar.api.batch.sensor.error.AnalysisError;
import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
import org.sonar.api.batch.sensor.internal.SensorStorage;
+import org.sonar.api.batch.sensor.issue.ExternalIssue;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
@@ -373,6 +374,18 @@ public class DefaultSensorStorage implements SensorStorage {
moduleIssues.initAndAddIssue(issue);
}
+ /**
+ * Thread safe assuming that each issues for each file are only written once.
+ */
+ @Override
+ public void store(ExternalIssue externalIssue) {
+ if (externalIssue.primaryLocation().inputComponent() instanceof DefaultInputFile) {
+ DefaultInputFile defaultInputFile = (DefaultInputFile) externalIssue.primaryLocation().inputComponent();
+ defaultInputFile.setPublished(true);
+ }
+ moduleIssues.initAndAddExternalIssue(externalIssue);
+ }
+
@Override
public void store(DefaultHighlighting highlighting) {
ScannerReportWriter writer = reportPublisher.getWriter();
@@ -493,4 +506,5 @@ public class DefaultSensorStorage implements SensorStorage {
public void storeProperty(String key, String value) {
contextPropertiesCache.put(key, value);
}
+
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ModuleIssuesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ModuleIssuesTest.java
index 07f627c8423..6309720339d 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ModuleIssuesTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ModuleIssuesTest.java
@@ -28,6 +28,7 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.RulesBuilder;
+import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
import org.sonar.api.rule.RuleKey;
@@ -147,6 +148,23 @@ public class ModuleIssuesTest {
}
@Test
+ public void add_external_issue_to_cache() {
+ ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
+ initModuleIssues();
+
+ DefaultExternalIssue issue = new DefaultExternalIssue()
+ .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
+ .forRule(SQUID_RULE_KEY)
+ .severity(org.sonar.api.batch.rule.Severity.CRITICAL);
+
+ moduleIssues.initAndAddExternalIssue(issue);
+
+ ArgumentCaptor<ScannerReport.ExternalIssue> argument = ArgumentCaptor.forClass(ScannerReport.ExternalIssue.class);
+ verify(reportPublisher.getWriter()).appendComponentExternalIssue(eq(file.batchId()), argument.capture());
+ assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.CRITICAL);
+ }
+
+ @Test
public void use_severity_from_active_rule_if_no_severity_on_issue() {
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java
index 64c8b66cb84..babe6f078ba 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java
@@ -29,8 +29,10 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.scanner.mediumtest.ScannerMediumTester;
import org.sonar.scanner.mediumtest.TaskResult;
+import org.sonar.scanner.protocol.output.ScannerReport.ExternalIssue;
import org.sonar.scanner.protocol.output.ScannerReport.Issue;
import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.OneExternalIssuePerLineSensor;
import org.sonar.xoo.rule.XooRulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
@@ -60,10 +62,31 @@ public class IssuesMediumTest {
List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
assertThat(issues).hasSize(8 /* lines */);
+
+ List<ExternalIssue> externalIssues = result.externalIssuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+ assertThat(externalIssues).isEmpty();
Issue issue = issues.get(0);
assertThat(issue.getTextRange().getStartLine()).isEqualTo(issue.getTextRange().getStartLine());
}
+
+ @Test
+ public void testOneExternalIssuePerLine() throws Exception {
+ File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+ File tmpDir = temp.newFolder();
+ FileUtils.copyDirectory(projectDir, tmpDir);
+
+ TaskResult result = tester
+ .newScanTask(new File(tmpDir, "sonar-project.properties"))
+ .property(OneExternalIssuePerLineSensor.ACTIVATE_EXTERNAL_ISSUES, "true")
+ .execute();
+
+ List<ExternalIssue> externalIssues = result.externalIssuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+ assertThat(externalIssues).hasSize(8 /* lines */);
+
+ ExternalIssue externalIssue = externalIssues.get(0);
+ assertThat(externalIssue.getTextRange().getStartLine()).isEqualTo(externalIssue.getTextRange().getStartLine());
+ }
@Test
public void findActiveRuleByInternalKey() throws Exception {
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java
index 68771fe5059..24cc70506be 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java
@@ -84,6 +84,7 @@ public class DefaultSensorContextTest {
assertThat(adaptor.runtime()).isEqualTo(runtime);
assertThat(adaptor.newIssue()).isNotNull();
+ assertThat(adaptor.newExternalIssue()).isNotNull();
assertThat(adaptor.newMeasure()).isNotNull();
assertThat(adaptor.isCancelled()).isFalse();
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
index 843e4730b45..bb039e2da18 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
@@ -36,7 +36,9 @@ import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.measure.MetricFinder;
import org.sonar.api.batch.sensor.highlighting.TypeOfText;
import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
+import org.sonar.api.batch.sensor.issue.ExternalIssue;
import org.sonar.api.batch.sensor.issue.Issue;
+import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
@@ -125,6 +127,18 @@ public class DefaultSensorStorageTest {
}
@Test
+ public void should_save_external_issue() {
+ InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").build();
+
+ DefaultExternalIssue externalIssue = new DefaultExternalIssue().at(new DefaultIssueLocation().on(file));
+ underTest.store(externalIssue);
+
+ ArgumentCaptor<ExternalIssue> argumentCaptor = ArgumentCaptor.forClass(ExternalIssue.class);
+ verify(moduleIssues).initAndAddExternalIssue(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue()).isEqualTo(externalIssue);
+ }
+
+ @Test
public void should_skip_issue_on_short_branch_when_file_status_is_SAME() {
InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").setStatus(InputFile.Status.SAME).build();
when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);