@@ -52,6 +52,7 @@ import org.sonar.xoo.rule.OneBugIssuePerTestLineSensor; | |||
import org.sonar.xoo.rule.OneCodeSmellIssuePerLineSensor; | |||
import org.sonar.xoo.rule.OneCodeSmellIssuePerTestLineSensor; | |||
import org.sonar.xoo.rule.OneDayDebtPerFileSensor; | |||
import org.sonar.xoo.rule.OneExternalIssueOnProjectSensor; | |||
import org.sonar.xoo.rule.OneExternalIssuePerLineSensor; | |||
import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; | |||
import org.sonar.xoo.rule.OneIssuePerDirectorySensor; | |||
@@ -146,6 +147,7 @@ public class XooPlugin implements Plugin { | |||
OneIssuePerUnknownFileSensor.class, | |||
OneExternalIssuePerLineSensor.class, | |||
OneExternalIssueOnProjectSensor.class, | |||
OnePredefinedRuleExternalIssuePerLineSensor.class, | |||
OnePredefinedAndAdHocRuleExternalIssuePerLineSensor.class, | |||
@@ -0,0 +1,62 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.xoo.rule; | |||
import org.sonar.api.batch.rule.Severity; | |||
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.NewExternalIssue; | |||
import org.sonar.api.rules.RuleType; | |||
import org.sonar.xoo.Xoo; | |||
public class OneExternalIssueOnProjectSensor implements Sensor { | |||
public static final String ENGINE_ID = "XooEngine"; | |||
public static final String SEVERITY = "MAJOR"; | |||
public static final RuleType TYPE = RuleType.BUG; | |||
public static final String ACTIVATE = "sonar.oneExternalIssueOnProject.activate"; | |||
public static final String RULE_ID = "OneExternalIssueOnProject"; | |||
@Override | |||
public void describe(SensorDescriptor descriptor) { | |||
descriptor | |||
.name("One External Issue At Project Level") | |||
.onlyOnLanguages(Xoo.KEY) | |||
.onlyWhenConfiguration(c -> c.getBoolean(ACTIVATE).orElse(false)); | |||
} | |||
@Override | |||
public void execute(SensorContext context) { | |||
analyse(context); | |||
} | |||
private static void analyse(SensorContext context) { | |||
NewExternalIssue newIssue = context.newExternalIssue(); | |||
newIssue | |||
.engineId(ENGINE_ID) | |||
.ruleId(RULE_ID) | |||
.at(newIssue.newLocation() | |||
.on(context.project()) | |||
.message("This issue is generated at project level")) | |||
.severity(Severity.valueOf(SEVERITY)) | |||
.type(TYPE) | |||
.save(); | |||
} | |||
} |
@@ -20,11 +20,11 @@ | |||
package org.sonar.api.batch.sensor.issue.internal; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.batch.fs.internal.DefaultInputProject; | |||
import org.sonar.api.batch.rule.Severity; | |||
import org.sonar.api.batch.sensor.internal.SensorStorage; | |||
import org.sonar.api.batch.sensor.issue.ExternalIssue; | |||
import org.sonar.api.batch.sensor.issue.NewExternalIssue; | |||
import org.sonar.api.batch.fs.internal.DefaultInputProject; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rules.RuleType; | |||
@@ -86,7 +86,6 @@ public class DefaultExternalIssue extends AbstractDefaultIssue<DefaultExternalIs | |||
requireNonNull(this.engineId, "Engine id is mandatory on external issue"); | |||
requireNonNull(this.ruleId, "Rule id is mandatory on external issue"); | |||
checkState(primaryLocation != null, "Primary location is mandatory on every external issue"); | |||
checkState(primaryLocation.inputComponent().isFile(), "External issues must be located in files"); | |||
checkState(primaryLocation.message() != null, "External issues must have a message"); | |||
checkState(severity != null, "Severity is mandatory on every external issue"); | |||
checkState(type != null, "Type is mandatory on every external issue"); |
@@ -26,16 +26,13 @@ import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputProject; | |||
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; | |||
import org.sonar.api.batch.rule.Severity; | |||
import org.sonar.api.batch.sensor.internal.SensorStorage; | |||
import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rules.RuleType; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputProject; | |||
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; | |||
import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
@@ -92,35 +89,46 @@ public class DefaultExternalIssueTest { | |||
} | |||
@Test | |||
public void fail_to_store_if_no_type() { | |||
public void build_project_issue() { | |||
SensorStorage storage = mock(SensorStorage.class); | |||
DefaultExternalIssue issue = new DefaultExternalIssue(project, storage) | |||
.at(new DefaultIssueLocation() | |||
.on(inputFile) | |||
.at(inputFile.selectLine(1)) | |||
.on(project) | |||
.message("Wrong way!")) | |||
.forRule(RuleKey.of("repo", "rule")) | |||
.remediationEffortMinutes(10l) | |||
.type(RuleType.BUG) | |||
.severity(Severity.BLOCKER); | |||
exception.expect(IllegalStateException.class); | |||
exception.expectMessage("Type is mandatory"); | |||
assertThat(issue.primaryLocation().inputComponent()).isEqualTo(project); | |||
assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("external_repo", "rule")); | |||
assertThat(issue.engineId()).isEqualTo("repo"); | |||
assertThat(issue.ruleId()).isEqualTo("rule"); | |||
assertThat(issue.primaryLocation().textRange()).isNull(); | |||
assertThat(issue.remediationEffort()).isEqualTo(10l); | |||
assertThat(issue.type()).isEqualTo(RuleType.BUG); | |||
assertThat(issue.severity()).isEqualTo(Severity.BLOCKER); | |||
assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!"); | |||
issue.save(); | |||
verify(storage).store(issue); | |||
} | |||
@Test | |||
public void fail_to_store_if_primary_location_is_not_a_file() { | |||
public void fail_to_store_if_no_type() { | |||
SensorStorage storage = mock(SensorStorage.class); | |||
DefaultExternalIssue issue = new DefaultExternalIssue(project, storage) | |||
.at(new DefaultIssueLocation() | |||
.on(mock(InputComponent.class)) | |||
.on(inputFile) | |||
.at(inputFile.selectLine(1)) | |||
.message("Wrong way!")) | |||
.forRule(RuleKey.of("repo", "rule")) | |||
.remediationEffortMinutes(10l) | |||
.severity(Severity.BLOCKER); | |||
exception.expect(IllegalStateException.class); | |||
exception.expectMessage("External issues must be located in files"); | |||
exception.expectMessage("Type is mandatory"); | |||
issue.save(); | |||
} | |||
@@ -38,6 +38,7 @@ 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.HasTagSensor; | |||
import org.sonar.xoo.rule.OneExternalIssueOnProjectSensor; | |||
import org.sonar.xoo.rule.OneExternalIssuePerLineSensor; | |||
import org.sonar.xoo.rule.XooRulesDefinition; | |||
@@ -92,6 +93,21 @@ public class IssuesMediumTest { | |||
assertThat(externalIssues).hasSize(8 /* lines */); | |||
} | |||
@Test | |||
public void testOneExternalIssueOnProject() throws Exception { | |||
File projectDir = new File("test-resources/mediumtest/xoo/sample"); | |||
File tmpDir = temp.newFolder(); | |||
FileUtils.copyDirectory(projectDir, tmpDir); | |||
AnalysisResult result = tester | |||
.newAnalysis(new File(tmpDir, "sonar-project.properties")) | |||
.property(OneExternalIssueOnProjectSensor.ACTIVATE, "true") | |||
.execute(); | |||
List<ExternalIssue> externalIssues = result.externalIssuesFor(result.project()); | |||
assertThat(externalIssues).hasSize(1); | |||
} | |||
@Test | |||
public void findActiveRuleByInternalKey() throws Exception { | |||
File projectDir = new File("test-resources/mediumtest/xoo/sample"); |