aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java2
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssuePerLineSensor.java4
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssueWithDetailsPerLineSensor.java83
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java15
-rw-r--r--plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java2
-rw-r--r--plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java9
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java50
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java43
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java46
-rw-r--r--tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java46
11 files changed, 289 insertions, 14 deletions
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
index 3b626b8ed9f..639d51c2172 100644
--- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
@@ -51,6 +51,7 @@ import org.sonar.xoo.rule.OneBlockerIssuePerFileSensor;
import org.sonar.xoo.rule.OneBugIssuePerLineSensor;
import org.sonar.xoo.rule.OneDayDebtPerFileSensor;
import org.sonar.xoo.rule.OneExternalIssuePerLineSensor;
+import org.sonar.xoo.rule.OneExternalIssueWithDetailsPerLineSensor;
import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor;
import org.sonar.xoo.rule.OneIssuePerDirectorySensor;
import org.sonar.xoo.rule.OneIssuePerFileSensor;
@@ -176,6 +177,7 @@ public class XooPlugin implements Plugin {
if (context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 2))) {
context.addExtensions(
OneExternalIssuePerLineSensor.class,
+ OneExternalIssueWithDetailsPerLineSensor.class,
SignificantCodeSensor.class);
}
}
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssuePerLineSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssuePerLineSensor.java
index d9fe4b59e82..c8d09704371 100644
--- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssuePerLineSensor.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssuePerLineSensor.java
@@ -38,7 +38,7 @@ public class OneExternalIssuePerLineSensor implements Sensor {
public static final String ENGINE_KEY = "XooEngine";
public static final String SEVERITY = "MAJOR";
public static final Long EFFORT = 10l;
- public static final RuleType type = RuleType.BUG;
+ public static final RuleType TYPE = RuleType.BUG;
public static final String ACTIVATE_EXTERNAL_ISSUES = "sonar.oneExternalIssuePerLine.activate";
private static final String NAME = "One External Issue Per Line";
@@ -76,7 +76,7 @@ public class OneExternalIssuePerLineSensor implements Sensor {
.message("This issue is generated on each line"))
.severity(Severity.valueOf(SEVERITY))
.remediationEffortMinutes(EFFORT)
- .type(type)
+ .type(TYPE)
.save();
}
}
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssueWithDetailsPerLineSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssueWithDetailsPerLineSensor.java
new file mode 100644
index 00000000000..0598b7a8d40
--- /dev/null
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneExternalIssueWithDetailsPerLineSensor.java
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.fs.FilePredicates;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputFile.Type;
+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.rule.RuleKey;
+import org.sonar.api.rules.RuleType;
+import org.sonar.xoo.Xoo;
+import org.sonar.xoo.Xoo2;
+
+public class OneExternalIssueWithDetailsPerLineSensor implements Sensor {
+ public static final String RULE_KEY = "OneExternalIssueWithDetailsPerLine";
+ public static final String ENGINE_KEY = "XooEngine";
+ public static final String SEVERITY = "MAJOR";
+ public static final Long EFFORT = 10l;
+ public static final RuleType TYPE = RuleType.BUG;
+ public static final String ACTIVATE_EXTERNAL_ISSUES = "sonar.oneExternalIssueWithDetailsPerLine.activate";
+ private static final String NAME = "One External Issue Per Line";
+
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor
+ .name(NAME)
+ .onlyOnLanguages(Xoo.KEY, Xoo2.KEY)
+ .onlyWhenConfiguration(c -> c.getBoolean(ACTIVATE_EXTERNAL_ISSUES).orElse(false));
+ }
+
+ @Override
+ public void execute(SensorContext context) {
+ analyse(context, Xoo.KEY, XooRulesDefinition.XOO_REPOSITORY);
+ analyse(context, Xoo2.KEY, XooRulesDefinition.XOO2_REPOSITORY);
+ }
+
+ private void analyse(SensorContext context, String language, String repo) {
+ FileSystem fs = context.fileSystem();
+ FilePredicates p = fs.predicates();
+ for (InputFile file : fs.inputFiles(p.and(p.hasLanguages(language), p.hasType(Type.MAIN)))) {
+ createIssues(file, context, repo);
+ }
+ }
+
+ private void createIssues(InputFile file, SensorContext context, String repo) {
+ RuleKey ruleKey = RuleKey.of(repo, RULE_KEY);
+ for (int line = 1; line <= file.lines(); line++) {
+ NewExternalIssue newIssue = context.newExternalIssue();
+ newIssue
+ .forRule(ruleKey)
+ .at(newIssue.newLocation()
+ .on(file)
+ .at(file.selectLine(line))
+ .message("This issue is generated on each line and the rule contains details"))
+ .severity(Severity.valueOf(SEVERITY))
+ .remediationEffortMinutes(EFFORT)
+ .type(TYPE)
+ .save();
+ }
+ }
+}
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java
index 13c238cae7a..4f119f9ebe0 100644
--- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java
@@ -35,6 +35,7 @@ public class XooRulesDefinition implements RulesDefinition {
public static final String XOO_REPOSITORY = "xoo";
public static final String XOO2_REPOSITORY = "xoo2";
+ public static final String XOO_EXTERNAL_REPOSITORY = "xoo";
private static final String TEN_MIN = "10min";
@@ -42,6 +43,7 @@ public class XooRulesDefinition implements RulesDefinition {
public void define(Context context) {
defineRulesXoo(context);
defineRulesXoo2(context);
+ defineRulesXooExternal(context);
}
private static void defineRulesXoo2(Context context) {
@@ -156,4 +158,17 @@ public class XooRulesDefinition implements RulesDefinition {
}
+ private static void defineRulesXooExternal(Context context) {
+ NewRepository repo = context.createExternalRepository(XOO_EXTERNAL_REPOSITORY, Xoo.KEY).setName("XooExternal");
+
+ repo.createRule(OneExternalIssueWithDetailsPerLineSensor.RULE_KEY)
+ .setSeverity(OneExternalIssueWithDetailsPerLineSensor.SEVERITY)
+ .setType(OneExternalIssueWithDetailsPerLineSensor.TYPE)
+ .setScope(RuleScope.ALL)
+ .setHtmlDescription("Generates one external issue in each line")
+ .setName("One external issue per line");
+
+ repo.done();
+ }
+
}
diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
index dacd5b3f575..e66d56e45c8 100644
--- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
+++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
@@ -62,6 +62,6 @@ public class XooPluginTest {
SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("7.2"), SonarQubeSide.SCANNER);
Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build();
new XooPlugin().define(context);
- assertThat(context.getExtensions()).hasSize(54).contains(OneExternalIssuePerLineSensor.class);
+ assertThat(context.getExtensions()).hasSize(55).contains(OneExternalIssuePerLineSensor.class);
}
}
diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java
index 7de2430f0c1..9658ab8e3a4 100644
--- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java
+++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java
@@ -51,6 +51,15 @@ public class XooRulesDefinitionTest {
assertThat(rule.debtRemediationFunction().baseEffort()).isNull();
assertThat(rule.gapDescription()).isNotEmpty();
}
+
+ @Test
+ public void define_xooExternal_rules() {
+ RulesDefinition.Repository repo = context.repository("external_xoo");
+ assertThat(repo).isNotNull();
+ assertThat(repo.name()).isEqualTo("XooExternal");
+ assertThat(repo.language()).isEqualTo("xoo");
+ assertThat(repo.rules()).hasSize(1);
+ }
@Test
public void define_xoo2_rules() {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java
index ba7e348ea02..51c5549ba3d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java
@@ -392,7 +392,7 @@ public class RegisterRules implements Startable {
.setSystemTags(ruleDef.tags())
.setType(RuleType.valueOf(ruleDef.type().name()))
.setScope(toDtoScope(ruleDef.scope()))
- .setIsExternal(false)
+ .setIsExternal(ruleDef.repository().isExternal())
.setCreatedAt(system2.now())
.setUpdatedAt(system2.now());
if (ruleDef.htmlDescription() != null) {
@@ -746,7 +746,6 @@ public class RegisterRules implements Startable {
dbClient.ruleDao().update(session, rule);
}
-
private static void verifyRuleKeyConsistency(List<RulesDefinition.ExtendedRepository> repositories, RegisterRulesContext registerRulesContext) {
List<RulesDefinition.Rule> definedRules = repositories.stream()
.flatMap(r -> r.rules().stream())
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
index 580e22dc528..2d70a76fa5e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
@@ -92,6 +92,8 @@ public class RegisterRulesTest {
private static final Date DATE2 = DateUtils.parseDateTime("2014-02-01T12:10:03+0100");
private static final Date DATE3 = DateUtils.parseDateTime("2014-03-01T12:10:03+0100");
+ private static final RuleKey EXTERNAL_RULE_KEY1 = RuleKey.of("external_eslint", "rule1");
+
private static final RuleKey RULE_KEY1 = RuleKey.of("fake", "rule1");
private static final RuleKey RULE_KEY2 = RuleKey.of("fake", "rule2");
private static final RuleKey RULE_KEY3 = RuleKey.of("fake", "rule3");
@@ -148,6 +150,7 @@ public class RegisterRulesTest {
assertThat(rule1.getDefRemediationBaseEffort()).isEqualTo("10h");
assertThat(rule1.getType()).isEqualTo(RuleType.CODE_SMELL.getDbConstant());
assertThat(rule1.getPluginKey()).isEqualTo(FAKE_PLUGIN_KEY);
+ assertThat(rule1.isExternal()).isFalse();
List<RuleParamDto> params = dbClient.ruleDao().selectRuleParamsByRuleKey(dbTester.getSession(), RULE_KEY1);
assertThat(params).hasSize(2);
@@ -164,6 +167,31 @@ public class RegisterRulesTest {
}
@Test
+ public void insert_new_external_rule() {
+ execute(new ExternalRuleRepository());
+
+ // verify db
+ assertThat(dbClient.ruleDao().selectAllDefinitions(dbTester.getSession())).hasSize(1);
+ RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(dbTester.getSession(), dbTester.getDefaultOrganization(), EXTERNAL_RULE_KEY1);
+ assertThat(rule1.getName()).isEqualTo("One");
+ assertThat(rule1.getDescription()).isEqualTo("Description of One");
+ assertThat(rule1.getSeverityString()).isEqualTo(BLOCKER);
+ assertThat(rule1.getTags()).isEmpty();
+ assertThat(rule1.getSystemTags()).containsOnly("tag1", "tag2", "tag3");
+ assertThat(rule1.getConfigKey()).isEqualTo("config1");
+ assertThat(rule1.getStatus()).isEqualTo(RuleStatus.BETA);
+ assertThat(rule1.getCreatedAt()).isEqualTo(DATE1.getTime());
+ assertThat(rule1.getScope()).isEqualTo(Scope.ALL);
+ assertThat(rule1.getUpdatedAt()).isEqualTo(DATE1.getTime());
+ assertThat(rule1.getDefRemediationFunction()).isNull();
+ assertThat(rule1.getDefRemediationGapMultiplier()).isNull();
+ assertThat(rule1.getDefRemediationBaseEffort()).isNull();
+ assertThat(rule1.getType()).isEqualTo(RuleType.CODE_SMELL.getDbConstant());
+ assertThat(rule1.getPluginKey()).isEqualTo(FAKE_PLUGIN_KEY);
+ assertThat(rule1.isExternal()).isTrue();
+ }
+
+ @Test
public void insert_then_remove_rule() {
String ruleKey = randomAlphanumeric(5);
@@ -496,7 +524,7 @@ public class RegisterRulesTest {
@DataProvider
public static Object[][] allRenamingCases() {
- return new Object[][]{
+ return new Object[][] {
{"repo1", "rule1", "repo1", "rule2"},
{"repo1", "rule1", "repo2", "rule1"},
{"repo1", "rule1", "repo2", "rule2"},
@@ -857,8 +885,7 @@ public class RegisterRulesTest {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("An incorrect state of deprecated rule keys has been detected.\n " +
- "The deprecated rule key [javascript:linelength] was previously deprecated by [javascript:s103]. [javascript:s103] should be a deprecated key of [sonarjs:s103],"
- );
+ "The deprecated rule key [javascript:linelength] was previously deprecated by [javascript:s103]. [javascript:s103] should be a deprecated key of [sonarjs:s103],");
// This rule should have been moved to another repository
execute(context -> createRule(context, "javascript", "sonarjs", "s103",
@@ -992,6 +1019,23 @@ public class RegisterRulesTest {
}
}
+ static class ExternalRuleRepository implements RulesDefinition {
+ @Override
+ public void define(Context context) {
+ NewRepository repo = context.createExternalRepository("eslint", "js");
+ repo.createRule(RULE_KEY1.rule())
+ .setName("One")
+ .setHtmlDescription("Description of One")
+ .setSeverity(BLOCKER)
+ .setInternalKey("config1")
+ .setTags("tag1", "tag2", "tag3")
+ .setScope(RuleScope.ALL)
+ .setType(RuleType.CODE_SMELL)
+ .setStatus(RuleStatus.BETA);
+ repo.done();
+ }
+ }
+
static class BigRepository implements RulesDefinition {
static final int SIZE = 500;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java
index cb1ce8ceac3..8c30bd2e82b 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java
@@ -397,7 +397,17 @@ public interface RulesDefinition {
* to execute {@link org.sonar.api.server.rule.RulesDefinition.NewRepository#setName(String)}
*/
public NewRepository createRepository(String key, String language) {
- return new NewRepositoryImpl(this, key, language);
+ return new NewRepositoryImpl(this, key, language, false);
+ }
+
+ /**
+ * Creates a repository of rules from external rule engines.
+ * The key will always be prefixed with "external_".
+ *
+ * @since 7.2
+ */
+ public NewRepository createExternalRepository(String key, String language) {
+ return new NewRepositoryImpl(this, key, language, true);
}
/**
@@ -472,19 +482,32 @@ public interface RulesDefinition {
interface NewRepository extends NewExtendedRepository {
NewRepository setName(String s);
+
+ /**
+ * @since 7.2
+ */
+ boolean isExternal();
}
class NewRepositoryImpl implements NewRepository {
private final Context context;
private final String key;
+ private final boolean isExternal;
private String language;
private String name;
private final Map<String, NewRule> newRules = new HashMap<>();
- private NewRepositoryImpl(Context context, String key, String language) {
+ private NewRepositoryImpl(Context context, String key, String language, boolean isExternal) {
this.context = context;
- this.key = this.name = key;
+ this.key = isExternal ? (RuleKey.EXTERNAL_RULE_REPO_PREFIX + key) : key;
+ this.name = key;
this.language = language;
+ this.isExternal = isExternal;
+ }
+
+ @Override
+ public boolean isExternal() {
+ return isExternal;
}
@Override
@@ -550,6 +573,11 @@ public interface RulesDefinition {
interface Repository extends ExtendedRepository {
String name();
+
+ /**
+ * @since 7.2
+ */
+ boolean isExternal();
}
@Immutable
@@ -557,12 +585,13 @@ public interface RulesDefinition {
private final String key;
private final String language;
private final String name;
+ private final boolean isExternal;
private final Map<String, Rule> rulesByKey;
private RepositoryImpl(NewRepositoryImpl newRepository, @Nullable Repository mergeInto) {
this.key = newRepository.key;
this.language = newRepository.language;
-
+ this.isExternal = newRepository.isExternal;
Map<String, Rule> ruleBuilder = new HashMap<>();
if (mergeInto != null) {
if (!StringUtils.equals(newRepository.language, mergeInto.language()) || !StringUtils.equals(newRepository.key, mergeInto.key())) {
@@ -601,6 +630,11 @@ public interface RulesDefinition {
}
@Override
+ public boolean isExternal() {
+ return isExternal;
+ }
+
+ @Override
@CheckForNull
public Rule rule(String ruleKey) {
return rulesByKey.get(ruleKey);
@@ -1036,7 +1070,6 @@ public interface RulesDefinition {
/**
* @since 7.1
- * @return
*/
public RuleScope scope() {
return scope;
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java
index 8398f50a2ae..0eefbb801ae 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java
@@ -103,6 +103,7 @@ public class RulesDefinitionTest {
RulesDefinition.Repository repo = context.repository("findbugs");
assertThat(repo.rules()).hasSize(2);
+ assertThat(repo.isExternal()).isFalse();
RulesDefinition.Rule rule = repo.rule("NPE");
assertThat(rule.scope()).isEqualTo(RuleScope.ALL);
@@ -166,6 +167,51 @@ public class RulesDefinitionTest {
}
@Test
+ public void define_external_rules() {
+ RulesDefinition.NewRepository newRepo = context.createExternalRepository("eslint", "js");
+ newRepo.createRule("NPE")
+ .setName("Detect NPE")
+ .setHtmlDescription("Detect <code>java.lang.NullPointerException</code>")
+ .setSeverity(Severity.BLOCKER)
+ .setInternalKey("/something")
+ .setStatus(RuleStatus.BETA)
+ .setTags("one", "two")
+ .setScope(RuleScope.ALL)
+ .addTags("two", "three", "four");
+
+ newRepo.createRule("ABC").setName("ABC").setMarkdownDescription("ABC");
+ newRepo.done();
+
+ assertThat(context.repository("eslint")).isNull();
+ RulesDefinition.Repository repo = context.repository("external_eslint");
+ assertThat(repo.rules()).hasSize(2);
+ assertThat(repo.isExternal()).isTrue();
+
+ RulesDefinition.Rule rule = repo.rule("NPE");
+ assertThat(rule.scope()).isEqualTo(RuleScope.ALL);
+ assertThat(rule.key()).isEqualTo("NPE");
+ assertThat(rule.name()).isEqualTo("Detect NPE");
+ assertThat(rule.severity()).isEqualTo(Severity.BLOCKER);
+ assertThat(rule.htmlDescription()).isEqualTo("Detect <code>java.lang.NullPointerException</code>");
+ assertThat(rule.markdownDescription()).isNull();
+ assertThat(rule.tags()).containsOnly("one", "two", "three", "four");
+ assertThat(rule.params()).isEmpty();
+ assertThat(rule.internalKey()).isEqualTo("/something");
+ assertThat(rule.template()).isFalse();
+ assertThat(rule.status()).isEqualTo(RuleStatus.BETA);
+ assertThat(rule.toString()).isEqualTo("[repository=external_eslint, key=NPE]");
+ assertThat(rule.repository()).isSameAs(repo);
+
+ RulesDefinition.Rule otherRule = repo.rule("ABC");
+ assertThat(otherRule.htmlDescription()).isNull();
+ assertThat(otherRule.markdownDescription()).isEqualTo("ABC");
+
+ // test equals() and hashCode()
+ assertThat(rule).isEqualTo(rule).isNotEqualTo(otherRule).isNotEqualTo("NPE").isNotEqualTo(null);
+ assertThat(rule.hashCode()).isEqualTo(rule.hashCode());
+ }
+
+ @Test
public void define_rule_parameters() {
RulesDefinition.NewRepository newFindbugs = context.createRepository("findbugs", "java");
RulesDefinition.NewRule newNpe = newFindbugs.createRule("NPE").setName("NPE").setHtmlDescription("NPE");
diff --git a/tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java b/tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java
index 5aa33193a6b..c71120b7ade 100644
--- a/tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java
+++ b/tests/src/test/java/org/sonarqube/tests/issue/ExternalIssueTest.java
@@ -80,6 +80,33 @@ public class ExternalIssueTest {
}
@Test
+ public void should_import_external_issues_with_details_provided_by_code_analyzers() {
+ noIssues();
+ ruleExistsWithDetails("external_xoo:OneExternalIssueWithDetailsPerLine");
+
+ ItUtils.runProjectAnalysis(orchestrator, "shared/xoo-sample",
+ "sonar.oneExternalIssueWithDetailsPerLine.activate", "true");
+ List<Issue> issuesList = tester.wsClient().issues().search(new SearchRequest()).getIssuesList();
+ assertThat(issuesList).hasSize(17);
+
+ assertThat(issuesList).allMatch(issue -> "external_xoo:OneExternalIssueWithDetailsPerLine".equals(issue.getRule()));
+ assertThat(issuesList).allMatch(issue -> "This issue is generated on each line and the rule contains details".equals(issue.getMessage()));
+ assertThat(issuesList).allMatch(issue -> Severity.MAJOR.equals(issue.getSeverity()));
+ assertThat(issuesList).allMatch(issue -> RuleType.BUG.equals(issue.getType()));
+ assertThat(issuesList).allMatch(issue -> "sample:src/main/xoo/sample/Sample.xoo".equals(issue.getComponent()));
+ assertThat(issuesList).allMatch(issue -> "OPEN".equals(issue.getStatus()));
+ assertThat(issuesList).allMatch(issue -> issue.getExternalRuleEngine().equals("xoo"));
+
+ ruleExistsWithDetails("external_xoo:OneExternalIssueWithDetailsPerLine");
+
+ // second analysis, issue tracking should work
+ ItUtils.runProjectAnalysis(orchestrator, "shared/xoo-sample",
+ "sonar.oneExternalIssueWithDetailsPerLine.activate", "true");
+ issuesList = tester.wsClient().issues().search(new SearchRequest()).getIssuesList();
+ assertThat(issuesList).hasSize(17);
+ }
+
+ @Test
public void should_import_external_issues_from_json_report_and_create_external_rules() {
noIssues();
ruleDoesntExist("external_externalXoo:rule1");
@@ -127,7 +154,7 @@ public class ExternalIssueTest {
}
- private void ruleExists(String key) {
+ private org.sonarqube.ws.Rules.Rule ruleExists(String key) {
List<org.sonarqube.ws.Rules.Rule> rulesList = tester.wsClient().rules()
.search(new org.sonarqube.ws.client.rules.SearchRequest()
.setRuleKey(key)
@@ -140,6 +167,23 @@ public class ExternalIssueTest {
assertThat(rulesList.get(0).getIsExternal()).isTrue();
assertThat(rulesList.get(0).getTags().getTagsCount()).isEqualTo(0);
assertThat(rulesList.get(0).getScope()).isEqualTo(RuleScope.ALL);
+
+ // if flag to include external is not given, shouldn't be able to find it
+ List<org.sonarqube.ws.Rules.Rule> rulesListWithoutExternal = tester.wsClient().rules()
+ .search(new org.sonarqube.ws.client.rules.SearchRequest()
+ .setRuleKey(key))
+ .getRulesList();
+ assertThat(rulesListWithoutExternal).isEmpty();
+
+ return rulesList.get(0);
+ }
+
+ private void ruleExistsWithDetails(String key) {
+ org.sonarqube.ws.Rules.Rule rule = ruleExists(key);
+ assertThat(rule.getHtmlDesc()).isEqualTo("Generates one external issue in each line");
+ assertThat(rule.getSeverity()).isEqualTo("MAJOR");
+ assertThat(rule.getType()).isEqualTo(RuleType.BUG);
+ assertThat(rule.getName()).isEqualTo("One external issue per line");
}
private void noIssues() {