import org.sonar.xoo.rule.OneCodeSmellIssuePerLineSensor; | import org.sonar.xoo.rule.OneCodeSmellIssuePerLineSensor; | ||||
import org.sonar.xoo.rule.OneCodeSmellIssuePerTestLineSensor; | import org.sonar.xoo.rule.OneCodeSmellIssuePerTestLineSensor; | ||||
import org.sonar.xoo.rule.OneDayDebtPerFileSensor; | import org.sonar.xoo.rule.OneDayDebtPerFileSensor; | ||||
import org.sonar.xoo.rule.OneExternalIssueOnProjectSensor; | |||||
import org.sonar.xoo.rule.OneExternalIssuePerLineSensor; | import org.sonar.xoo.rule.OneExternalIssuePerLineSensor; | ||||
import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; | import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; | ||||
import org.sonar.xoo.rule.OneIssuePerDirectorySensor; | import org.sonar.xoo.rule.OneIssuePerDirectorySensor; | ||||
OneIssuePerUnknownFileSensor.class, | OneIssuePerUnknownFileSensor.class, | ||||
OneExternalIssuePerLineSensor.class, | OneExternalIssuePerLineSensor.class, | ||||
OneExternalIssueOnProjectSensor.class, | |||||
OnePredefinedRuleExternalIssuePerLineSensor.class, | OnePredefinedRuleExternalIssuePerLineSensor.class, | ||||
OnePredefinedAndAdHocRuleExternalIssuePerLineSensor.class, | OnePredefinedAndAdHocRuleExternalIssuePerLineSensor.class, | ||||
/* | |||||
* 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(); | |||||
} | |||||
} |
package org.sonar.api.batch.sensor.issue.internal; | package org.sonar.api.batch.sensor.issue.internal; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.sonar.api.batch.fs.internal.DefaultInputProject; | |||||
import org.sonar.api.batch.rule.Severity; | import org.sonar.api.batch.rule.Severity; | ||||
import org.sonar.api.batch.sensor.internal.SensorStorage; | import org.sonar.api.batch.sensor.internal.SensorStorage; | ||||
import org.sonar.api.batch.sensor.issue.ExternalIssue; | import org.sonar.api.batch.sensor.issue.ExternalIssue; | ||||
import org.sonar.api.batch.sensor.issue.NewExternalIssue; | 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.rule.RuleKey; | ||||
import org.sonar.api.rules.RuleType; | import org.sonar.api.rules.RuleType; | ||||
requireNonNull(this.engineId, "Engine id is mandatory on external issue"); | requireNonNull(this.engineId, "Engine id is mandatory on external issue"); | ||||
requireNonNull(this.ruleId, "Rule 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 != 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(primaryLocation.message() != null, "External issues must have a message"); | ||||
checkState(severity != null, "Severity is mandatory on every external issue"); | checkState(severity != null, "Severity is mandatory on every external issue"); | ||||
checkState(type != null, "Type is mandatory on every external issue"); | checkState(type != null, "Type is mandatory on every external issue"); |
import org.junit.rules.ExpectedException; | import org.junit.rules.ExpectedException; | ||||
import org.junit.rules.TemporaryFolder; | import org.junit.rules.TemporaryFolder; | ||||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | 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.rule.Severity; | ||||
import org.sonar.api.batch.sensor.internal.SensorStorage; | 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.rule.RuleKey; | ||||
import org.sonar.api.rules.RuleType; | 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.assertj.core.api.Assertions.assertThat; | ||||
import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||
} | } | ||||
@Test | @Test | ||||
public void fail_to_store_if_no_type() { | |||||
public void build_project_issue() { | |||||
SensorStorage storage = mock(SensorStorage.class); | SensorStorage storage = mock(SensorStorage.class); | ||||
DefaultExternalIssue issue = new DefaultExternalIssue(project, storage) | DefaultExternalIssue issue = new DefaultExternalIssue(project, storage) | ||||
.at(new DefaultIssueLocation() | .at(new DefaultIssueLocation() | ||||
.on(inputFile) | |||||
.at(inputFile.selectLine(1)) | |||||
.on(project) | |||||
.message("Wrong way!")) | .message("Wrong way!")) | ||||
.forRule(RuleKey.of("repo", "rule")) | .forRule(RuleKey.of("repo", "rule")) | ||||
.remediationEffortMinutes(10l) | .remediationEffortMinutes(10l) | ||||
.type(RuleType.BUG) | |||||
.severity(Severity.BLOCKER); | .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(); | issue.save(); | ||||
verify(storage).store(issue); | |||||
} | } | ||||
@Test | @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); | SensorStorage storage = mock(SensorStorage.class); | ||||
DefaultExternalIssue issue = new DefaultExternalIssue(project, storage) | DefaultExternalIssue issue = new DefaultExternalIssue(project, storage) | ||||
.at(new DefaultIssueLocation() | .at(new DefaultIssueLocation() | ||||
.on(mock(InputComponent.class)) | |||||
.on(inputFile) | |||||
.at(inputFile.selectLine(1)) | |||||
.message("Wrong way!")) | .message("Wrong way!")) | ||||
.forRule(RuleKey.of("repo", "rule")) | .forRule(RuleKey.of("repo", "rule")) | ||||
.remediationEffortMinutes(10l) | .remediationEffortMinutes(10l) | ||||
.severity(Severity.BLOCKER); | .severity(Severity.BLOCKER); | ||||
exception.expect(IllegalStateException.class); | exception.expect(IllegalStateException.class); | ||||
exception.expectMessage("External issues must be located in files"); | |||||
exception.expectMessage("Type is mandatory"); | |||||
issue.save(); | issue.save(); | ||||
} | } | ||||
import org.sonar.scanner.protocol.output.ScannerReport.Issue; | import org.sonar.scanner.protocol.output.ScannerReport.Issue; | ||||
import org.sonar.xoo.XooPlugin; | import org.sonar.xoo.XooPlugin; | ||||
import org.sonar.xoo.rule.HasTagSensor; | import org.sonar.xoo.rule.HasTagSensor; | ||||
import org.sonar.xoo.rule.OneExternalIssueOnProjectSensor; | |||||
import org.sonar.xoo.rule.OneExternalIssuePerLineSensor; | import org.sonar.xoo.rule.OneExternalIssuePerLineSensor; | ||||
import org.sonar.xoo.rule.XooRulesDefinition; | import org.sonar.xoo.rule.XooRulesDefinition; | ||||
assertThat(externalIssues).hasSize(8 /* lines */); | 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 | @Test | ||||
public void findActiveRuleByInternalKey() throws Exception { | public void findActiveRuleByInternalKey() throws Exception { | ||||
File projectDir = new File("test-resources/mediumtest/xoo/sample"); | File projectDir = new File("test-resources/mediumtest/xoo/sample"); |