From: Julien HENRY Date: Tue, 12 Sep 2017 16:58:14 +0000 (+0200) Subject: SONAR-5236 Create new API to register built-in quality profiles X-Git-Tag: 6.6-RC1~246 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=4c5d6a27222a8eff31fadf1457a4e6c5b2e1911a;p=sonarqube.git SONAR-5236 Create new API to register built-in quality profiles --- 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 a61386c233f..922bd8fdda0 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 @@ -60,6 +60,7 @@ import org.sonar.xoo.rule.SaveDataTwiceSensor; import org.sonar.xoo.rule.Xoo2BasicProfile; import org.sonar.xoo.rule.Xoo2SonarWayProfile; import org.sonar.xoo.rule.XooBasicProfile; +import org.sonar.xoo.rule.XooBuiltInQualityProfilesDefinition; import org.sonar.xoo.rule.XooEmptyProfile; import org.sonar.xoo.rule.XooFakeExporter; import org.sonar.xoo.rule.XooFakeImporter; @@ -161,6 +162,9 @@ public class XooPlugin implements Plugin { if (context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(5, 5))) { context.addExtension(CpdTokenizerSensor.class); } + if (context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(6,6))) { + context.addExtension(XooBuiltInQualityProfilesDefinition.class); + } } } diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooBuiltInQualityProfilesDefinition.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooBuiltInQualityProfilesDefinition.java new file mode 100644 index 00000000000..41ac7e39f1c --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooBuiltInQualityProfilesDefinition.java @@ -0,0 +1,37 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.xoo.Xoo; + +import static org.sonar.xoo.rule.XooRulesDefinition.XOO_REPOSITORY; + +public class XooBuiltInQualityProfilesDefinition implements BuiltInQualityProfilesDefinition { + @Override + public void define(Context context) { + NewBuiltInQualityProfile profile = context.createBuiltInQualityProfile("test BuiltInQualityProfilesDefinition", Xoo.KEY); + profile.setDefault(false); + NewBuiltInActiveRule rule = profile.activateRule(XOO_REPOSITORY, HasTagSensor.RULE_KEY); + rule.overrideSeverity("BLOCKER"); + rule.overrideParam("tag", "TODO"); + profile.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 94fdc279df1..1e71ad76efc 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 @@ -46,4 +46,12 @@ public class XooPluginTest { new XooPlugin().define(context); assertThat(context.getExtensions()).hasSize(49).contains(CpdTokenizerSensor.class); } + + @Test + public void provide_extensions_for_6_6() { + SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("6.6"), SonarQubeSide.SCANNER); + Plugin.Context context = new Plugin.Context(runtime); + new XooPlugin().define(context); + assertThat(context.getExtensions()).hasSize(50).contains(CpdTokenizerSensor.class); + } } diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooBuiltInQualityProfilesDefinitionTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooBuiltInQualityProfilesDefinitionTest.java new file mode 100644 index 00000000000..ce80f0f5eb1 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooBuiltInQualityProfilesDefinitionTest.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInQualityProfile; + +import static org.assertj.core.api.Assertions.assertThat; + +public class XooBuiltInQualityProfilesDefinitionTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private XooBuiltInQualityProfilesDefinition underTest = new XooBuiltInQualityProfilesDefinition(); + + @Test + public void test_built_in_quality_profile() { + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + + underTest.define(context); + + BuiltInQualityProfile profile = context.profile("xoo", "test BuiltInQualityProfilesDefinition"); + assertThat(profile.isDefault()).isFalse(); + assertThat(profile.name()).isEqualTo("test BuiltInQualityProfilesDefinition"); + assertThat(profile.language()).isEqualTo("xoo"); + assertThat(profile.rules()).hasSize(1); + BuiltInQualityProfilesDefinition.BuiltInActiveRule activeRule = profile.rule(RuleKey.of("xoo", "HasTag")); + assertThat(activeRule.overriddenSeverity()).isEqualTo("BLOCKER"); + assertThat(activeRule.overriddenParams()).hasSize(1); + assertThat(activeRule.overriddenParam("tag").overriddenValue()).isEqualTo("TODO"); + } +} diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java index 759a323fa29..ffa45d1cda1 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java @@ -34,6 +34,7 @@ import org.sonar.api.resources.Languages; import org.sonar.api.resources.ResourceTypes; import org.sonar.api.rules.AnnotationRuleParser; import org.sonar.api.rules.XMLRuleParser; +import org.sonar.api.server.profile.BuiltInQualityProfileAnnotationLoader; import org.sonar.api.server.rule.RulesDefinitionXmlLoader; import org.sonar.api.utils.Durations; import org.sonar.api.utils.System2; @@ -318,6 +319,7 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer { XMLProfileParser.class, XMLProfileSerializer.class, AnnotationProfileParser.class, + BuiltInQualityProfileAnnotationLoader.class, Rules.QProfiles.class, // rule diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index ff887bef380..467fe263aac 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -123,7 +123,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getComponentAdapters()) .hasSize( CONTAINER_ITSELF - + 72 // level 4 + + 73 // level 4 + 4 // content of CeConfigurationModule + 4 // content of CeQueueModule + 4 // content of CeHttpModule diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index f6c7a77d648..0aca6403bef 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -152,6 +152,7 @@ import org.sonar.server.projecttag.ws.ProjectTagsWsModule; import org.sonar.server.property.InternalPropertiesImpl; import org.sonar.server.property.ws.PropertiesWs; import org.sonar.server.qualitygate.QualityGateModule; +import org.sonar.server.qualityprofile.BuiltInQProfileDefinitionsBridge; import org.sonar.server.qualityprofile.BuiltInQProfileRepositoryImpl; import org.sonar.server.qualityprofile.QProfileBackuperImpl; import org.sonar.server.qualityprofile.QProfileComparison; @@ -276,6 +277,7 @@ public class PlatformLevel4 extends PlatformLevel { BillingValidationsProxyImpl.class, // quality profile + BuiltInQProfileDefinitionsBridge.class, BuiltInQProfileRepositoryImpl.class, ActiveRuleIndexer.class, XMLProfileParser.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfile.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfile.java index 2109187bef0..32658aa49b4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfile.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfile.java @@ -23,16 +23,16 @@ import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.List; import javax.annotation.concurrent.Immutable; -import org.sonar.api.profiles.ProfileDefinition; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; /** - * Represent a Quality Profile as computed from {@link ProfileDefinition} provided by installed plugins. + * Represent a Quality Profile as computed from {@link BuiltInQualityProfilesDefinition} provided by installed plugins. */ @Immutable public final class BuiltInQProfile { private final QProfileName qProfileName; private final boolean isDefault; - private final List activeRules; + private final List activeRules; private BuiltInQProfile(Builder builder) { this.qProfileName = new QProfileName(builder.language, builder.name); @@ -56,7 +56,7 @@ public final class BuiltInQProfile { return isDefault; } - public List getActiveRules() { + public List getActiveRules() { return activeRules; } @@ -65,7 +65,7 @@ public final class BuiltInQProfile { private String name; private boolean declaredDefault; private boolean computedDefault; - private final List activeRules = new ArrayList<>(); + private final List activeRules = new ArrayList<>(); public Builder setLanguage(String language) { this.language = language; @@ -95,7 +95,7 @@ public final class BuiltInQProfile { return this; } - Builder addRules(List rules) { + Builder addRules(List rules) { this.activeRules.addAll(rules); return this; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileDefinitionsBridge.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileDefinitionsBridge.java new file mode 100644 index 00000000000..7adb0d8e249 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileDefinitionsBridge.java @@ -0,0 +1,91 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.server.qualityprofile; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.sonar.api.profiles.ProfileDefinition; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rules.ActiveRuleParam; +import org.sonar.api.rules.RulePriority; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.log.Profiler; + +import static java.lang.String.format; + +/** + * Bridge between deprecated {@link ProfileDefinition} API and new {@link BuiltInQualityProfilesDefinition} + */ +public class BuiltInQProfileDefinitionsBridge implements BuiltInQualityProfilesDefinition { + private static final Logger LOGGER = Loggers.get(BuiltInQProfileDefinitionsBridge.class); + + private final List definitions; + + /** + * Requires for pico container when no {@link ProfileDefinition} is defined at all + */ + public BuiltInQProfileDefinitionsBridge() { + this(new ProfileDefinition[0]); + } + + public BuiltInQProfileDefinitionsBridge(ProfileDefinition... definitions) { + this.definitions = ImmutableList.copyOf(definitions); + } + + @Override + public void define(Context context) { + Profiler profiler = Profiler.create(Loggers.get(getClass())); + for (ProfileDefinition definition : definitions) { + profiler.start(); + ValidationMessages validation = ValidationMessages.create(); + RulesProfile profile = definition.createProfile(validation); + validation.log(LOGGER); + if (profile == null) { + profiler.stopDebug(format("Loaded definition %s that return no profile", definition)); + } else { + if (!validation.hasErrors()) { + define(context, profile); + } + profiler.stopDebug(format("Loaded deprecated profile definition %s for language %s", profile.getName(), profile.getLanguage())); + } + } + } + + private static void define(Context context, RulesProfile profile) { + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage()) + .setDefault(profile.getDefaultProfile().booleanValue()); + + for (org.sonar.api.rules.ActiveRule ar : profile.getActiveRules()) { + NewBuiltInActiveRule newActiveRule = newQp.activateRule(ar.getRepositoryKey(), ar.getRuleKey()); + RulePriority overriddenSeverity = ar.getOverriddenSeverity(); + if (overriddenSeverity != null) { + newActiveRule.overrideSeverity(overriddenSeverity.name()); + } + for (ActiveRuleParam param : ar.getActiveRuleParams()) { + newActiveRule.overrideParam(param.getKey(), param.getValue()); + } + } + newQp.done(); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImpl.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImpl.java index 9698e5f5fde..cb07b2f2562 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImpl.java @@ -36,7 +36,7 @@ import java.util.stream.Collectors; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.ActiveRuleParam; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; import org.sonar.api.server.rule.RuleParamType; import org.sonar.api.utils.System2; import org.sonar.core.util.UuidFactory; @@ -139,8 +139,8 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert { return dto; } - private ActiveRuleChange insertActiveRule(DbSession dbSession, RulesProfileDto rulesProfileDto, org.sonar.api.rules.ActiveRule activeRule, long now) { - RuleKey ruleKey = RuleKey.of(activeRule.getRepositoryKey(), activeRule.getRuleKey()); + private ActiveRuleChange insertActiveRule(DbSession dbSession, RulesProfileDto rulesProfileDto, BuiltInQualityProfilesDefinition.BuiltInActiveRule activeRule, long now) { + RuleKey ruleKey = RuleKey.of(activeRule.repoKey(), activeRule.ruleKey()); RuleDefinitionDto ruleDefinitionDto = ruleRepository.getDefinition(ruleKey) .orElseThrow(() -> new IllegalStateException("RuleDefinition not found for key " + ruleKey)); @@ -148,7 +148,7 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert { dto.setProfileId(rulesProfileDto.getId()); dto.setRuleId(ruleDefinitionDto.getId()); dto.setKey(ActiveRuleKey.of(rulesProfileDto, ruleDefinitionDto.getKey())); - dto.setSeverity(firstNonNull(activeRule.getSeverity().name(), ruleDefinitionDto.getSeverityString())); + dto.setSeverity(firstNonNull(activeRule.overriddenSeverity(), ruleDefinitionDto.getSeverityString())); dto.setUpdatedAt(now); dto.setCreatedAt(now); dbClient.activeRuleDao().insert(dbSession, dto); @@ -161,10 +161,11 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert { return change; } - private List insertActiveRuleParams(DbSession session, org.sonar.api.rules.ActiveRule activeRule, RuleKey ruleKey, ActiveRuleDto activeRuleDto) { - Map valuesByParamKey = activeRule.getActiveRuleParams() + private List insertActiveRuleParams(DbSession session, BuiltInQualityProfilesDefinition.BuiltInActiveRule activeRule, RuleKey ruleKey, + ActiveRuleDto activeRuleDto) { + Map valuesByParamKey = activeRule.overriddenParams() .stream() - .collect(MoreCollectors.uniqueIndex(ActiveRuleParam::getParamKey, ActiveRuleParam::getValue)); + .collect(MoreCollectors.uniqueIndex(BuiltInQualityProfilesDefinition.OverriddenParam::key, BuiltInQualityProfilesDefinition.OverriddenParam::overriddenValue)); return ruleRepository.getRuleParams(ruleKey) .stream() .map(param -> { diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java index 321c69fb9a8..c41bfa866a6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java @@ -19,49 +19,41 @@ */ package org.sonar.server.qualityprofile; -import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Multimaps; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; -import org.sonar.api.profiles.ProfileDefinition; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.Languages; -import org.sonar.api.utils.ValidationMessages; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInQualityProfile; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.core.util.stream.MoreCollectors; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; -import static java.lang.String.format; -import static org.apache.commons.lang.StringUtils.isNotEmpty; -import static org.apache.commons.lang.StringUtils.lowerCase; public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository { private static final Logger LOGGER = Loggers.get(BuiltInQProfileRepositoryImpl.class); private static final String DEFAULT_PROFILE_NAME = "Sonar way"; private final Languages languages; - private final List definitions; + private final List definitions; private List qProfiles; /** - * Requires for pico container when no {@link ProfileDefinition} is defined at all + * Requires for pico container when no {@link BuiltInQualityProfilesDefinition} is defined at all */ public BuiltInQProfileRepositoryImpl(Languages languages) { - this(languages, new ProfileDefinition[0]); + this(languages, new BuiltInQualityProfilesDefinition[0]); } - public BuiltInQProfileRepositoryImpl(Languages languages, ProfileDefinition... definitions) { + public BuiltInQProfileRepositoryImpl(Languages languages, BuiltInQualityProfilesDefinition... definitions) { this.languages = languages; this.definitions = ImmutableList.copyOf(definitions); } @@ -70,9 +62,12 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository public void initialize() { checkState(qProfiles == null, "initialize must be called only once"); - Profiler profiler = Profiler.create(Loggers.get(getClass())).startInfo("Load quality profiles"); - ListMultimap rulesProfilesByLanguage = buildRulesProfilesByLanguage(); - validateAndClean(rulesProfilesByLanguage); + Profiler profiler = Profiler.create(LOGGER).startInfo("Load quality profiles"); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + for (BuiltInQualityProfilesDefinition definition : definitions) { + definition.define(context); + } + Map> rulesProfilesByLanguage = validateAndClean(context); this.qProfiles = toFlatList(rulesProfilesByLanguage); profiler.stopDebug(); } @@ -84,49 +79,27 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository return qProfiles; } - /** - * @return profiles by language - */ - private ListMultimap buildRulesProfilesByLanguage() { - ListMultimap byLang = ArrayListMultimap.create(); - Profiler profiler = Profiler.create(Loggers.get(getClass())); - for (ProfileDefinition definition : definitions) { - profiler.start(); - ValidationMessages validation = ValidationMessages.create(); - RulesProfile profile = definition.createProfile(validation); - validation.log(LOGGER); - if (profile == null) { - profiler.stopDebug(format("Loaded definition %s that return no profile", definition)); - } else { - if (!validation.hasErrors()) { - checkArgument(isNotEmpty(profile.getName()), "Profile created by Definition %s can't have a blank name", definition); - byLang.put(lowerCase(profile.getLanguage(), Locale.ENGLISH), profile); - } - profiler.stopDebug(format("Loaded definition %s for language %s", profile.getName(), profile.getLanguage())); - } - } - return byLang; - } - - private void validateAndClean(ListMultimap byLang) { - byLang.asMap().entrySet() + private Map> validateAndClean(BuiltInQualityProfilesDefinition.Context context) { + Map> profilesByLanguageAndName = context.profilesByLanguageAndName(); + profilesByLanguageAndName.entrySet() .removeIf(entry -> { String language = entry.getKey(); if (languages.get(language) == null) { LOGGER.info("Language {} is not installed, related Quality profiles are ignored", language); return true; } - Collection profiles = entry.getValue(); + Collection profiles = entry.getValue().values(); if (profiles.isEmpty()) { LOGGER.warn("No Quality profiles defined for language: {}", language); return true; } return false; }); + return profilesByLanguageAndName; } - private static List toFlatList(ListMultimap rulesProfilesByLanguage) { - Map> buildersByLanguage = Multimaps.asMap(rulesProfilesByLanguage) + private static List toFlatList(Map> rulesProfilesByLanguage) { + Map> buildersByLanguage = rulesProfilesByLanguage .entrySet() .stream() .collect(MoreCollectors.uniqueIndex(Map.Entry::getKey, BuiltInQProfileRepositoryImpl::toQualityProfileBuilders)); @@ -151,14 +124,14 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository * RulesProfile with a given name * */ - private static List toQualityProfileBuilders(Map.Entry> rulesProfilesByLanguage) { - String language = rulesProfilesByLanguage.getKey(); + private static List toQualityProfileBuilders(Map.Entry> rulesProfilesByLanguageAndName) { + String language = rulesProfilesByLanguageAndName.getKey(); // use a LinkedHashMap to keep order of insertion of RulesProfiles Map qualityProfileBuildersByName = new LinkedHashMap<>(); - for (RulesProfile rulesProfile : rulesProfilesByLanguage.getValue()) { + for (BuiltInQualityProfile builtInProfile : rulesProfilesByLanguageAndName.getValue().values()) { qualityProfileBuildersByName.compute( - rulesProfile.getName(), - (name, existingBuilder) -> updateOrCreateBuilder(language, existingBuilder, rulesProfile)); + builtInProfile.name(), + (name, existingBuilder) -> updateOrCreateBuilder(language, existingBuilder, builtInProfile)); } return ImmutableList.copyOf(qualityProfileBuildersByName.values()); } @@ -175,20 +148,16 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository return true; } - private static BuiltInQProfile.Builder updateOrCreateBuilder(String language, @Nullable BuiltInQProfile.Builder existingBuilder, RulesProfile rulesProfile) { + private static BuiltInQProfile.Builder updateOrCreateBuilder(String language, @Nullable BuiltInQProfile.Builder existingBuilder, BuiltInQualityProfile builtInProfile) { BuiltInQProfile.Builder builder = existingBuilder; if (builder == null) { builder = new BuiltInQProfile.Builder() .setLanguage(language) - .setName(rulesProfile.getName()); + .setName(builtInProfile.name()); } - Boolean defaultProfile = rulesProfile.getDefaultProfile(); - boolean declaredDefault = defaultProfile != null && defaultProfile; return builder - // if there is multiple RulesProfiles with the same name, if at least one is declared default, - // then QualityProfile is flagged as declared default - .setDeclaredDefault(builder.isDeclaredDefault() || declaredDefault) - .addRules(rulesProfile.getActiveRules()); + .setDeclaredDefault(builtInProfile.isDefault()) + .addRules(builtInProfile.rules()); } private static List toQualityProfiles(List builders) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java index 1a1d0bd507b..8f1331f91d7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java @@ -24,7 +24,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.ActiveRuleParam; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInActiveRule; import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -59,18 +60,16 @@ public class BuiltInQProfileUpdateImpl implements BuiltInQProfileUpdate { }); // these rules are not part of the built-in profile anymore - toBeDeactivated.forEach(ruleKey -> - changes.addAll(ruleActivator.deactivateOnBuiltInRulesProfile(dbSession, ruleProfile, ruleKey, false))); + toBeDeactivated.forEach(ruleKey -> changes.addAll(ruleActivator.deactivateOnBuiltInRulesProfile(dbSession, ruleProfile, ruleKey, false))); activeRuleIndexer.commitAndIndex(dbSession, changes); return changes; } - private static RuleActivation convert(org.sonar.api.rules.ActiveRule ar) { - String severity = ar.getSeverity() != null ? ar.getSeverity().name() : null; - Map params = ar.getActiveRuleParams().stream() - .collect(MoreCollectors.uniqueIndex(ActiveRuleParam::getKey, ActiveRuleParam::getValue)); - return RuleActivation.create(ar.getRule().ruleKey(), severity, params); + private static RuleActivation convert(BuiltInActiveRule ar) { + Map params = ar.overriddenParams().stream() + .collect(MoreCollectors.uniqueIndex(BuiltInQualityProfilesDefinition.OverriddenParam::key, BuiltInQualityProfilesDefinition.OverriddenParam::overriddenValue)); + return RuleActivation.create(RuleKey.of(ar.repoKey(), ar.ruleKey()), ar.overriddenSeverity(), params); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileDefinitionsBridgeTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileDefinitionsBridgeTest.java new file mode 100644 index 00000000000..47d3e31d4eb --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileDefinitionsBridgeTest.java @@ -0,0 +1,106 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.server.qualityprofile; + +import java.util.Arrays; +import org.junit.Test; +import org.sonar.api.profiles.ProfileDefinition; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.ActiveRule; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleParam; +import org.sonar.api.rules.RulePriority; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInActiveRule; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInQualityProfile; +import org.sonar.api.utils.ValidationMessages; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.groups.Tuple.tuple; + +public class BuiltInQProfileDefinitionsBridgeTest { + + @Test + public void noProfileDefinitions() { + BuiltInQProfileDefinitionsBridge bridge = new BuiltInQProfileDefinitionsBridge(); + + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + bridge.define(context); + + assertThat(context.profilesByLanguageAndName()).isEmpty(); + } + + @Test + public void bridgeProfileDefinitions() { + BuiltInQProfileDefinitionsBridge bridge = new BuiltInQProfileDefinitionsBridge(new Profile1(), new NullProfile(), new ProfileWithError()); + + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + bridge.define(context); + + assertThat(context.profilesByLanguageAndName()).hasSize(1); + assertThat(context.profilesByLanguageAndName().get("xoo")).hasSize(1); + + BuiltInQualityProfile profile1 = context.profile("xoo", "Profile 1"); + assertThat(profile1).isNotNull(); + assertThat(profile1.rules()).hasSize(3); + BuiltInActiveRule defaultSeverity = profile1.rule(RuleKey.of("repo1", "defaultSeverity")); + assertThat(defaultSeverity).isNotNull(); + assertThat(defaultSeverity.overriddenSeverity()).isNull(); + assertThat(defaultSeverity.overriddenParams()).isEmpty(); + + assertThat(profile1.rule(RuleKey.of("repo1", "overrideSeverity")).overriddenSeverity()).isEqualTo(Severity.CRITICAL); + + assertThat(profile1.rule(RuleKey.of("repo1", "overrideParam")).overriddenParams()) + .extracting(BuiltInQualityProfilesDefinition.OverriddenParam::key, BuiltInQualityProfilesDefinition.OverriddenParam::overriddenValue).containsOnly(tuple("param", "value")); + } + + private class Profile1 extends ProfileDefinition { + @Override + public RulesProfile createProfile(ValidationMessages validation) { + RulesProfile profile1 = RulesProfile.create("Profile 1", "xoo"); + + profile1.activateRule(Rule.create("repo1", "defaultSeverity"), null); + profile1.activateRule(Rule.create("repo1", "overrideSeverity"), RulePriority.CRITICAL); + Rule ruleWithParam = Rule.create("repo1", "overrideParam"); + ruleWithParam.setParams(Arrays.asList(new RuleParam(ruleWithParam, "param", "", ""))); + ActiveRule arWithParam = profile1.activateRule(ruleWithParam, null); + arWithParam.setParameter("param", "value"); + + return profile1; + } + } + + private class NullProfile extends ProfileDefinition { + @Override + public RulesProfile createProfile(ValidationMessages validation) { + return null; + } + } + + private class ProfileWithError extends ProfileDefinition { + @Override + public RulesProfile createProfile(ValidationMessages validation) { + validation.addErrorText("Foo"); + return RulesProfile.create("Profile with errors", "xoo"); + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImplTest.java index 3d85ade5b77..9a66ceefe03 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImplTest.java @@ -27,6 +27,8 @@ import org.junit.rules.ExpectedException; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.rule.Severity; import org.sonar.api.rules.RulePriority; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile; import org.sonar.api.utils.System2; import org.sonar.api.utils.internal.AlwaysIncreasingSystem2; import org.sonar.core.util.SequenceUuidFactory; @@ -48,8 +50,6 @@ import org.sonar.server.util.TypeValidations; import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; -import static org.sonar.api.rules.RulePriority.CRITICAL; -import static org.sonar.api.rules.RulePriority.MAJOR; public class BuiltInQProfileInsertImplTest { @@ -102,11 +102,15 @@ public class BuiltInQProfileInsertImplTest { OrganizationDto org = db.organizations().insert(); RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo")); RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo")); - RulesProfile apiProfile = RulesProfile.create("the name", "xoo"); - activeRule(apiProfile, rule1, CRITICAL); - activeRule(apiProfile, rule2, MAJOR); - BuiltInQProfile builtIn = builtInQProfileRepository.create(apiProfile); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("the name", "xoo"); + + newQp.activateRule(rule1.getRepositoryKey(), rule1.getRuleKey()).overrideSeverity(Severity.CRITICAL); + newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR); + newQp.done(); + + BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name")); call(builtIn); verifyTableSize("rules_profiles", 1); @@ -122,9 +126,11 @@ public class BuiltInQProfileInsertImplTest { @Test public void flag_profile_as_default_on_organization_if_declared_as_default_by_api() { OrganizationDto org = db.organizations().insert(); - RulesProfile apiProfile = RulesProfile.create("the name", "xoo"); - apiProfile.setDefaultProfile(true); - BuiltInQProfile builtIn = builtInQProfileRepository.create(apiProfile); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("the name", "xoo").setDefault(true); + newQp.done(); + + BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name")); call(builtIn); @@ -135,26 +141,28 @@ public class BuiltInQProfileInsertImplTest { @Test public void existing_default_profile_in_organization_must_not_be_changed() { - RulesProfile apiProfile = RulesProfile.create("the name", "xoo"); - apiProfile.setDefaultProfile(true); - BuiltInQProfile builtIn = builtInQProfileRepository.create(apiProfile); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("the name", "xoo").setDefault(true); + newQp.done(); + BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name")); OrganizationDto org = db.organizations().insert(); - QProfileDto currentDefault = db.qualityProfiles().insert(org, p -> p.setLanguage(apiProfile.getLanguage())); + QProfileDto currentDefault = db.qualityProfiles().insert(org, p -> p.setLanguage("xoo")); db.qualityProfiles().setAsDefault(currentDefault); call(builtIn); - QProfileDto defaultProfile = db.getDbClient().qualityProfileDao().selectDefaultProfile(dbSession, org, apiProfile.getLanguage()); + QProfileDto defaultProfile = db.getDbClient().qualityProfileDao().selectDefaultProfile(dbSession, org, "xoo"); assertThat(defaultProfile.getKee()).isEqualTo(currentDefault.getKee()); } @Test public void dont_flag_profile_as_default_on_organization_if_not_declared_as_default_by_api() { OrganizationDto org = db.organizations().insert(); - RulesProfile apiProfile = RulesProfile.create("the name", "xoo"); - apiProfile.setDefaultProfile(false); - BuiltInQProfile builtIn = builtInQProfileRepository.create(apiProfile); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("the name", "xoo").setDefault(false); + newQp.done(); + BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name")); call(builtIn); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImplTest.java index b541eabe956..4c838744595 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImplTest.java @@ -22,15 +22,12 @@ package org.sonar.server.qualityprofile; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Random; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.sonar.api.profiles.ProfileDefinition; -import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; -import org.sonar.api.utils.ValidationMessages; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; import org.sonar.server.language.LanguageTesting; import static java.util.Arrays.asList; @@ -83,28 +80,6 @@ public class BuiltInQProfileRepositoryImplTest { assertThat(underTest.get()).isEmpty(); } - @Test - public void initialize_throws_IAE_if_profileDefinition_creates_RulesProfile_with_null_name() { - DummyProfileDefinition definition = new DummyProfileDefinition("foo", null, false); - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(new Languages(), definition); - - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Profile created by Definition " + definition + " can't have a blank name"); - - underTest.initialize(); - } - - @Test - public void initialize_throws_IAE_if_profileDefinition_creates_RulesProfile_with_empty_name() { - DummyProfileDefinition definition = new DummyProfileDefinition("foo", "", false); - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(new Languages(), definition); - - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Profile created by Definition " + definition + " can't have a blank name"); - - underTest.initialize(); - } - @Test public void initialize_makes_single_profile_of_a_language_default_even_if_not_flagged_as_so() { BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(new Languages(FOO_LANGUAGE), new DummyProfileDefinition("foo", "foo1", false)); @@ -134,7 +109,7 @@ public class BuiltInQProfileRepositoryImplTest { Collections.shuffle(definitions); String firstName = definitions.get(0).getName(); String secondName = definitions.get(1).getName(); - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(new Languages(FOO_LANGUAGE), definitions.toArray(new ProfileDefinition[0])); + BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(new Languages(FOO_LANGUAGE), definitions.toArray(new BuiltInQualityProfilesDefinition[0])); underTest.initialize(); @@ -154,32 +129,6 @@ public class BuiltInQProfileRepositoryImplTest { underTest.initialize(); } - @Test - public void initialize_creates_profile_as_default_even_if_only_one_profile_with_given_name_has_default_flag_true() { - String name = "doh"; - boolean flag = new Random().nextBoolean(); - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(new Languages(FOO_LANGUAGE), - new DummyProfileDefinition("foo", name, flag), new DummyProfileDefinition("foo", name, !flag)); - - underTest.initialize(); - - assertThat(underTest.get()) - .extracting(BuiltInQProfile::getLanguage, BuiltInQProfile::isDefault) - .containsExactly(tuple("foo", true)); - } - - @Test - public void initialize_creates_single_profile_if_several_profile_have_the_same_name_for_a_given_language() { - BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(new Languages(FOO_LANGUAGE), - new DummyProfileDefinition("foo", "aName", true), new DummyProfileDefinition("foo", "aName", true)); - - underTest.initialize(); - - assertThat(underTest.get()) - .extracting(BuiltInQProfile::getLanguage, BuiltInQProfile::getName) - .containsExactlyInAnyOrder(tuple(FOO_LANGUAGE.getKey(), "aName")); - } - @Test public void initialize_creates_profile_Sonar_Way_as_default_if_none_other_is_defined_default_for_a_given_language() { BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl( @@ -227,7 +176,7 @@ public class BuiltInQProfileRepositoryImplTest { .containsExactly("goo"); } - private static final class DummyProfileDefinition extends ProfileDefinition { + private static final class DummyProfileDefinition implements BuiltInQualityProfilesDefinition { private final String language; private final String name; private final boolean defaultProfile; @@ -239,23 +188,15 @@ public class BuiltInQProfileRepositoryImplTest { } @Override - public RulesProfile createProfile(ValidationMessages validation) { - RulesProfile res = RulesProfile.create(name, language); - res.setDefaultProfile(defaultProfile); - return res; - } - - String getLanguage() { - return language; + public void define(Context context) { + context.createBuiltInQualityProfile(name, language) + .setDefault(defaultProfile).done(); } String getName() { return name; } - boolean isDefaultProfile() { - return defaultProfile; - } } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryRule.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryRule.java index 8c93d260da9..bcc6073526e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryRule.java @@ -24,8 +24,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.rules.ExternalResource; -import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.Language; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; import static com.google.common.base.Preconditions.checkState; @@ -60,13 +60,13 @@ public class BuiltInQProfileRepositoryRule extends ExternalResource implements B return add(language, profileName, false); } - public BuiltInQProfile add(Language language, String profileName, boolean isDefault, org.sonar.api.rules.ActiveRule... rules) { + public BuiltInQProfile add(Language language, String profileName, boolean isDefault, BuiltInQualityProfilesDefinition.BuiltInActiveRule... rules) { BuiltInQProfile builtIn = create(language, profileName, isDefault, rules); profiles.add(builtIn); return builtIn; } - public BuiltInQProfile create(Language language, String profileName, boolean isDefault, org.sonar.api.rules.ActiveRule... rules) { + public BuiltInQProfile create(Language language, String profileName, boolean isDefault, BuiltInQualityProfilesDefinition.BuiltInActiveRule... rules) { return new BuiltInQProfile.Builder() .setLanguage(language.getKey()) .setName(profileName) @@ -75,12 +75,12 @@ public class BuiltInQProfileRepositoryRule extends ExternalResource implements B .build(); } - public BuiltInQProfile create(RulesProfile api) { + public BuiltInQProfile create(BuiltInQualityProfilesDefinition.BuiltInQualityProfile api) { return new BuiltInQProfile.Builder() - .setLanguage(api.getLanguage()) - .setName(api.getName()) - .setDeclaredDefault(api.getDefaultProfile()) - .addRules(new ArrayList<>(api.getActiveRules())) + .setLanguage(api.language()) + .setName(api.name()) + .setDeclaredDefault(api.isDefault()) + .addRules(new ArrayList<>(api.rules())) .build(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java index 6a0642b003b..12cdf2fb380 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java @@ -25,7 +25,10 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rule.Severity; import org.sonar.api.rules.RulePriority; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile; import org.sonar.api.utils.System2; import org.sonar.api.utils.internal.TestSystem2; import org.sonar.db.DbTester; @@ -81,10 +84,12 @@ public class BuiltInQProfileUpdateImplTest { public void activate_new_rules() { RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo")); RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo")); - RulesProfile apiProfile = RulesProfile.create("Sonar way", "xoo"); - activateRuleInDef(apiProfile, rule1, CRITICAL); - activateRuleInDef(apiProfile, rule2, MAJOR); - BuiltInQProfile builtIn = builtInProfileRepository.create(apiProfile); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); + newQp.activateRule(rule1.getRepositoryKey(), rule1.getRuleKey()).overrideSeverity(Severity.CRITICAL); + newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR); + newQp.done(); + BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way")); underTest.update(db.getSession(), builtIn, persistedProfile); @@ -98,9 +103,11 @@ public class BuiltInQProfileUpdateImplTest { @Test public void already_activated_rule_is_updated_in_case_of_differences() { RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo")); - RulesProfile apiProfile = RulesProfile.create("Sonar way", "xoo"); - activateRuleInDef(apiProfile, rule, CRITICAL); - BuiltInQProfile builtIn = builtInProfileRepository.create(apiProfile); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); + newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()).overrideSeverity(Severity.CRITICAL); + newQp.done(); + BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way")); activateRuleInDb(persistedProfile, rule, BLOCKER); @@ -115,9 +122,11 @@ public class BuiltInQProfileUpdateImplTest { @Test public void already_activated_rule_is_not_touched_if_no_differences() { RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage("xoo")); - RulesProfile apiProfile = RulesProfile.create("Sonar way", "xoo"); - activateRuleInDef(apiProfile, rule, CRITICAL); - BuiltInQProfile builtIn = builtInProfileRepository.create(apiProfile); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); + newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()).overrideSeverity(Severity.CRITICAL); + newQp.done(); + BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way")); activateRuleInDb(persistedProfile, rule, CRITICAL); @@ -133,9 +142,11 @@ public class BuiltInQProfileUpdateImplTest { public void deactivate_rule_that_is_not_in_built_in_definition_anymore() { RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo")); RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo")); - RulesProfile apiProfile = RulesProfile.create("Sonar way", "xoo"); - activateRuleInDef(apiProfile, rule2, CRITICAL); - BuiltInQProfile builtIn = builtInProfileRepository.create(apiProfile); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); + newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR); + newQp.done(); + BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way")); // built-in definition contains only rule2 // so rule1 must be deactivated @@ -154,10 +165,13 @@ public class BuiltInQProfileUpdateImplTest { RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo")); RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo")); RuleDefinitionDto rule3 = db.rules().insert(r -> r.setLanguage("xoo")); - RulesProfile apiProfile = RulesProfile.create("Sonar way", "xoo"); - activateRuleInDef(apiProfile, rule1, CRITICAL); - activateRuleInDef(apiProfile, rule2, MAJOR); - BuiltInQProfile builtIn = builtInProfileRepository.create(apiProfile); + + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo"); + newQp.activateRule(rule1.getRepositoryKey(), rule1.getRuleKey()).overrideSeverity(Severity.CRITICAL); + newQp.activateRule(rule2.getRepositoryKey(), rule2.getRuleKey()).overrideSeverity(Severity.MAJOR); + newQp.done(); + BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way")); // rule1 must be updated (blocker to critical) // rule2 must be activated @@ -175,7 +189,6 @@ public class BuiltInQProfileUpdateImplTest { assertThatProfileIsMarkedAsUpdated(persistedProfile); } - private static void assertThatRuleIsNewlyActivated(List activeRules, RuleDefinitionDto rule, RulePriority severity) { ActiveRuleDto activeRule = findRule(activeRules, rule).get(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java index 6fbba727fc6..eac458ab215 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java @@ -27,10 +27,11 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.ArgumentCaptor; -import org.sonar.api.profiles.RulesProfile; import org.sonar.api.rule.Severity; -import org.sonar.api.rules.ActiveRule; import org.sonar.api.rules.RulePriority; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInActiveRule; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile; import org.sonar.api.utils.System2; import org.sonar.api.utils.log.LogTester; import org.sonar.core.util.UuidFactoryFast; @@ -57,7 +58,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; -import static org.sonar.api.rules.Rule.create; import static org.sonar.api.rules.RulePriority.MAJOR; import static org.sonar.db.qualityprofile.QualityProfileTesting.newRuleProfileDto; import static org.sonar.server.language.LanguageTesting.newLanguage; @@ -296,15 +296,23 @@ public class RegisterQualityProfilesNotificationTest { } private void addPluginProfile(RulesProfileDto dbProfile, RuleDefinitionDto... dbRules) { - RulesProfile pluginProfile = RulesProfile.create(dbProfile.getName(), dbProfile.getLanguage()); - Arrays.stream(dbRules).forEach(dbRule -> pluginProfile.activateRule(create(dbRule.getRepositoryKey(), dbRule.getRuleKey()), null)); - builtInQProfileRepositoryRule.add(newLanguage(dbProfile.getLanguage()), dbProfile.getName(), false, pluginProfile.getActiveRules().toArray(new ActiveRule[0])); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(dbProfile.getName(), dbProfile.getLanguage()); + + Arrays.stream(dbRules).forEach(dbRule -> newQp.activateRule(dbRule.getRepositoryKey(), dbRule.getRuleKey()).overrideSeverity(Severity.MAJOR)); + newQp.done(); + builtInQProfileRepositoryRule.add(newLanguage(dbProfile.getLanguage()), dbProfile.getName(), false, + context.profile(dbProfile.getLanguage(), dbProfile.getName()).rules().toArray(new BuiltInActiveRule[0])); } private void addPluginProfile(QProfileDto profile, RuleDefinitionDto... dbRules) { - RulesProfile pluginProfile = RulesProfile.create(profile.getName(), profile.getLanguage()); - Arrays.stream(dbRules).forEach(dbRule -> pluginProfile.activateRule(create(dbRule.getRepositoryKey(), dbRule.getRuleKey()), null)); - builtInQProfileRepositoryRule.add(newLanguage(profile.getLanguage()), profile.getName(), false, pluginProfile.getActiveRules().toArray(new ActiveRule[0])); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage()); + + Arrays.stream(dbRules).forEach(dbRule -> newQp.activateRule(dbRule.getRepositoryKey(), dbRule.getRuleKey()).overrideSeverity(Severity.MAJOR)); + newQp.done(); + builtInQProfileRepositoryRule.add(newLanguage(profile.getLanguage()), profile.getName(), false, + context.profile(profile.getLanguage(), profile.getName()).rules().toArray(new BuiltInActiveRule[0])); } private RulesProfileDto insertBuiltInProfile(String language) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/AnnotationProfileParser.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/AnnotationProfileParser.java index e029e1f180a..8e94e17a2f2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/AnnotationProfileParser.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/AnnotationProfileParser.java @@ -21,20 +21,23 @@ package org.sonar.api.profiles; import java.util.Collection; import org.apache.commons.lang.StringUtils; +import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleAnnotationUtils; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; -import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.server.ServerSide; +import org.sonar.api.server.profile.BuiltInQualityProfileAnnotationLoader; import org.sonar.api.utils.ValidationMessages; import org.sonar.check.BelongsToProfile; /** * @since 2.3 + * @deprecated since 6.6 use {@link BuiltInQualityProfileAnnotationLoader} */ @ServerSide @ComputeEngineSide +@Deprecated public final class AnnotationProfileParser { private final RuleFinder ruleFinder; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileDefinition.java index 88957329396..2a00e03c392 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileDefinition.java @@ -21,6 +21,7 @@ package org.sonar.api.profiles; import org.sonar.api.ExtensionPoint; import org.sonar.api.server.ServerSide; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; import org.sonar.api.utils.ValidationMessages; /** @@ -28,9 +29,11 @@ import org.sonar.api.utils.ValidationMessages; * The components AnnotationProfileParser and XMLProfileParser can be used to help implementing the method create(). * * @since 2.3 + * @deprecated since 6.6 use {@link BuiltInQualityProfilesDefinition} */ @ServerSide @ExtensionPoint +@Deprecated public abstract class ProfileDefinition { public abstract RulesProfile createProfile(ValidationMessages validation); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java index 6811f3f4683..6a5d3c35cf4 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java @@ -308,10 +308,7 @@ public class RulesProfile implements Cloneable { "The definition of the profile '%s' (language '%s') contains multiple occurrences of the '%s:%s' rule. The plugin which declares this profile should fix this.", getName(), getLanguage(), rule.getRepositoryKey(), rule.getKey())); } - ActiveRule activeRule = new ActiveRule(); - activeRule.setRule(rule); - activeRule.setRulesProfile(this); - activeRule.setSeverity(optionalSeverity == null ? rule.getSeverity() : optionalSeverity); + ActiveRule activeRule = new ActiveRule(this, rule, optionalSeverity); activeRules.add(activeRule); return activeRule; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileParser.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileParser.java index deefa7663fa..ffd1ca68c7d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileParser.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileParser.java @@ -31,11 +31,11 @@ import org.apache.commons.lang.StringUtils; import org.codehaus.staxmate.SMInputFactory; import org.codehaus.staxmate.in.SMHierarchicCursor; import org.codehaus.staxmate.in.SMInputCursor; +import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.rules.ActiveRule; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; -import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.ValidationMessages; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java index d4d73b831f0..5e3469af78f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java @@ -36,6 +36,7 @@ public class ActiveRule implements Cloneable { private Integer id; private Rule rule; private RulePriority severity; + private RulePriority overriddenSeverity; private RulesProfile rulesProfile; private List activeRuleParams = new ArrayList<>(); private String inheritance; @@ -53,6 +54,7 @@ public class ActiveRule implements Cloneable { @Deprecated public ActiveRule(RulesProfile profile, Rule rule, RulePriority severity) { this.rule = rule; + this.overriddenSeverity = severity; if (severity == null && rule != null) { this.severity = rule.getSeverity(); } else { @@ -119,6 +121,16 @@ public class ActiveRule implements Cloneable { return severity; } + /** + * For internal use + * @since 6.6 + * @deprecated + */ + @Deprecated + public RulePriority getOverriddenSeverity() { + return overriddenSeverity; + } + /** * @since 2.5 */ diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/profile/BuiltInQualityProfileAnnotationLoader.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/profile/BuiltInQualityProfileAnnotationLoader.java new file mode 100644 index 00000000000..869ab39dad8 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/profile/BuiltInQualityProfileAnnotationLoader.java @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.profile; + +import javax.annotation.CheckForNull; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.rules.RuleAnnotationUtils; +import org.sonar.api.server.ServerSide; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInActiveRule; +import org.sonar.check.BelongsToProfile; + +/** + * Read definitions of quality profiles based on the annotation {@link BelongsToProfile} provided by sonar-check-api. It is used + * to feed {@link BuiltInQualityProfilesDefinition}. + * + * @see BuiltInQualityProfilesDefinition + * @since 6.6 + */ +@ServerSide +public class BuiltInQualityProfileAnnotationLoader { + + public void load(BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile builtInProfile, String repositoryKey, Class... annotatedClasses) { + for (Class annotatedClass : annotatedClasses) { + loadActiveRule(builtInProfile, repositoryKey, annotatedClass); + } + } + + @CheckForNull + void loadActiveRule(BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile profile, String repositoryKey, Class clazz) { + BelongsToProfile belongsToProfile = clazz.getAnnotation(BelongsToProfile.class); + if ((belongsToProfile != null) && StringUtils.equals(belongsToProfile.title(), profile.name())) { + String ruleKey = RuleAnnotationUtils.getRuleKey(clazz); + NewBuiltInActiveRule activeRule = profile.activateRule(repositoryKey, ruleKey); + activeRule.overrideSeverity(belongsToProfile.priority().name()); + } + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/profile/BuiltInQualityProfilesDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/profile/BuiltInQualityProfilesDefinition.java new file mode 100644 index 00000000000..84b500d041f --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/profile/BuiltInQualityProfilesDefinition.java @@ -0,0 +1,424 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.profile; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import org.sonar.api.ExtensionPoint; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.server.ServerSide; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.String.format; +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableMap; +import static org.apache.commons.lang.StringUtils.defaultIfEmpty; +import static org.apache.commons.lang.StringUtils.isNotBlank; + +/** + * Define built-in quality profiles which are automatically registered during SonarQube startup. + * We no more provide any facility to load profiles from XML file or annotated classes, but it should + * be straightforward to implement (adapt code of deprecated {@link org.sonar.api.profiles.AnnotationProfileParser} + * or {@link org.sonar.api.profiles.XMLProfileParser} for example). + * + * @since 6.6 + */ +@ServerSide +@ExtensionPoint +public interface BuiltInQualityProfilesDefinition { + + /** + * Instantiated by core but not by plugins, except for their tests. + */ + class Context { + + private final Map> profilesByLanguageAndName = new HashMap<>(); + + /** + * New builder for {@link BuiltInQualityProfile}. + *
+ * A plugin can activate rules in a built in quality profile that is defined by another plugin. + */ + public NewBuiltInQualityProfile createBuiltInQualityProfile(String name, String language) { + return new NewBuiltInQualityProfileImpl(this, name, language); + } + + private void registerProfile(NewBuiltInQualityProfileImpl newProfile) { + String language = newProfile.language(); + String name = newProfile.name(); + Preconditions.checkArgument(!profilesByLanguageAndName.computeIfAbsent(language, l -> new LinkedHashMap<>()).containsKey(name), + "There is already a quality profile with name '%s' for language '%s'", name, language); + profilesByLanguageAndName.get(language).put(name, new BuiltInQualityProfileImpl(newProfile)); + } + + public Map> profilesByLanguageAndName() { + return profilesByLanguageAndName; + } + + public BuiltInQualityProfile profile(String language, String name) { + return profilesByLanguageAndName.computeIfAbsent(language, l -> new LinkedHashMap<>()).get(name); + } + } + + interface NewBuiltInQualityProfile { + + /** + * Set whether this is the default profile for the language. The default profile is used when none is explicitly defined when analyzing a project. + */ + NewBuiltInQualityProfile setDefault(boolean value); + + /** + * Activate a rule with specified key. + * + * @throws IllegalArgumentException if rule is already activated in this profile. + */ + NewBuiltInActiveRule activateRule(String repoKey, String ruleKey); + + Collection activeRules(); + + String language(); + + String name(); + + boolean isDefault(); + + void done(); + } + + class NewBuiltInQualityProfileImpl implements NewBuiltInQualityProfile { + private final Context context; + private final String name; + private final String language; + private boolean isDefault; + private final Map newActiveRules = new HashMap<>(); + + private NewBuiltInQualityProfileImpl(Context context, String name, String language) { + this.context = context; + this.name = name; + this.language = language; + } + + @Override + public NewBuiltInQualityProfile setDefault(boolean value) { + this.isDefault = value; + return this; + } + + @Override + public NewBuiltInActiveRule activateRule(String repoKey, String ruleKey) { + RuleKey ruleKeyObj = RuleKey.of(repoKey, ruleKey); + checkArgument(!newActiveRules.containsKey(ruleKeyObj), "The rule '%s' is already activated", ruleKeyObj); + NewBuiltInActiveRule newActiveRule = new NewBuiltInActiveRule(repoKey, ruleKey); + newActiveRules.put(ruleKeyObj, newActiveRule); + return newActiveRule; + } + + @Override + public String language() { + return language; + } + + @Override + public String name() { + return name; + } + + @Override + public boolean isDefault() { + return isDefault; + } + + @Override + public Collection activeRules() { + return newActiveRules.values(); + } + + @Override + public void done() { + checkArgument(isNotBlank(name), "Built-In Quality Profile can't have a blank name"); + checkArgument(isNotBlank(language), "Built-In Quality Profile can't have a blank language"); + + context.registerProfile(this); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("NewBuiltInQualityProfile{"); + sb.append("name='").append(name).append('\''); + sb.append(", language='").append(language).append('\''); + sb.append(", default='").append(isDefault).append('\''); + sb.append('}'); + return sb.toString(); + } + } + + interface BuiltInQualityProfile { + String name(); + + String language(); + + boolean isDefault(); + + @CheckForNull + BuiltInActiveRule rule(RuleKey ruleKey); + + List rules(); + } + + @Immutable + class BuiltInQualityProfileImpl implements BuiltInQualityProfile { + + private static final Logger LOG = Loggers.get(BuiltInQualityProfilesDefinition.BuiltInQualityProfileImpl.class); + private final String language; + private final String name; + private final boolean isDefault; + private final Map activeRulesByKey; + + private BuiltInQualityProfileImpl(NewBuiltInQualityProfileImpl newProfile) { + this.name = newProfile.name(); + this.language = newProfile.language(); + this.isDefault = newProfile.isDefault(); + + Map ruleBuilder = new HashMap<>(); + for (NewBuiltInActiveRule newActiveRule : newProfile.activeRules()) { + ruleBuilder.put(RuleKey.of(newActiveRule.repoKey, newActiveRule.ruleKey), new BuiltInActiveRule(newActiveRule)); + } + this.activeRulesByKey = unmodifiableMap(ruleBuilder); + } + + @Override + public String language() { + return language; + } + + @Override + public String name() { + return name; + } + + @Override + public boolean isDefault() { + return isDefault; + } + + @Override + @CheckForNull + public BuiltInActiveRule rule(RuleKey ruleKey) { + return activeRulesByKey.get(ruleKey); + } + + @Override + public List rules() { + return unmodifiableList(new ArrayList<>(activeRulesByKey.values())); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BuiltInQualityProfileImpl that = (BuiltInQualityProfileImpl) o; + return language.equals(that.language) && name.equals(that.name); + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + language.hashCode(); + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("BuiltInQualityProfile{"); + sb.append("name='").append(name).append('\''); + sb.append(", language='").append(language).append('\''); + sb.append(", default='").append(isDefault).append('\''); + sb.append('}'); + return sb.toString(); + } + } + + class NewBuiltInActiveRule { + private final String repoKey; + private final String ruleKey; + private String overriddenSeverity = null; + private final Map paramsByKey = new HashMap<>(); + + private NewBuiltInActiveRule(String repoKey, String ruleKey) { + this.repoKey = repoKey; + this.ruleKey = ruleKey; + } + + public String repoKey() { + return this.repoKey; + } + + public String ruleKey() { + return this.ruleKey; + } + + /** + * Override default rule severity in this quality profile. By default the active rule will have the default rule severity. + * @param severity See {@link Severity} constants. + */ + public NewBuiltInActiveRule overrideSeverity(String severity) { + checkArgument(Severity.ALL.contains(severity), "Severity of rule %s is not correct: %s", RuleKey.of(repoKey, ruleKey), severity); + this.overriddenSeverity = severity; + return this; + } + + /** + * Create a parameter with given unique key. Max length of key is 128 characters. + */ + public NewOverriddenParam overrideParam(String paramKey, @Nullable String value) { + checkArgument(!paramsByKey.containsKey(paramKey), "The parameter '%s' was already overridden on the built in active rule %s", paramKey, this); + NewOverriddenParam param = new NewOverriddenParam(paramKey).setOverriddenValue(value); + paramsByKey.put(paramKey, param); + return param; + } + + @CheckForNull + public NewOverriddenParam getOverriddenParam(String paramKey) { + return paramsByKey.get(paramKey); + } + + public Collection getOverriddenParams() { + return paramsByKey.values(); + } + + @Override + public String toString() { + return format("[repository=%s, key=%s]", repoKey, ruleKey); + } + } + + /** + * A rule activated on a built in quality profile. + */ + @Immutable + class BuiltInActiveRule { + private final String repoKey; + private final String ruleKey; + private final String overriddenSeverity; + private final Map overriddenParams; + + private BuiltInActiveRule(NewBuiltInActiveRule newBuiltInActiveRule) { + this.repoKey = newBuiltInActiveRule.repoKey(); + this.ruleKey = newBuiltInActiveRule.ruleKey(); + this.overriddenSeverity = newBuiltInActiveRule.overriddenSeverity; + Map paramsBuilder = new HashMap<>(); + for (NewOverriddenParam newParam : newBuiltInActiveRule.getOverriddenParams()) { + paramsBuilder.put(newParam.key, new OverriddenParam(newParam)); + } + this.overriddenParams = Collections.unmodifiableMap(paramsBuilder); + } + + public String repoKey() { + return repoKey; + } + + public String ruleKey() { + return ruleKey; + } + + @CheckForNull + public String overriddenSeverity() { + return overriddenSeverity; + } + + @CheckForNull + public OverriddenParam overriddenParam(String key) { + return overriddenParams.get(key); + } + + public List overriddenParams() { + return unmodifiableList(new ArrayList<>(overriddenParams.values())); + } + + @Override + public String toString() { + return format("[repository=%s, key=%s]", repoKey, ruleKey); + } + } + + class NewOverriddenParam { + private final String key; + private String overriddenValue; + + private NewOverriddenParam(String key) { + this.key = key; + } + + public String key() { + return key; + } + + /** + * Empty default value will be converted to null. Max length is 4000 characters. + */ + public NewOverriddenParam setOverriddenValue(@Nullable String s) { + this.overriddenValue = defaultIfEmpty(s, null); + return this; + } + } + + @Immutable + class OverriddenParam { + private final String key; + private final String overriddenValue; + + private OverriddenParam(NewOverriddenParam newOverriddenParam) { + this.key = newOverriddenParam.key(); + this.overriddenValue = newOverriddenParam.overriddenValue; + } + + public String key() { + return key; + } + + @Nullable + public String overriddenValue() { + return overriddenValue; + } + + } + + /** + * This method is executed when server is started. + */ + void define(Context context); + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/profile/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/profile/package-info.java new file mode 100644 index 00000000000..495f6f25f64 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/profile/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.api.server.profile; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/profile/BuiltInQualityProfileAnnotationLoaderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/profile/BuiltInQualityProfileAnnotationLoaderTest.java new file mode 100644 index 00000000000..de544d5a02e --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/profile/BuiltInQualityProfileAnnotationLoaderTest.java @@ -0,0 +1,69 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.profile; + +import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile; +import org.sonar.check.BelongsToProfile; +import org.sonar.check.Priority; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BuiltInQualityProfileAnnotationLoaderTest { + + @Test + public void shouldParseAnnotatedClasses() { + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newProfile = context.createBuiltInQualityProfile("Foo way", "java"); + + new BuiltInQualityProfileAnnotationLoader().load(newProfile, "squid", FakeRule.class, RuleNoProfile.class); + newProfile.done(); + + assertThat(context.profile("java", "Foo way").rule(RuleKey.of("squid", "fake")).overriddenSeverity()).isEqualTo(Severity.BLOCKER); + } + + @Test + public void shouldParseOnlyWantedProfile() { + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + NewBuiltInQualityProfile newProfile = context.createBuiltInQualityProfile("Foo way", "java"); + + new BuiltInQualityProfileAnnotationLoader().load(newProfile, "squid", FakeRule.class, RuleOnOtherProfile.class, RuleNoProfile.class); + newProfile.done(); + + assertThat(context.profile("java", "Foo way").rule(RuleKey.of("squid", "fake"))).isNotNull(); + assertThat(context.profile("java", "Foo way").rule(RuleKey.of("squid", "other"))).isNull(); + } +} + +@BelongsToProfile(title = "Other profile", priority = Priority.BLOCKER) +@org.sonar.check.Rule(key = "other", priority = Priority.CRITICAL) +class RuleOnOtherProfile { +} + +@org.sonar.check.Rule(key = "no", priority = Priority.CRITICAL) +class RuleNoProfile { +} + +@BelongsToProfile(title = "Foo way", priority = Priority.BLOCKER) +@org.sonar.check.Rule(key = "fake", priority = Priority.CRITICAL) +class FakeRule { +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/profile/BuiltInQualityProfilesDefinitionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/profile/BuiltInQualityProfilesDefinitionTest.java new file mode 100644 index 00000000000..53def9e1e99 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/profile/BuiltInQualityProfilesDefinitionTest.java @@ -0,0 +1,176 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.profile; + +import java.util.Map; +import java.util.function.Consumer; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInQualityProfile; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInActiveRule; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.groups.Tuple.tuple; + +public class BuiltInQualityProfilesDefinitionTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void coverage() { + assertThat(new BuiltInQualityProfilesDefinition.Context().profile("Foo", "xoo")).isNull(); + } + + @Test + public void createEmptyProfile() { + Map> profiles = define(c -> { + c.createBuiltInQualityProfile("Foo", "xoo").done(); + }); + assertThat(profiles).containsOnlyKeys("xoo"); + assertThat(profiles.get("xoo")).containsOnlyKeys("Foo"); + BuiltInQualityProfile profile = profiles.get("xoo").get("Foo"); + assertThat(profile.name()).isEqualTo("Foo"); + assertThat(profile.language()).isEqualTo("xoo"); + assertThat(profile.isDefault()).isFalse(); + } + + @Test + public void sanityCheck() { + Map> profiles = define(c -> { + NewBuiltInQualityProfile profile1 = c.createBuiltInQualityProfile("Foo1", "xoo"); + NewBuiltInActiveRule rule = profile1.activateRule("repo", "rule"); + profile1.done(); + NewBuiltInQualityProfile profile2 = c.createBuiltInQualityProfile("Foo2", "xoo"); + profile2.done(); + NewBuiltInQualityProfile profile3 = c.createBuiltInQualityProfile("Foo1", "xoo2"); + profile3.done(); + assertThat(profile1).isEqualTo(profile1); + assertThat(profile1).isNotEqualTo(null); + assertThat(profile1).isNotEqualTo("Foo"); + assertThat(profile1).isNotEqualTo(profile2); + assertThat(profile1).isNotEqualTo(profile3); + assertThat(profile1.hashCode()).isNotEqualTo(profile2.hashCode()); + assertThat(profile1.toString()).isEqualTo("NewBuiltInQualityProfile{name='Foo1', language='xoo', default='false'}"); + assertThat(rule.toString()).isEqualTo("[repository=repo, key=rule]"); + }); + BuiltInQualityProfile profile1 = profiles.get("xoo").get("Foo1"); + BuiltInQualityProfile profile2 = profiles.get("xoo").get("Foo2"); + BuiltInQualityProfile profile3 = profiles.get("xoo2").get("Foo1"); + assertThat(profile1).isEqualTo(profile1); + assertThat(profile1).isNotEqualTo(null); + assertThat(profile1).isNotEqualTo("Foo"); + assertThat(profile1).isNotEqualTo(profile2); + assertThat(profile1).isNotEqualTo(profile3); + assertThat(profile1.hashCode()).isNotEqualTo(profile2.hashCode()); + assertThat(profile1.toString()).isEqualTo("BuiltInQualityProfile{name='Foo1', language='xoo', default='false'}"); + assertThat(profile1.rule(RuleKey.of("repo", "rule")).toString()).isEqualTo("[repository=repo, key=rule]"); + } + + @Test + public void createDefaultProfile() { + Map> profiles = define(c -> { + c.createBuiltInQualityProfile("Foo", "xoo") + .setDefault(true) + .done(); + }); + assertThat(profiles).containsOnlyKeys("xoo"); + assertThat(profiles.get("xoo")).containsOnlyKeys("Foo"); + BuiltInQualityProfile profile = profiles.get("xoo").get("Foo"); + assertThat(profile.name()).isEqualTo("Foo"); + assertThat(profile.language()).isEqualTo("xoo"); + assertThat(profile.isDefault()).isTrue(); + } + + @Test + public void duplicateProfile() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("There is already a quality profile with name 'Foo' for language 'xoo'"); + define(c -> { + c.createBuiltInQualityProfile("Foo", "xoo").done(); + c.createBuiltInQualityProfile("Foo", "xoo").done(); + }); + } + + @Test + public void createProfileWithRules() { + Map> profiles = define(c -> { + NewBuiltInQualityProfile profile = c.createBuiltInQualityProfile("Foo", "xoo"); + profile.activateRule("repo", "ruleWithoutParam"); + profile.activateRule("repo", "ruleWithSeverity").overrideSeverity("CRITICAL"); + profile.activateRule("repo", "ruleWithParam").overrideParam("param", "value"); + profile.done(); + }); + assertThat(profiles).containsOnlyKeys("xoo"); + assertThat(profiles.get("xoo")).containsOnlyKeys("Foo"); + BuiltInQualityProfile profile = profiles.get("xoo").get("Foo"); + assertThat(profile.name()).isEqualTo("Foo"); + assertThat(profile.language()).isEqualTo("xoo"); + assertThat(profile.isDefault()).isFalse(); + assertThat(profile.rules()) + .extracting(BuiltInQualityProfilesDefinition.BuiltInActiveRule::repoKey, BuiltInQualityProfilesDefinition.BuiltInActiveRule::ruleKey, + BuiltInQualityProfilesDefinition.BuiltInActiveRule::overriddenSeverity, r -> r.overriddenParams().size()) + .containsOnly( + tuple("repo", "ruleWithoutParam", null, 0), + tuple("repo", "ruleWithSeverity", "CRITICAL", 0), + tuple("repo", "ruleWithParam", null, 1)); + assertThat(profile.rule(RuleKey.of("repo", "ruleWithParam")).overriddenParam("param").key()).isEqualTo("param"); + assertThat(profile.rule(RuleKey.of("repo", "ruleWithParam")).overriddenParam("param").overriddenValue()).isEqualTo("value"); + } + + @Test + public void createProfileWithDuplicateRules() { + + define(c -> { + NewBuiltInQualityProfile profile = c.createBuiltInQualityProfile("Foo", "xoo"); + profile.activateRule("repo", "rule"); + + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("The rule 'repo:rule' is already activated"); + + profile.activateRule("repo", "rule"); + }); + } + + private Map> define(Consumer consumer) { + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + new FakeProfile(consumer).define(context); + return context.profilesByLanguageAndName(); + } + + private class FakeProfile implements BuiltInQualityProfilesDefinition { + + private Consumer consumer; + + public FakeProfile(Consumer consumer) { + this.consumer = consumer; + } + + @Override + public void define(Context context) { + consumer.accept(context); + } + + } + +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionAnnotationLoaderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionAnnotationLoaderTest.java index 10b91fafc9e..8ea8bbab6f0 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionAnnotationLoaderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionAnnotationLoaderTest.java @@ -60,9 +60,9 @@ public class RulesDefinitionAnnotationLoaderTest { RulesDefinition.Context context = new RulesDefinition.Context(); RulesDefinition.NewRepository newRepository = context.createRepository("squid", "java"); NewRule newRule = annotationLoader.loadRule(newRepository, RuleWithProperty.class); - newRule.setName("Overriden name"); + newRule.setName("Overridden name"); newRule.param("property").setDefaultValue("true"); - newRule.param("property").setDescription("Overriden"); + newRule.param("property").setDescription("Overridden"); newRepository.done(); RulesDefinition.Repository repository = context.repository("squid"); @@ -70,14 +70,14 @@ public class RulesDefinitionAnnotationLoaderTest { RulesDefinition.Rule rule = repository.rules().get(0); assertThat(rule.key()).isEqualTo("foo"); assertThat(rule.status()).isEqualTo(RuleStatus.BETA); - assertThat(rule.name()).isEqualTo("Overriden name"); + assertThat(rule.name()).isEqualTo("Overridden name"); assertThat(rule.htmlDescription()).isEqualTo("Foo Bar"); assertThat(rule.severity()).isEqualTo(Severity.BLOCKER); assertThat(rule.params()).hasSize(1); RulesDefinition.Param prop = rule.param("property"); assertThat(prop.key()).isEqualTo("property"); - assertThat(prop.description()).isEqualTo("Overriden"); + assertThat(prop.description()).isEqualTo("Overridden"); assertThat(prop.defaultValue()).isEqualTo("true"); assertThat(prop.type()).isEqualTo(RuleParamType.STRING); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DeprecatedIssueWrapper.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DeprecatedIssueWrapper.java index 5c487b5bf4b..515d6f1e5ac 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DeprecatedIssueWrapper.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DeprecatedIssueWrapper.java @@ -66,8 +66,8 @@ public class DeprecatedIssueWrapper implements Issue { @Override public String severity() { - Severity overridenSeverity = newIssue.overriddenSeverity(); - return overridenSeverity != null ? overridenSeverity.name() : null; + Severity overriddenSeverity = newIssue.overriddenSeverity(); + return overriddenSeverity != null ? overriddenSeverity.name() : null; } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/LocalIssueTracking.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/LocalIssueTracking.java index 758f6eebc16..f513e5c5ca2 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/LocalIssueTracking.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/LocalIssueTracking.java @@ -198,7 +198,7 @@ public class LocalIssueTracking { tracked.setCreationDate(new Date(dto.getCreationDate())); if (dto.getManualSeverity()) { - // Severity overriden by user + // Severity overridden by user tracked.setSeverity(dto.getSeverity().name()); } mergeTo.add(tracked); diff --git a/tests/src/test/java/org/sonarqube/tests/issue/CustomRulesTest.java b/tests/src/test/java/org/sonarqube/tests/issue/CustomRulesTest.java index cae6130cea6..ea4f1dad675 100644 --- a/tests/src/test/java/org/sonarqube/tests/issue/CustomRulesTest.java +++ b/tests/src/test/java/org/sonarqube/tests/issue/CustomRulesTest.java @@ -62,7 +62,7 @@ public class CustomRulesTest extends AbstractIssueTest { Issue issue = issues.get(0); assertThat(issue.ruleKey()).isEqualTo("xoo:MyCustomRule"); assertThat(issue.line()).isEqualTo(2); - // Overriden in quality profile + // Overridden in quality profile assertThat(issue.severity()).isEqualTo("CRITICAL"); } } diff --git a/tests/src/test/java/org/sonarqube/tests/qualityProfile/BuiltInQualityProfilesTest.java b/tests/src/test/java/org/sonarqube/tests/qualityProfile/BuiltInQualityProfilesTest.java index 286b412d1fa..6e9173e042a 100644 --- a/tests/src/test/java/org/sonarqube/tests/qualityProfile/BuiltInQualityProfilesTest.java +++ b/tests/src/test/java/org/sonarqube/tests/qualityProfile/BuiltInQualityProfilesTest.java @@ -63,6 +63,7 @@ public class BuiltInQualityProfilesTest { tuple("Basic", "xoo", true, true), tuple("Sonar way", "xoo", true, false), tuple("empty", "xoo", true, false), + tuple("test BuiltInQualityProfilesDefinition", "xoo", true, false), tuple("Basic", "xoo2", true, true), tuple("Sonar way", "xoo2", true, false)); } @@ -77,6 +78,7 @@ public class BuiltInQualityProfilesTest { tuple("default-organization", "Basic", "xoo", true, true), tuple("default-organization", "Sonar way", "xoo", true, false), tuple("default-organization", "empty", "xoo", true, false), + tuple("default-organization", "test BuiltInQualityProfilesDefinition", "xoo", true, false), tuple("default-organization", "Basic", "xoo2", true, true), tuple("default-organization", "Sonar way", "xoo2", true, false)); }