@@ -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); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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 |
@@ -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 |
@@ -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, |
@@ -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<org.sonar.api.rules.ActiveRule> activeRules; | |||
private final List<BuiltInQualityProfilesDefinition.BuiltInActiveRule> 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<org.sonar.api.rules.ActiveRule> getActiveRules() { | |||
public List<BuiltInQualityProfilesDefinition.BuiltInActiveRule> getActiveRules() { | |||
return activeRules; | |||
} | |||
@@ -65,7 +65,7 @@ public final class BuiltInQProfile { | |||
private String name; | |||
private boolean declaredDefault; | |||
private boolean computedDefault; | |||
private final List<org.sonar.api.rules.ActiveRule> activeRules = new ArrayList<>(); | |||
private final List<BuiltInQualityProfilesDefinition.BuiltInActiveRule> activeRules = new ArrayList<>(); | |||
public Builder setLanguage(String language) { | |||
this.language = language; | |||
@@ -95,7 +95,7 @@ public final class BuiltInQProfile { | |||
return this; | |||
} | |||
Builder addRules(List<org.sonar.api.rules.ActiveRule> rules) { | |||
Builder addRules(List<BuiltInQualityProfilesDefinition.BuiltInActiveRule> rules) { | |||
this.activeRules.addAll(rules); | |||
return this; | |||
} |
@@ -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<ProfileDefinition> 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(); | |||
} | |||
} |
@@ -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<ActiveRuleParamDto> insertActiveRuleParams(DbSession session, org.sonar.api.rules.ActiveRule activeRule, RuleKey ruleKey, ActiveRuleDto activeRuleDto) { | |||
Map<String, String> valuesByParamKey = activeRule.getActiveRuleParams() | |||
private List<ActiveRuleParamDto> insertActiveRuleParams(DbSession session, BuiltInQualityProfilesDefinition.BuiltInActiveRule activeRule, RuleKey ruleKey, | |||
ActiveRuleDto activeRuleDto) { | |||
Map<String, String> 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 -> { |
@@ -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<ProfileDefinition> definitions; | |||
private final List<BuiltInQualityProfilesDefinition> definitions; | |||
private List<BuiltInQProfile> 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<String, RulesProfile> 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<String, Map<String, BuiltInQualityProfile>> 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<String, RulesProfile> buildRulesProfilesByLanguage() { | |||
ListMultimap<String, RulesProfile> 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<String, RulesProfile> byLang) { | |||
byLang.asMap().entrySet() | |||
private Map<String, Map<String, BuiltInQualityProfile>> validateAndClean(BuiltInQualityProfilesDefinition.Context context) { | |||
Map<String, Map<String, BuiltInQualityProfile>> 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<RulesProfile> profiles = entry.getValue(); | |||
Collection<BuiltInQualityProfile> 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<BuiltInQProfile> toFlatList(ListMultimap<String, RulesProfile> rulesProfilesByLanguage) { | |||
Map<String, List<BuiltInQProfile.Builder>> buildersByLanguage = Multimaps.asMap(rulesProfilesByLanguage) | |||
private static List<BuiltInQProfile> toFlatList(Map<String, Map<String, BuiltInQualityProfile>> rulesProfilesByLanguage) { | |||
Map<String, List<BuiltInQProfile.Builder>> 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</li> | |||
* </ul> | |||
*/ | |||
private static List<BuiltInQProfile.Builder> toQualityProfileBuilders(Map.Entry<String, List<RulesProfile>> rulesProfilesByLanguage) { | |||
String language = rulesProfilesByLanguage.getKey(); | |||
private static List<BuiltInQProfile.Builder> toQualityProfileBuilders(Map.Entry<String, Map<String, BuiltInQualityProfile>> rulesProfilesByLanguageAndName) { | |||
String language = rulesProfilesByLanguageAndName.getKey(); | |||
// use a LinkedHashMap to keep order of insertion of RulesProfiles | |||
Map<String, BuiltInQProfile.Builder> 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<BuiltInQProfile> toQualityProfiles(List<BuiltInQProfile.Builder> builders) { |
@@ -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<String, String> 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<String, String> 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); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} | |||
} |
@@ -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); | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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<ActiveRuleDto> activeRules, RuleDefinitionDto rule, RulePriority severity) { | |||
ActiveRuleDto activeRule = findRule(activeRules, rule).get(); | |||
@@ -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) { |
@@ -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; |
@@ -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 <code>AnnotationProfileParser</code> and <code>XMLProfileParser</code> 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); |
@@ -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; | |||
} |
@@ -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; | |||
@@ -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<ActiveRuleParam> 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 | |||
*/ |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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<String, Map<String, BuiltInQualityProfile>> profilesByLanguageAndName = new HashMap<>(); | |||
/** | |||
* New builder for {@link BuiltInQualityProfile}. | |||
* <br> | |||
* 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<String, Map<String, BuiltInQualityProfile>> 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<NewBuiltInActiveRule> 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<RuleKey, NewBuiltInActiveRule> 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<NewBuiltInActiveRule> 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<BuiltInActiveRule> 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<RuleKey, BuiltInActiveRule> activeRulesByKey; | |||
private BuiltInQualityProfileImpl(NewBuiltInQualityProfileImpl newProfile) { | |||
this.name = newProfile.name(); | |||
this.language = newProfile.language(); | |||
this.isDefault = newProfile.isDefault(); | |||
Map<RuleKey, BuiltInActiveRule> 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<BuiltInActiveRule> 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<String, NewOverriddenParam> 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<NewOverriddenParam> 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<String, OverriddenParam> overriddenParams; | |||
private BuiltInActiveRule(NewBuiltInActiveRule newBuiltInActiveRule) { | |||
this.repoKey = newBuiltInActiveRule.repoKey(); | |||
this.ruleKey = newBuiltInActiveRule.ruleKey(); | |||
this.overriddenSeverity = newBuiltInActiveRule.overriddenSeverity; | |||
Map<String, OverriddenParam> 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<OverriddenParam> 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); | |||
} |
@@ -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; |
@@ -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 { | |||
} |
@@ -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<String, Map<String, BuiltInQualityProfile>> 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<String, Map<String, BuiltInQualityProfile>> 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<String, Map<String, BuiltInQualityProfile>> 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<String, Map<String, BuiltInQualityProfile>> 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<String, Map<String, BuiltInQualityProfile>> define(Consumer<BuiltInQualityProfilesDefinition.Context> consumer) { | |||
BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); | |||
new FakeProfile(consumer).define(context); | |||
return context.profilesByLanguageAndName(); | |||
} | |||
private class FakeProfile implements BuiltInQualityProfilesDefinition { | |||
private Consumer<Context> consumer; | |||
public FakeProfile(Consumer<Context> consumer) { | |||
this.consumer = consumer; | |||
} | |||
@Override | |||
public void define(Context context) { | |||
consumer.accept(context); | |||
} | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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 |
@@ -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); |
@@ -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"); | |||
} | |||
} |
@@ -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)); | |||
} |