diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2019-07-12 15:33:56 -0500 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-07-15 20:21:11 +0200 |
commit | 60a97ce418e2271cd55db7d1456673f1a0616d1f (patch) | |
tree | 87508f4ab3500a2de27149d6edb6b1e08318d2da /sonar-plugin-api/src | |
parent | 5227460bf31c1d5359258d5bf21310246d54d76e (diff) | |
download | sonarqube-60a97ce418e2271cd55db7d1456673f1a0616d1f.tar.gz sonarqube-60a97ce418e2271cd55db7d1456673f1a0616d1f.zip |
Bring back server rule implementations to sonar-plugin-api
Diffstat (limited to 'sonar-plugin-api/src')
11 files changed, 1443 insertions, 24 deletions
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 d249ac0bda8..c55ca6ce550 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 @@ -20,8 +20,11 @@ package org.sonar.api.server.rule; import java.net.URL; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -34,8 +37,14 @@ import org.sonar.api.rule.RuleStatus; import org.sonar.api.rules.RuleType; import org.sonar.api.server.ServerSide; import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.rule.internal.DefaultNewRepository; +import org.sonar.api.server.rule.internal.DefaultRepository; import org.sonarsource.api.sonarlint.SonarLintSide; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static org.sonar.api.utils.Preconditions.checkState; + /** * Defines some coding rules of the same repository. For example the Java Findbugs plugin provides an implementation of * this extension point in order to define the rules that it supports. @@ -345,9 +354,77 @@ public interface RulesDefinition { } /** + * This implementation will be removed as soon as analyzers stop instantiating it. + * Use RulesDefinitionContext in sonar-plugin-api-impl. + */ + class Context extends AbstractContext { + private final Map<String, Repository> repositoriesByKey = new HashMap<>(); + private String currentPluginKey; + + @Override + public RulesDefinition.NewRepository createRepository(String key, String language) { + return new DefaultNewRepository(this, key, language, false); + } + + @Override + public RulesDefinition.NewRepository createExternalRepository(String engineId, String language) { + return new DefaultNewRepository(this, RuleKey.EXTERNAL_RULE_REPO_PREFIX + engineId, language, true); + } + + @Override + @Deprecated + public RulesDefinition.NewRepository extendRepository(String key, String language) { + return createRepository(key, language); + } + + @Override + @CheckForNull + public RulesDefinition.Repository repository(String key) { + return repositoriesByKey.get(key); + } + + @Override + public List<RulesDefinition.Repository> repositories() { + return unmodifiableList(new ArrayList<>(repositoriesByKey.values())); + } + + @Override + @Deprecated + public List<RulesDefinition.ExtendedRepository> extendedRepositories(String repositoryKey) { + return emptyList(); + } + + @Override + @Deprecated + public List<RulesDefinition.ExtendedRepository> extendedRepositories() { + return emptyList(); + } + + public void registerRepository(DefaultNewRepository newRepository) { + RulesDefinition.Repository existing = repositoriesByKey.get(newRepository.key()); + if (existing != null) { + String existingLanguage = existing.language(); + checkState(existingLanguage.equals(newRepository.language()), + "The rule repository '%s' must not be defined for two different languages: %s and %s", + newRepository.key(), existingLanguage, newRepository.language()); + } + repositoriesByKey.put(newRepository.key(), new DefaultRepository(newRepository, existing)); + } + + public String currentPluginKey() { + return currentPluginKey; + } + + @Override + public void setCurrentPluginKey(@Nullable String pluginKey) { + this.currentPluginKey = pluginKey; + } + } + + /** * Instantiated by core but not by plugins, except for their tests. */ - abstract class Context { + abstract class AbstractContext { /* * New builder for {@link org.sonar.api.server.rule.RulesDefinition.Repository}. * <br> @@ -651,49 +728,49 @@ public interface RulesDefinition { } @Immutable - interface Rule { + abstract class Rule { - Repository repository(); + public abstract Repository repository(); /** * @since 6.6 the plugin the rule was declared in */ @CheckForNull - String pluginKey(); + public abstract String pluginKey(); - String key(); + public abstract String key(); - String name(); + public abstract String name(); /** * @since 7.1 */ - RuleScope scope(); + public abstract RuleScope scope(); /** * @see NewRule#setType(RuleType) * @since 5.5 */ - RuleType type(); + public abstract RuleType type(); - String severity(); + public abstract String severity(); @CheckForNull - String htmlDescription(); + public abstract String htmlDescription(); @CheckForNull - String markdownDescription(); + public abstract String markdownDescription(); - boolean template(); + public abstract boolean template(); /** * Should this rule be enabled by default. For example in SonarLint standalone. * * @since 6.0 */ - boolean activatedByDefault(); + public abstract boolean activatedByDefault(); - RuleStatus status(); + public abstract RuleStatus status(); /** * @see #type() @@ -702,29 +779,29 @@ public interface RulesDefinition { */ @CheckForNull @Deprecated - String debtSubCharacteristic(); + public abstract String debtSubCharacteristic(); @CheckForNull - DebtRemediationFunction debtRemediationFunction(); + public abstract DebtRemediationFunction debtRemediationFunction(); /** * @deprecated since 5.5, replaced by {@link #gapDescription()} */ @Deprecated @CheckForNull - String effortToFixDescription(); + public abstract String effortToFixDescription(); @CheckForNull - String gapDescription(); + public abstract String gapDescription(); @CheckForNull - Param param(String key); + public abstract Param param(String key); - List<Param> params(); + public abstract List<Param> params(); - Set<String> tags(); + public abstract Set<String> tags(); - Set<String> securityStandards(); + public abstract Set<String> securityStandards(); /** * Deprecated rules keys for this rule. @@ -784,13 +861,13 @@ public interface RulesDefinition { * @see NewRule#addDeprecatedRuleKey(String, String) * @since 7.1 */ - Set<RuleKey> deprecatedRuleKeys(); + public abstract Set<RuleKey> deprecatedRuleKeys(); /** * @see RulesDefinition.NewRule#setInternalKey(String) */ @CheckForNull - String internalKey(); + public abstract String internalKey(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultDebtRemediationFunctions.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultDebtRemediationFunctions.java new file mode 100644 index 00000000000..29c5cebf2a4 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultDebtRemediationFunctions.java @@ -0,0 +1,68 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.server.rule.internal; + +import javax.annotation.Nullable; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.utils.MessageException; + +/** + * Factory of {@link DebtRemediationFunction} that keeps + * a context of rule for better error messages. Used only when declaring rules. + * + * @see RulesDefinition + */ +class DefaultDebtRemediationFunctions implements RulesDefinition.DebtRemediationFunctions { + + private final String repoKey; + private final String key; + + DefaultDebtRemediationFunctions(String repoKey, String key) { + this.repoKey = repoKey; + this.key = key; + } + + @Override + public DebtRemediationFunction linear(String gapMultiplier) { + return create(DefaultDebtRemediationFunction.Type.LINEAR, gapMultiplier, null); + } + + @Override + public DebtRemediationFunction linearWithOffset(String gapMultiplier, String baseEffort) { + return create(DefaultDebtRemediationFunction.Type.LINEAR_OFFSET, gapMultiplier, baseEffort); + } + + @Override + public DebtRemediationFunction constantPerIssue(String baseEffort) { + return create(DefaultDebtRemediationFunction.Type.CONSTANT_ISSUE, null, baseEffort); + } + + @Override + public DebtRemediationFunction create(DebtRemediationFunction.Type type, @Nullable String gapMultiplier, @Nullable String baseEffort) { + try { + return new DefaultDebtRemediationFunction(type, gapMultiplier, baseEffort); + } catch (Exception e) { + throw MessageException.of(String.format("The rule '%s:%s' is invalid : %s ", this.repoKey, this.key, e.getMessage())); + } + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewParam.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewParam.java new file mode 100644 index 00000000000..3c989553b9d --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewParam.java @@ -0,0 +1,85 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.server.rule.internal; + +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.server.rule.RuleParamType; +import org.sonar.api.server.rule.RulesDefinition; + +import static org.apache.commons.lang.StringUtils.defaultIfEmpty; + +public class DefaultNewParam extends RulesDefinition.NewParam { + private final String key; + private String name; + private String description; + private String defaultValue; + private RuleParamType type = RuleParamType.STRING; + + DefaultNewParam(String key) { + this.key = this.name = key; + } + + @Override + public String key() { + return key; + } + + @Override + public DefaultNewParam setName(@Nullable String s) { + // name must never be null. + this.name = StringUtils.defaultIfBlank(s, key); + return this; + } + + @Override + public DefaultNewParam setType(RuleParamType t) { + this.type = t; + return this; + } + + @Override + public DefaultNewParam setDescription(@Nullable String s) { + this.description = StringUtils.defaultIfBlank(s, null); + return this; + } + + @Override + public DefaultNewParam setDefaultValue(@Nullable String s) { + this.defaultValue = defaultIfEmpty(s, null); + return this; + } + + public String name() { + return name; + } + + public String description() { + return description; + } + + public String defaultValue() { + return defaultValue; + } + + public RuleParamType type() { + return type; + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRepository.java new file mode 100644 index 00000000000..f85824c91ef --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRepository.java @@ -0,0 +1,113 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.server.rule.internal; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.server.rule.RulesDefinition; + +import static org.sonar.api.utils.Preconditions.checkArgument; + +public class DefaultNewRepository implements RulesDefinition.NewRepository { + private final RulesDefinition.Context context; + private final String key; + private final boolean isExternal; + private final String language; + private String name; + private final Map<String, RulesDefinition.NewRule> newRules = new HashMap<>(); + + public DefaultNewRepository(RulesDefinition.Context context, String key, String language, boolean isExternal) { + this.context = context; + this.key = key; + this.name = key; + this.language = language; + this.isExternal = isExternal; + } + + @Override + public boolean isExternal() { + return isExternal; + } + + @Override + public String key() { + return key; + } + + public String language() { + return language; + } + + public Map<String, RulesDefinition.NewRule> newRules() { + return newRules; + } + + public String name() { + return name; + } + + @Override + public DefaultNewRepository setName(@Nullable String s) { + if (StringUtils.isNotEmpty(s)) { + this.name = s; + } + return this; + } + + @Override + public RulesDefinition.NewRule createRule(String ruleKey) { + checkArgument(!newRules.containsKey(ruleKey), "The rule '%s' of repository '%s' is declared several times", ruleKey, key); + RulesDefinition.NewRule newRule = new DefaultNewRule(context.currentPluginKey(), key, ruleKey); + newRules.put(ruleKey, newRule); + return newRule; + } + + @CheckForNull + @Override + public RulesDefinition.NewRule rule(String ruleKey) { + return newRules.get(ruleKey); + } + + @Override + public Collection<RulesDefinition.NewRule> rules() { + return newRules.values(); + } + + @Override + public void done() { + // note that some validations can be done here, for example for + // verifying that at least one rule is declared + + context.registerRepository(this); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("NewRepository{"); + sb.append("key='").append(key).append('\''); + sb.append(", language='").append(language).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRule.java new file mode 100644 index 00000000000..44a32d431d8 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultNewRule.java @@ -0,0 +1,350 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.server.rule.internal; + +import java.io.IOException; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.apache.commons.io.IOUtils; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.RuleType; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.rule.RuleTagFormat; +import org.sonar.api.server.rule.RulesDefinition; + +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.commons.lang.StringUtils.isEmpty; +import static org.apache.commons.lang.StringUtils.trimToNull; +import static org.sonar.api.utils.Preconditions.checkArgument; +import static org.sonar.api.utils.Preconditions.checkState; + +class DefaultNewRule extends RulesDefinition.NewRule { + private final String pluginKey; + private final String repoKey; + private final String key; + private RuleType type; + private String name; + private String htmlDescription; + private String markdownDescription; + private String internalKey; + private String severity = Severity.MAJOR; + private boolean template; + private RuleStatus status = RuleStatus.defaultStatus(); + private DebtRemediationFunction debtRemediationFunction; + private String gapDescription; + private final Set<String> tags = new TreeSet<>(); + private final Set<String> securityStandards = new TreeSet<>(); + private final Map<String, RulesDefinition.NewParam> paramsByKey = new HashMap<>(); + private final RulesDefinition.DebtRemediationFunctions functions; + private boolean activatedByDefault; + private RuleScope scope; + private final Set<RuleKey> deprecatedRuleKeys = new TreeSet<>(); + + DefaultNewRule(@Nullable String pluginKey, String repoKey, String key) { + this.pluginKey = pluginKey; + this.repoKey = repoKey; + this.key = key; + this.functions = new DefaultDebtRemediationFunctions(repoKey, key); + } + + @Override + public String key() { + return this.key; + } + + @CheckForNull + @Override + public RuleScope scope() { + return this.scope; + } + + @Override + public DefaultNewRule setScope(RuleScope scope) { + this.scope = scope; + return this; + } + + @Override + public DefaultNewRule setName(String s) { + this.name = trimToNull(s); + return this; + } + + @Override + public DefaultNewRule setTemplate(boolean template) { + this.template = template; + return this; + } + + @Override + public DefaultNewRule setActivatedByDefault(boolean activatedByDefault) { + this.activatedByDefault = activatedByDefault; + return this; + } + + @Override + public DefaultNewRule setSeverity(String s) { + checkArgument(Severity.ALL.contains(s), "Severity of rule %s is not correct: %s", this, s); + this.severity = s; + return this; + } + + @Override + public DefaultNewRule setType(RuleType t) { + this.type = t; + return this; + } + + @Override + public DefaultNewRule setHtmlDescription(@Nullable String s) { + checkState(markdownDescription == null, "Rule '%s' already has a Markdown description", this); + this.htmlDescription = trimToNull(s); + return this; + } + + @Override + public DefaultNewRule setHtmlDescription(@Nullable URL classpathUrl) { + if (classpathUrl != null) { + try { + setHtmlDescription(IOUtils.toString(classpathUrl, UTF_8)); + } catch (IOException e) { + throw new IllegalStateException("Fail to read: " + classpathUrl, e); + } + } else { + this.htmlDescription = null; + } + return this; + } + + @Override + public DefaultNewRule setMarkdownDescription(@Nullable String s) { + checkState(htmlDescription == null, "Rule '%s' already has an HTML description", this); + this.markdownDescription = trimToNull(s); + return this; + } + + @Override + public DefaultNewRule setMarkdownDescription(@Nullable URL classpathUrl) { + if (classpathUrl != null) { + try { + setMarkdownDescription(IOUtils.toString(classpathUrl, UTF_8)); + } catch (IOException e) { + throw new IllegalStateException("Fail to read: " + classpathUrl, e); + } + } else { + this.markdownDescription = null; + } + return this; + } + + @Override + public DefaultNewRule setStatus(RuleStatus status) { + checkArgument(RuleStatus.REMOVED != status, "Status 'REMOVED' is not accepted on rule '%s'", this); + this.status = status; + return this; + } + + @Override + public DefaultNewRule setDebtSubCharacteristic(@Nullable String s) { + return this; + } + + @Override + public RulesDefinition.DebtRemediationFunctions debtRemediationFunctions() { + return functions; + } + + @Override + public DefaultNewRule setDebtRemediationFunction(@Nullable DebtRemediationFunction fn) { + this.debtRemediationFunction = fn; + return this; + } + + @Deprecated + @Override + public DefaultNewRule setEffortToFixDescription(@Nullable String s) { + return setGapDescription(s); + } + + @Override + public DefaultNewRule setGapDescription(@Nullable String s) { + this.gapDescription = s; + return this; + } + + @Override + public RulesDefinition.NewParam createParam(String paramKey) { + checkArgument(!paramsByKey.containsKey(paramKey), "The parameter '%s' is declared several times on the rule %s", paramKey, this); + DefaultNewParam param = new DefaultNewParam(paramKey); + paramsByKey.put(paramKey, param); + return param; + } + + @CheckForNull + @Override + public RulesDefinition.NewParam param(String paramKey) { + return paramsByKey.get(paramKey); + } + + @Override + public Collection<RulesDefinition.NewParam> params() { + return paramsByKey.values(); + } + + @Override + public DefaultNewRule addTags(String... list) { + for (String tag : list) { + RuleTagFormat.validate(tag); + tags.add(tag); + } + return this; + } + + @Override + public DefaultNewRule setTags(String... list) { + tags.clear(); + addTags(list); + return this; + } + + @Override + public DefaultNewRule addOwaspTop10(RulesDefinition.OwaspTop10... standards) { + for (RulesDefinition.OwaspTop10 owaspTop10 : standards) { + String standard = "owaspTop10:" + owaspTop10.name().toLowerCase(Locale.ENGLISH); + securityStandards.add(standard); + } + return this; + } + + @Override + public DefaultNewRule addCwe(int... nums) { + for (int num : nums) { + String standard = "cwe:" + num; + securityStandards.add(standard); + } + return this; + } + + @Override + public DefaultNewRule setInternalKey(@Nullable String s) { + this.internalKey = s; + return this; + } + + void validate() { + if (isEmpty(name)) { + throw new IllegalStateException(format("Name of rule %s is empty", this)); + } + if (isEmpty(htmlDescription) && isEmpty(markdownDescription)) { + throw new IllegalStateException(format("One of HTML description or Markdown description must be defined for rule %s", this)); + } + } + + @Override + public DefaultNewRule addDeprecatedRuleKey(String repository, String key) { + deprecatedRuleKeys.add(RuleKey.of(repository, key)); + return this; + } + + String pluginKey() { + return pluginKey; + } + + String repoKey() { + return repoKey; + } + + RuleType type() { + return type; + } + + String name() { + return name; + } + + String htmlDescription() { + return htmlDescription; + } + + String markdownDescription() { + return markdownDescription; + } + + @CheckForNull + String internalKey() { + return internalKey; + } + + String severity() { + return severity; + } + + boolean template() { + return template; + } + + RuleStatus status() { + return status; + } + + DebtRemediationFunction debtRemediationFunction() { + return debtRemediationFunction; + } + + String gapDescription() { + return gapDescription; + } + + Set<String> tags() { + return tags; + } + + Set<String> securityStandards() { + return securityStandards; + } + + Map<String, RulesDefinition.NewParam> paramsByKey() { + return paramsByKey; + } + + boolean activatedByDefault() { + return activatedByDefault; + } + + Set<RuleKey> deprecatedRuleKeys() { + return deprecatedRuleKeys; + } + + @Override + public String toString() { + return format("[repository=%s, key=%s]", repoKey, key); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultParam.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultParam.java new file mode 100644 index 00000000000..c62e10a03ba --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultParam.java @@ -0,0 +1,87 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.server.rule.internal; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import org.sonar.api.server.rule.RuleParamType; +import org.sonar.api.server.rule.RulesDefinition; + +@Immutable +public class DefaultParam implements RulesDefinition.Param { + private final String key; + private final String name; + private final String description; + private final String defaultValue; + private final RuleParamType type; + + DefaultParam(DefaultNewParam newParam) { + this.key = newParam.key(); + this.name = newParam.name(); + this.description = newParam.description(); + this.defaultValue = newParam.defaultValue(); + this.type = newParam.type(); + } + + @Override + public String key() { + return key; + } + + @Override + public String name() { + return name; + } + + @Override + @Nullable + public String description() { + return description; + } + + @Override + @Nullable + public String defaultValue() { + return defaultValue; + } + + @Override + public RuleParamType type() { + return type; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RulesDefinition.Param that = (RulesDefinition.Param) o; + return key.equals(that.key()); + } + + @Override + public int hashCode() { + return key.hashCode(); + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRepository.java new file mode 100644 index 00000000000..5c534607ad3 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRepository.java @@ -0,0 +1,128 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.server.rule.internal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.utils.log.Loggers; + +import static java.lang.String.format; +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableMap; + +@Immutable +public class DefaultRepository implements RulesDefinition.Repository { + private final String key; + private final String language; + private final String name; + private final boolean isExternal; + private final Map<String, RulesDefinition.Rule> rulesByKey; + + public DefaultRepository(DefaultNewRepository newRepository, @Nullable RulesDefinition.Repository mergeInto) { + this.key = newRepository.key(); + this.language = newRepository.language(); + this.isExternal = newRepository.isExternal(); + Map<String, RulesDefinition.Rule> ruleBuilder = new HashMap<>(); + if (mergeInto != null) { + if (!StringUtils.equals(newRepository.language(), mergeInto.language()) || !StringUtils.equals(newRepository.key(), mergeInto.key())) { + throw new IllegalArgumentException(format("Bug - language and key of the repositories to be merged should be the sames: %s and %s", newRepository, mergeInto)); + } + this.name = StringUtils.defaultIfBlank(mergeInto.name(), newRepository.name()); + for (RulesDefinition.Rule rule : mergeInto.rules()) { + if (!newRepository.key().startsWith("common-") && ruleBuilder.containsKey(rule.key())) { + Loggers.get(getClass()).warn("The rule '{}' of repository '{}' is declared several times", rule.key(), mergeInto.key()); + } + ruleBuilder.put(rule.key(), rule); + } + } else { + this.name = newRepository.name(); + } + for (RulesDefinition.NewRule newRule : newRepository.newRules().values()) { + DefaultNewRule defaultNewRule = (DefaultNewRule) newRule; + defaultNewRule.validate(); + ruleBuilder.put(newRule.key(), new DefaultRule(this, defaultNewRule)); + } + this.rulesByKey = unmodifiableMap(ruleBuilder); + } + + @Override + public String key() { + return key; + } + + @Override + public String language() { + return language; + } + + @Override + public String name() { + return name; + } + + @Override + public boolean isExternal() { + return isExternal; + } + + @Override + @CheckForNull + public RulesDefinition.Rule rule(String ruleKey) { + return rulesByKey.get(ruleKey); + } + + @Override + public List<RulesDefinition.Rule> rules() { + return unmodifiableList(new ArrayList<>(rulesByKey.values())); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultRepository that = (DefaultRepository) o; + return key.equals(that.key); + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Repository{"); + sb.append("key='").append(key).append('\''); + sb.append(", language='").append(language).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRule.java new file mode 100644 index 00000000000..8ba64b94ccf --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/internal/DefaultRule.java @@ -0,0 +1,239 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.server.rule.internal; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.rules.RuleType; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.rule.RuleTagsToTypeConverter; +import org.sonar.api.server.rule.RulesDefinition; + +import static java.lang.String.format; +import static java.util.Collections.unmodifiableList; + +@Immutable +public class DefaultRule extends RulesDefinition.Rule { + private final String pluginKey; + private final RulesDefinition.Repository repository; + private final String repoKey; + private final String key; + private final String name; + private final RuleType type; + private final String htmlDescription; + private final String markdownDescription; + private final String internalKey; + private final String severity; + private final boolean template; + private final DebtRemediationFunction debtRemediationFunction; + private final String gapDescription; + private final Set<String> tags; + private final Set<String> securityStandards; + private final Map<String, RulesDefinition.Param> params; + private final RuleStatus status; + private final boolean activatedByDefault; + private final RuleScope scope; + private final Set<RuleKey> deprecatedRuleKeys; + + DefaultRule(DefaultRepository repository, DefaultNewRule newRule) { + this.pluginKey = newRule.pluginKey(); + this.repository = repository; + this.repoKey = newRule.repoKey(); + this.key = newRule.key(); + this.name = newRule.name(); + this.htmlDescription = newRule.htmlDescription(); + this.markdownDescription = newRule.markdownDescription(); + this.internalKey = newRule.internalKey(); + this.severity = newRule.severity(); + this.template = newRule.template(); + this.status = newRule.status(); + this.debtRemediationFunction = newRule.debtRemediationFunction(); + this.gapDescription = newRule.gapDescription(); + this.scope = newRule.scope() == null ? RuleScope.MAIN : newRule.scope(); + this.type = newRule.type() == null ? RuleTagsToTypeConverter.convert(newRule.tags()) : newRule.type(); + Set<String> tagsBuilder = new TreeSet<>(newRule.tags()); + tagsBuilder.removeAll(RuleTagsToTypeConverter.RESERVED_TAGS); + this.tags = Collections.unmodifiableSet(tagsBuilder); + this.securityStandards = Collections.unmodifiableSet(new TreeSet<>(newRule.securityStandards())); + Map<String, RulesDefinition.Param> paramsBuilder = new HashMap<>(); + for (RulesDefinition.NewParam newParam : newRule.paramsByKey().values()) { + paramsBuilder.put(newParam.key(), new DefaultParam((DefaultNewParam) newParam)); + } + this.params = Collections.unmodifiableMap(paramsBuilder); + this.activatedByDefault = newRule.activatedByDefault(); + this.deprecatedRuleKeys = Collections.unmodifiableSet(new TreeSet<>(newRule.deprecatedRuleKeys())); + } + + @Override + public RulesDefinition.Repository repository() { + return repository; + } + + @Override + @CheckForNull + public String pluginKey() { + return pluginKey; + } + + @Override + public String key() { + return key; + } + + @Override + public String name() { + return name; + } + + @Override + public RuleScope scope() { + return scope; + } + + @Override + public RuleType type() { + return type; + } + + @Override + public String severity() { + return severity; + } + + @Override + @CheckForNull + public String htmlDescription() { + return htmlDescription; + } + + @Override + @CheckForNull + public String markdownDescription() { + return markdownDescription; + } + + @Override + public boolean template() { + return template; + } + + @Override + public boolean activatedByDefault() { + return activatedByDefault; + } + + @Override + public RuleStatus status() { + return status; + } + + @CheckForNull + @Deprecated + @Override + public String debtSubCharacteristic() { + return null; + } + + @CheckForNull + @Override + public DebtRemediationFunction debtRemediationFunction() { + return debtRemediationFunction; + } + + @Deprecated + @CheckForNull + @Override + public String effortToFixDescription() { + return gapDescription(); + } + + @CheckForNull + @Override + public String gapDescription() { + return gapDescription; + } + + @CheckForNull + @Override + public RulesDefinition.Param param(String key) { + return params.get(key); + } + + @Override + public List<RulesDefinition.Param> params() { + return unmodifiableList(new ArrayList<>(params.values())); + } + + @Override + public Set<String> tags() { + return tags; + } + + @Override + public Set<String> securityStandards() { + return securityStandards; + } + + @Override + public Set<RuleKey> deprecatedRuleKeys() { + return deprecatedRuleKeys; + } + + @CheckForNull + @Override + public String internalKey() { + return internalKey; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultRule other = (DefaultRule) o; + return key.equals(other.key) && repoKey.equals(other.repoKey); + } + + @Override + public int hashCode() { + int result = repoKey.hashCode(); + result = 31 * result + key.hashCode(); + return result; + } + + @Override + public String toString() { + return format("[repository=%s, key=%s]", repoKey, key); + } +} + diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultNewRuleTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultNewRuleTest.java new file mode 100644 index 00000000000..33901a1e8f8 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultNewRuleTest.java @@ -0,0 +1,137 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.server.rule.internal; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.rules.RuleType; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.rule.RulesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class DefaultNewRuleTest { + @Rule + public ExpectedException exception = ExpectedException.none(); + + private DefaultNewRule rule = new DefaultNewRule("plugin", "repo", "key"); + + @Test + public void testSimpleSetGet() { + assertThat(rule.pluginKey()).isEqualTo("plugin"); + assertThat(rule.repoKey()).isEqualTo("repo"); + assertThat(rule.key()).isEqualTo("key"); + + rule.setScope(RuleScope.MAIN); + assertThat(rule.scope()).isEqualTo(RuleScope.MAIN); + + rule.setName(" name "); + assertThat(rule.name()).isEqualTo("name"); + + rule.setHtmlDescription(" html "); + assertThat(rule.htmlDescription()).isEqualTo("html"); + + rule.setTemplate(true); + assertThat(rule.template()).isTrue(); + + rule.setActivatedByDefault(true); + assertThat(rule.activatedByDefault()).isTrue(); + + RulesDefinition.NewParam param1 = rule.createParam("param1"); + assertThat(rule.param("param1")).isEqualTo(param1); + assertThat(rule.params()).containsOnly(param1); + + rule.setTags("tag1", "tag2"); + rule.addTags("tag3"); + assertThat(rule.tags()).containsExactly("tag1", "tag2", "tag3"); + + rule.setEffortToFixDescription("effort"); + assertThat(rule.gapDescription()).isEqualTo("effort"); + + rule.setGapDescription("gap"); + assertThat(rule.gapDescription()).isEqualTo("gap"); + + rule.setInternalKey("internal"); + assertThat(rule.internalKey()).isEqualTo("internal"); + + rule.addDeprecatedRuleKey("deprecatedrepo", "deprecatedkey"); + assertThat(rule.deprecatedRuleKeys()).containsOnly(RuleKey.of("deprecatedrepo", "deprecatedkey")); + + rule.setStatus(RuleStatus.READY); + assertThat(rule.status()).isEqualTo(RuleStatus.READY); + + rule.addCwe(12); + rule.addCwe(10); + assertThat(rule.securityStandards()).containsOnly("cwe:10", "cwe:12"); + + rule.setType(RuleType.SECURITY_HOTSPOT); + assertThat(rule.type()).isEqualTo(RuleType.SECURITY_HOTSPOT); + + DebtRemediationFunction f = mock(DebtRemediationFunction.class); + rule.setDebtRemediationFunction(f); + assertThat(rule.debtRemediationFunction()).isEqualTo(f); + + rule.setSeverity("MAJOR"); + assertThat(rule.severity()).isEqualTo("MAJOR"); + } + + @Test + public void validate_fails() { + rule.setHtmlDescription("html"); + exception.expect(IllegalStateException.class); + rule.validate(); + } + + @Test + public void validate_succeeds() { + rule.setHtmlDescription("html"); + rule.setName("name"); + rule.validate(); + } + + @Test + public void set_markdown_description() { + rule.setMarkdownDescription("markdown"); + assertThat(rule.markdownDescription()).isEqualTo("markdown"); + } + @Test + public void fail_if_severity_is_invalid() { + exception.expect(IllegalArgumentException.class); + rule.setSeverity("invalid"); + } + + @Test + public void fail_setting_markdown_if_html_is_set() { + exception.expect(IllegalStateException.class); + rule.setHtmlDescription("html"); + rule.setMarkdownDescription("markdown"); + } + + @Test + public void fail_if_set_status_to_removed() { + exception.expect(IllegalArgumentException.class); + rule.setStatus(RuleStatus.REMOVED); + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRepositoryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRepositoryTest.java new file mode 100644 index 00000000000..c6f443ceb55 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRepositoryTest.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.server.rule.internal; + +import org.junit.Test; +import org.sonar.api.impl.server.RulesDefinitionContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class DefaultRepositoryTest { + @Test + public void create_simple_repo() { + RulesDefinitionContext ctx = mock(RulesDefinitionContext.class); + DefaultNewRepository newRepo = new DefaultNewRepository(ctx, "key", "lang", false); + newRepo.createRule("rule1") + .setName("rule1") + .setHtmlDescription("desc"); + newRepo.setName("name"); + DefaultRepository repo = new DefaultRepository(newRepo, null); + + assertThat(repo.isExternal()).isFalse(); + assertThat(repo.key()).isEqualTo("key"); + assertThat(repo.language()).isEqualTo("lang"); + assertThat(repo.isExternal()).isFalse(); + assertThat(repo.name()).isEqualTo("name"); + assertThat(repo.rules()).extracting(r -> r.key()).containsOnly("rule1"); + + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRuleTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRuleTest.java new file mode 100644 index 00000000000..a3d8b49aaee --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/internal/DefaultRuleTest.java @@ -0,0 +1,88 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.server.rule.internal; + +import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleScope; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.rules.RuleType; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.rule.RulesDefinition; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class DefaultRuleTest { + @Test + public void getters() { + DefaultRepository repo = mock(DefaultRepository.class); + DefaultNewRule rule = new DefaultNewRule("plugin", "repo", "key"); + + rule.setScope(RuleScope.MAIN); + rule.setName(" name "); + rule.setHtmlDescription(" html "); + rule.setTemplate(true); + rule.setActivatedByDefault(true); + RulesDefinition.NewParam param1 = rule.createParam("param1"); + rule.setTags("tag1", "tag2"); + rule.addTags("tag3"); + rule.setEffortToFixDescription("effort"); + rule.setGapDescription("gap"); + rule.setInternalKey("internal"); + rule.addDeprecatedRuleKey("deprecatedrepo", "deprecatedkey"); + rule.setStatus(RuleStatus.READY); + rule.addCwe(12); + rule.addCwe(10); + rule.setType(RuleType.SECURITY_HOTSPOT); + DebtRemediationFunction f = mock(DebtRemediationFunction.class); + rule.setDebtRemediationFunction(f); + rule.setSeverity("MAJOR"); + + DefaultRule defaultRule = new DefaultRule(repo, rule); + assertThat(defaultRule.scope()).isEqualTo(RuleScope.MAIN); + assertThat(defaultRule.name()).isEqualTo("name"); + assertThat(defaultRule.htmlDescription()).isEqualTo("html"); + assertThat(defaultRule.template()).isTrue(); + assertThat(defaultRule.activatedByDefault()).isTrue(); + assertThat(defaultRule.params()).containsOnly(new DefaultParam(new DefaultNewParam("param1"))); + assertThat(defaultRule.tags()).containsOnly("tag1", "tag2", "tag3"); + assertThat(defaultRule.effortToFixDescription()).isEqualTo("gap"); + assertThat(defaultRule.gapDescription()).isEqualTo("gap"); + assertThat(defaultRule.internalKey()).isEqualTo("internal"); + assertThat(defaultRule.deprecatedRuleKeys()).containsOnly(RuleKey.of("deprecatedrepo", "deprecatedkey")); + assertThat(defaultRule.status()).isEqualTo(RuleStatus.READY); + assertThat(rule.securityStandards()).containsOnly("cwe:10", "cwe:12"); + assertThat(defaultRule.type()).isEqualTo(RuleType.SECURITY_HOTSPOT); + assertThat(defaultRule.debtRemediationFunction()).isEqualTo(f); + assertThat(defaultRule.markdownDescription()).isNull(); + assertThat(defaultRule.severity()).isEqualTo("MAJOR"); + } + + @Test + public void to_string() { + DefaultRepository repo = mock(DefaultRepository.class); + DefaultNewRule rule = new DefaultNewRule("plugin", "repo", "key"); + DefaultRule defaultRule = new DefaultRule(repo, rule); + + assertThat(defaultRule.toString()).isEqualTo("[repository=repo, key=key]"); + } +} |