From: Julien Lancelot Date: Thu, 24 Apr 2014 15:15:07 +0000 (+0200) Subject: SONAR-4764 Restore default profiles for a language (Server side, no ui) X-Git-Tag: 4.4-RC1~1406 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=722b816695bbddcdc454063b0fc9386d8d1a2c4f;p=sonarqube.git SONAR-4764 Restore default profiles for a language (Server side, no ui) --- diff --git a/sonar-core/src/main/java/org/sonar/core/template/LoadedTemplateDao.java b/sonar-core/src/main/java/org/sonar/core/template/LoadedTemplateDao.java index 4b793e81132..0077ed68ff0 100644 --- a/sonar-core/src/main/java/org/sonar/core/template/LoadedTemplateDao.java +++ b/sonar-core/src/main/java/org/sonar/core/template/LoadedTemplateDao.java @@ -34,23 +34,30 @@ public class LoadedTemplateDao implements BatchComponent, ServerComponent { public int countByTypeAndKey(String type, String key) { SqlSession session = mybatis.openSession(); - LoadedTemplateMapper mapper = session.getMapper(LoadedTemplateMapper.class); try { - return mapper.countByTypeAndKey(type, key); + return countByTypeAndKey(type, key, session); } finally { MyBatis.closeQuietly(session); } } + public int countByTypeAndKey(String type, String key, SqlSession session) { + return session.getMapper(LoadedTemplateMapper.class).countByTypeAndKey(type, key); + } + + public void insert(LoadedTemplateDto loadedTemplateDto) { SqlSession session = mybatis.openSession(); - LoadedTemplateMapper mapper = session.getMapper(LoadedTemplateMapper.class); try { - mapper.insert(loadedTemplateDto); + insert(loadedTemplateDto, session); session.commit(); } finally { MyBatis.closeQuietly(session); } } + public void insert(LoadedTemplateDto loadedTemplateDto, SqlSession session) { + session.getMapper(LoadedTemplateMapper.class).insert(loadedTemplateDto); + } + } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index 510b3f6f620..ff19bc981f3 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -148,6 +148,10 @@ public class Platform { } } + public void addExtensions(Object... extensions){ + serverComponents.addExtensions(extensions); + } + public ComponentContainer getContainer() { return currentContainer; } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 333305896a5..5d3c7fdcdc8 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -131,6 +131,7 @@ import java.util.List; class ServerComponents { private final Object[] rootComponents; + private Object[] extensions; ServerComponents(Object... rootComponents) { this.rootComponents = rootComponents; @@ -392,6 +393,12 @@ class ServerComponents { pico.addSingleton(StringTypeValidation.class); pico.addSingleton(StringListTypeValidation.class); + if (extensions != null) { + for (Object extension : extensions) { + pico.addSingleton(extension); + } + } + ServerExtensionInstaller extensionRegistrar = pico.getComponentByType(ServerExtensionInstaller.class); extensionRegistrar.installExtensions(pico); @@ -399,6 +406,10 @@ class ServerComponents { executeStartupTaks(pico); } + void addExtensions(Object... extensions){ + this.extensions = extensions; + } + private void executeStartupTaks(ComponentContainer pico) { final ComponentContainer startupContainer = pico.createChild(); startupContainer.addSingleton(GwtPublisher.class); diff --git a/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java b/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java index 4d5b8b2a649..f8d8c64556f 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java +++ b/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java @@ -25,7 +25,6 @@ import com.google.common.collect.Collections2; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.apache.ibatis.session.SqlSession; -import org.elasticsearch.common.collect.Lists; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric.ValueType; @@ -77,7 +76,7 @@ public class QualityGates { private final MyBatis myBatis; public QualityGates(QualityGateDao dao, QualityGateConditionDao conditionDao, MetricFinder metricFinder, PropertiesDao propertiesDao, ComponentDao componentDao, - MyBatis myBatis) { + MyBatis myBatis) { this.dao = dao; this.conditionDao = conditionDao; this.metricFinder = metricFinder; @@ -119,11 +118,12 @@ public class QualityGates { SqlSession session = myBatis.openSession(); try { dao.insert(destinationGate, session); - for(QualityGateConditionDto sourceCondition: conditionDao.selectForQualityGate(sourceId, session)) { + for (QualityGateConditionDto sourceCondition : conditionDao.selectForQualityGate(sourceId, session)) { conditionDao.insert(new QualityGateConditionDto().setQualityGateId(destinationGate.getId()) - .setMetricId(sourceCondition.getMetricId()).setOperator(sourceCondition.getOperator()) - .setWarningThreshold(sourceCondition.getWarningThreshold()).setErrorThreshold(sourceCondition.getErrorThreshold()).setPeriod(sourceCondition.getPeriod()), - session); + .setMetricId(sourceCondition.getMetricId()).setOperator(sourceCondition.getOperator()) + .setWarningThreshold(sourceCondition.getWarningThreshold()).setErrorThreshold(sourceCondition.getErrorThreshold()).setPeriod(sourceCondition.getPeriod()), + session + ); } session.commit(); } finally { @@ -174,7 +174,7 @@ public class QualityGates { } public QualityGateConditionDto createCondition(long qGateId, String metricKey, String operator, - @Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) { + @Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) { checkPermission(UserSession.get()); getNonNullQgate(qGateId); Metric metric = getNonNullMetric(metricKey); @@ -187,7 +187,7 @@ public class QualityGates { } public QualityGateConditionDto updateCondition(long condId, String metricKey, String operator, - @Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) { + @Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) { checkPermission(UserSession.get()); QualityGateConditionDto condition = getNonNullCondition(condId); Metric metric = getNonNullMetric(metricKey); @@ -200,7 +200,7 @@ public class QualityGates { public Collection listConditions(long qGateId) { Collection conditionsForGate = conditionDao.selectForQualityGate(qGateId); - for (QualityGateConditionDto condition: conditionsForGate) { + for (QualityGateConditionDto condition : conditionsForGate) { condition.setMetricKey(metricFinder.findById((int) condition.getMetricId()).getKey()); } return conditionsForGate; @@ -239,14 +239,14 @@ public class QualityGates { try { checkPermission(UserSession.get()); hasWritePermission = true; - } catch(ServerException unallowed) { + } catch (ServerException unallowed) { // Ignored } return hasWritePermission; } private void validateCondition(Metric metric, String operator, @Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) { - List validationMessages = Lists.newArrayList(); + List validationMessages = newArrayList(); validateMetric(metric, validationMessages); validateOperator(metric, operator, validationMessages); validateThresholds(warningThreshold, errorThreshold, validationMessages); diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperations.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperations.java index 70e344d77e8..b5b00e3b619 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperations.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperations.java @@ -27,6 +27,7 @@ import org.apache.ibatis.session.SqlSession; import org.elasticsearch.common.base.Predicate; import org.elasticsearch.common.collect.Iterables; import org.sonar.api.ServerComponent; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.rules.RulePriority; import org.sonar.api.server.rule.RuleParamType; @@ -91,7 +92,7 @@ public class QProfileActiveRuleOperations implements ServerComponent { RuleDto rule = findRuleNotNull(ruleId, session); ActiveRuleDto activeRule = findActiveRule(profileId, ruleId, session); if (activeRule == null) { - createActiveRule(profile.getId(), rule.getId(), severity, userSession, session); + activateRule(profile.getId(), rule.getId(), severity, userSession, session); } else { updateSeverity(activeRule, severity, userSession, session); } @@ -100,7 +101,21 @@ public class QProfileActiveRuleOperations implements ServerComponent { } } - private ActiveRuleDto createActiveRule(int profileId, int ruleId, String severity, UserSession userSession, SqlSession session) { + private ActiveRuleDto activateRule(int profileId, int ruleId, String severity, UserSession userSession, SqlSession session) { + ActiveRuleDto activeRule = createActiveRule(profileId, ruleId, severity, session); + session.commit(); + ProfilesManager.RuleInheritanceActions actions = profilesManager.activated(profileId, activeRule.getId(), getLoggedName(userSession)); + reindexInheritanceResult(actions, session); + return activeRule; + } + + ActiveRuleDto createActiveRule(int profileId, RuleKey ruleKey, String severity, SqlSession session) { + RuleDto rule = ruleDao.selectByKey(ruleKey, session); + QProfileValidations.checkRuleIsNotNull(rule); + return createActiveRule(profileId, rule.getId(), severity, session); + } + + private ActiveRuleDto createActiveRule(int profileId, int ruleId, String severity, SqlSession session) { ActiveRuleDto activeRule = new ActiveRuleDto() .setProfileId(profileId) .setRuleId(ruleId) @@ -118,9 +133,6 @@ public class QProfileActiveRuleOperations implements ServerComponent { activeRuleParams.add(activeRuleParam); activeRuleDao.insert(activeRuleParam, session); } - session.commit(); - ProfilesManager.RuleInheritanceActions actions = profilesManager.activated(profileId, activeRule.getId(), getLoggedName(userSession)); - reindexInheritanceResult(actions, session); return activeRule; } @@ -141,7 +153,7 @@ public class QProfileActiveRuleOperations implements ServerComponent { List ruleIdsToActivate = rules.searchInactiveProfileRuleIds(query); for (Integer ruleId : ruleIdsToActivate) { RuleDto rule = findRuleNotNull(ruleId, session); - createActiveRule(profileId, ruleId, rule.getSeverityString(), userSession, session); + activateRule(profileId, ruleId, rule.getSeverityString(), userSession, session); } return ruleIdsToActivate.size(); } finally { @@ -203,11 +215,11 @@ public class QProfileActiveRuleOperations implements ServerComponent { ActiveRuleParamDto activeRuleParam = findActiveRuleParam(activeRuleId, key, session); ActiveRuleDto activeRule = findActiveRuleNotNull(activeRuleId, session); if (activeRuleParam == null && sanitizedValue != null) { - createActiveRuleParam(activeRule, key, value, userSession, session); + createActiveRuleParam(activeRule, key, sanitizedValue, userSession, session); } else if (activeRuleParam != null && sanitizedValue == null) { deleteActiveRuleParam(activeRule, activeRuleParam, userSession, session); } else if (activeRuleParam != null) { - updateActiveRuleParam(activeRule, activeRuleParam, value, userSession, session); + updateActiveRuleParam(activeRule, activeRuleParam, sanitizedValue, userSession, session); } // If no active rule param and no value -> do nothing @@ -234,6 +246,15 @@ public class QProfileActiveRuleOperations implements ServerComponent { notifyParamsDeleted(activeRule, newArrayList(activeRuleParam), session, userSession); } + void updateActiveRuleParam(ActiveRuleDto activeRule, String key, String value, SqlSession session) { + RuleParamDto ruleParam = findRuleParamNotNull(activeRule.getRulId(), key, session); + ActiveRuleParamDto activeRuleParam = findActiveRuleParam(activeRule.getId(), key, session); + validateParam(ruleParam, value); + + activeRuleParam.setValue(value); + activeRuleDao.update(activeRuleParam, session); + } + private void updateActiveRuleParam(ActiveRuleDto activeRule, ActiveRuleParamDto activeRuleParam, String value, UserSession userSession, SqlSession session) { RuleParamDto ruleParam = findRuleParamNotNull(activeRule.getRulId(), activeRuleParam.getKey(), session); validateParam(ruleParam, value); diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackup.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackup.java index 2bc2c63b67b..c00e297cc86 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackup.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackup.java @@ -20,16 +20,22 @@ package org.sonar.server.qualityprofile; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; import org.apache.ibatis.session.SqlSession; import org.sonar.api.ServerComponent; import org.sonar.api.database.DatabaseSession; +import org.sonar.api.profiles.ProfileDefinition; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.profiles.XMLProfileParser; import org.sonar.api.profiles.XMLProfileSerializer; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.RuleParam; import org.sonar.api.utils.ValidationMessages; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.MyBatis; import org.sonar.core.preview.PreviewCache; +import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.jpa.session.DatabaseSessionFactory; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.user.UserSession; @@ -37,7 +43,10 @@ import org.sonar.server.user.UserSession; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; import static com.google.common.collect.Lists.newArrayList; @@ -52,18 +61,31 @@ public class QProfileBackup implements ServerComponent { private final MyBatis myBatis; private final QProfileLookup qProfileLookup; + private final QProfileOperations qProfileOperations; + private final QProfileActiveRuleOperations qProfileActiveRuleOperations; private final ESActiveRule esActiveRule; + private final List definitions; private final PreviewCache dryRunCache; public QProfileBackup(DatabaseSessionFactory sessionFactory, XMLProfileParser xmlProfileParser, XMLProfileSerializer xmlProfileSerializer, MyBatis myBatis, - QProfileLookup qProfileLookup, ESActiveRule esActiveRule, PreviewCache dryRunCache) { + QProfileLookup qProfileLookup, QProfileOperations qProfileOperations, QProfileActiveRuleOperations qProfileActiveRuleOperations, ESActiveRule esActiveRule, + PreviewCache dryRunCache) { + this(sessionFactory, xmlProfileParser, xmlProfileSerializer, myBatis, qProfileLookup, qProfileOperations, qProfileActiveRuleOperations, esActiveRule, + Collections.emptyList(), dryRunCache); + } + public QProfileBackup(DatabaseSessionFactory sessionFactory, XMLProfileParser xmlProfileParser, XMLProfileSerializer xmlProfileSerializer, MyBatis myBatis, + QProfileLookup qProfileLookup, QProfileOperations qProfileOperations, QProfileActiveRuleOperations qProfileActiveRuleOperations, ESActiveRule esActiveRule, + List definitions, PreviewCache dryRunCache) { this.sessionFactory = sessionFactory; this.xmlProfileParser = xmlProfileParser; this.xmlProfileSerializer = xmlProfileSerializer; this.myBatis = myBatis; this.qProfileLookup = qProfileLookup; + this.qProfileOperations = qProfileOperations; + this.qProfileActiveRuleOperations = qProfileActiveRuleOperations; this.esActiveRule = esActiveRule; + this.definitions = definitions; this.dryRunCache = dryRunCache; } @@ -76,8 +98,7 @@ public class QProfileBackup implements ServerComponent { } /** - * @param deleteExisting is used to not fail if profile exist but to delete it first. - * It's only used by WS, and it should should be soon removed + * @param deleteExisting is used to not fail if profile exist but to delete it first. It's only used by WS, and it should be soon removed. */ public QProfileResult restore(String xmlBackup, boolean deleteExisting) { checkPermission(UserSession.get()); @@ -109,6 +130,63 @@ public class QProfileBackup implements ServerComponent { return result; } + /** + * Restore default profile for a given language. + * If a profile with same name than default profile already exists, an exception will be thrown. + */ + public QProfileResult restoreDefaultProfilesFromLanguage(String language) { + checkPermission(UserSession.get()); + QProfileResult result = new QProfileResult(); + + SqlSession session = myBatis.openSession(); + try { + ListMultimap profilesByName = profilesByName(language, result); + for (Map.Entry> entry : profilesByName.asMap().entrySet()) { + String name = entry.getKey(); + QProfile profile = qProfileOperations.newProfile(name, language, true, UserSession.get(), session); + for (RulesProfile currentRulesProfile : entry.getValue()) { + restoreFromActiveRules(profile, currentRulesProfile, session); + } + esActiveRule.bulkIndexProfile(profile.id(), session); + } + dryRunCache.reportGlobalModification(session); + session.commit(); + } finally { + MyBatis.closeQuietly(session); + } + return result; + } + + private ListMultimap profilesByName(String language, QProfileResult result) { + ListMultimap profilesByName = ArrayListMultimap.create(); + for (ProfileDefinition definition : definitions) { + ValidationMessages validation = ValidationMessages.create(); + RulesProfile profile = definition.createProfile(validation); + if (language.equals(profile.getLanguage())) { + processValidationMessages(validation, result); + profilesByName.put(profile.getName(), profile); + } + } + return profilesByName; + } + + /** + * Used by {@link org.sonar.server.startup.RegisterQualityProfiles} + */ + public void restoreFromActiveRules(QProfile profile, RulesProfile rulesProfile, SqlSession session) { + for (org.sonar.api.rules.ActiveRule activeRule : rulesProfile.getActiveRules()) { + RuleKey ruleKey = RuleKey.of(activeRule.getRepositoryKey(), activeRule.getRuleKey()); + ActiveRuleDto activeRuleDto = qProfileActiveRuleOperations.createActiveRule(profile.id(), ruleKey, activeRule.getSeverity().name(), session); + for (RuleParam param : activeRule.getRule().getParams()) { + String paramKey = param.getKey(); + String value = activeRule.getParameter(param.getKey()); + if (value != null) { + qProfileActiveRuleOperations.updateActiveRuleParam(activeRuleDto, paramKey, value, session); + } + } + } + } + private void checkProfileDoesNotExists(RulesProfile importedProfile, boolean deleteExisting, DatabaseSession hibernateSession) { RulesProfile existingProfile = hibernateSession.getSingleResult(RulesProfile.class, "name", importedProfile.getName(), "language", importedProfile.getLanguage()); if (existingProfile != null && !deleteExisting) { @@ -130,8 +208,8 @@ public class QProfileBackup implements ServerComponent { } throw BadRequestException.of("Fail to restore profile", errors); } - result.setWarnings(messages.getWarnings()); - result.setInfos(messages.getInfos()); + result.addWarnings(messages.getWarnings()); + result.addInfos(messages.getInfos()); } private void checkPermission(UserSession userSession) { diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLookup.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLookup.java index 48d7179a08a..1b38d2a35ec 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLookup.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLookup.java @@ -29,6 +29,7 @@ import org.sonar.core.qualityprofile.db.QualityProfileDao; import org.sonar.core.qualityprofile.db.QualityProfileDto; import javax.annotation.CheckForNull; + import java.util.List; import static com.google.common.collect.Lists.newArrayList; @@ -70,15 +71,24 @@ public class QProfileLookup implements ServerComponent { return null; } - @CheckForNull - public QProfile profile(String name, String language) { - QualityProfileDto dto = findQualityProfile(name, language); + public QProfile profile(String name, String language, SqlSession session) { + QualityProfileDto dto = findQualityProfile(name, language, session); if (dto != null) { return QProfile.from(dto); } return null; } + @CheckForNull + public QProfile profile(String name, String language) { + SqlSession session = myBatis.openSession(); + try { + return profile(name, language, session); + } finally { + MyBatis.closeQuietly(session); + } + } + @CheckForNull public QProfile defaultProfile(String language) { SqlSession session = myBatis.openSession(); @@ -100,14 +110,19 @@ public class QProfileLookup implements ServerComponent { @CheckForNull public QProfile parent(QProfile profile) { - String parent = profile.parent(); - if (parent != null) { - QualityProfileDto parentDto = findQualityProfile(parent, profile.language()); - if (parentDto != null) { - return QProfile.from(parentDto); + SqlSession session = myBatis.openSession(); + try { + String parent = profile.parent(); + if (parent != null) { + QualityProfileDto parentDto = findQualityProfile(parent, profile.language(), session); + if (parentDto != null) { + return QProfile.from(parentDto); + } } + return null; + } finally { + MyBatis.closeQuietly(session); } - return null; } public List children(QProfile profile) { @@ -191,8 +206,8 @@ public class QProfileLookup implements ServerComponent { } @CheckForNull - private QualityProfileDto findQualityProfile(String name, String language) { - return dao.selectByNameAndLanguage(name, language); + private QualityProfileDto findQualityProfile(String name, String language, SqlSession session) { + return dao.selectByNameAndLanguage(name, language, session); } } diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java index 5996a6984eb..114664637b1 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java @@ -131,15 +131,14 @@ public class QProfileOperations implements ServerComponent { checkPermission(userSession); SqlSession session = myBatis.openSession(); try { - deleteProfile(profileId, userSession, session); + deleteProfile(profileId, session); session.commit(); } finally { MyBatis.closeQuietly(session); } } - private void deleteProfile(int profileId, UserSession userSession, SqlSession session) { - checkPermission(userSession); + public void deleteProfile(int profileId, SqlSession session) { QualityProfileDto profile = findNotNull(profileId, session); if (!profileLookup.isDeletable(QProfile.from(profile), session)) { throw new BadRequestException("This profile can not be deleted"); diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRepositoryExporter.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRepositoryExporter.java index dfbcf2a3c46..a6a5becc61b 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRepositoryExporter.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRepositoryExporter.java @@ -90,7 +90,7 @@ public class QProfileRepositoryExporter implements ServerComponent { public String exportToXml(QProfile profile, String pluginKey) { DatabaseSession session = sessionFactory.getSession(); RulesProfile rulesProfile = session.getSingleResult(RulesProfile.class, "id", profile.id()); - if (profile == null) { + if (rulesProfile == null) { throw new NotFoundException("This profile does not exists."); } ProfileExporter exporter = getProfileExporter(pluginKey); @@ -127,8 +127,8 @@ public class QProfileRepositoryExporter implements ServerComponent { } throw BadRequestException.of("Fail to import profile", errors); } - result.setWarnings(messages.getWarnings()); - result.setInfos(messages.getInfos()); + result.addWarnings(messages.getWarnings()); + result.addInfos(messages.getInfos()); } private ActiveRuleDto toActiveRuleDto(ActiveRule activeRule, int profileId) { diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResult.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResult.java index 96c47c98d3e..81fbb63faa3 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResult.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResult.java @@ -40,8 +40,8 @@ public class QProfileResult { return warnings; } - public QProfileResult setWarnings(List warnings) { - this.warnings = warnings; + public QProfileResult addWarnings(List warnings) { + this.warnings.addAll(warnings); return this; } @@ -49,8 +49,8 @@ public class QProfileResult { return infos; } - public QProfileResult setInfos(List infos) { - this.infos = infos; + public QProfileResult addInfos(List infos) { + this.infos.addAll(infos); return this; } diff --git a/sonar-server/src/main/java/org/sonar/server/startup/RegisterQualityProfiles.java b/sonar-server/src/main/java/org/sonar/server/startup/RegisterQualityProfiles.java index bf58747770f..1b76f943c65 100644 --- a/sonar-server/src/main/java/org/sonar/server/startup/RegisterQualityProfiles.java +++ b/sonar-server/src/main/java/org/sonar/server/startup/RegisterQualityProfiles.java @@ -25,21 +25,23 @@ import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; import org.apache.commons.lang.StringUtils; +import org.apache.ibatis.session.SqlSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.database.DatabaseSession; import org.sonar.api.profiles.ProfileDefinition; import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.rules.*; import org.sonar.api.utils.SonarException; import org.sonar.api.utils.TimeProfiler; import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.persistence.MyBatis; import org.sonar.core.template.LoadedTemplateDao; import org.sonar.core.template.LoadedTemplateDto; -import org.sonar.jpa.session.DatabaseSessionFactory; import org.sonar.server.platform.PersistentSettings; -import org.sonar.server.qualityprofile.ESActiveRule; +import org.sonar.server.qualityprofile.*; import org.sonar.server.rule.RegisterRules; +import org.sonar.server.user.UserSession; + +import javax.annotation.Nullable; import java.util.*; @@ -48,115 +50,112 @@ public class RegisterQualityProfiles { private static final Logger LOGGER = LoggerFactory.getLogger(RegisterQualityProfiles.class); private static final String DEFAULT_PROFILE_NAME = "Sonar way"; - private final List definitions; private final LoadedTemplateDao loadedTemplateDao; - private final RuleFinder ruleFinder; + private final QProfileBackup qProfileBackup; + private final QProfileOperations qProfileOperations; + private final QProfileLookup qProfileLookup; private final ESActiveRule esActiveRule; - private final DatabaseSessionFactory sessionFactory; private final PersistentSettings settings; - private DatabaseSession session = null; - - public RegisterQualityProfiles(List definitions, - PersistentSettings settings, - RuleFinder ruleFinder, - ESActiveRule esActiveRule, - LoadedTemplateDao loadedTemplateDao, - DatabaseSessionFactory sessionFactory, - RegisterRules registerRulesBefore) { + private final List definitions; + private final MyBatis myBatis; + + public RegisterQualityProfiles(MyBatis myBatis, + PersistentSettings settings, + ESActiveRule esActiveRule, + LoadedTemplateDao loadedTemplateDao, + QProfileBackup qProfileBackup, + QProfileOperations qProfileOperations, + QProfileLookup qProfileLookup, + RegisterRules registerRulesBefore) { + this(myBatis, settings, esActiveRule, loadedTemplateDao, qProfileBackup, qProfileOperations, qProfileLookup, registerRulesBefore, + Collections.emptyList()); + } + + public RegisterQualityProfiles(MyBatis myBatis, + PersistentSettings settings, + ESActiveRule esActiveRule, + LoadedTemplateDao loadedTemplateDao, + QProfileBackup qProfileBackup, + QProfileOperations qProfileOperations, + QProfileLookup qProfileLookup, + RegisterRules registerRulesBefore, + List definitions) { + this.myBatis = myBatis; this.settings = settings; - this.ruleFinder = ruleFinder; this.esActiveRule = esActiveRule; + this.qProfileBackup = qProfileBackup; + this.qProfileOperations = qProfileOperations; + this.qProfileLookup = qProfileLookup; this.definitions = definitions; this.loadedTemplateDao = loadedTemplateDao; - this.sessionFactory = sessionFactory; - } - - public RegisterQualityProfiles(PersistentSettings settings, - RuleFinder ruleFinder, - ESActiveRule esActiveRule, - LoadedTemplateDao loadedTemplateDao, - DatabaseSessionFactory sessionFactory, - RegisterRules registerRulesBefore) { - this(Collections.emptyList(), settings, ruleFinder, esActiveRule, loadedTemplateDao, sessionFactory, registerRulesBefore); } public void start() { TimeProfiler profiler = new TimeProfiler(LOGGER).start("Register Quality Profiles"); - session = sessionFactory.getSession(); - - // hibernate session can contain an invalid cache of rules - session.commit(); - ListMultimap profilesByLanguage = loadDefinitions(); - for (String language : profilesByLanguage.keySet()) { - List profiles = profilesByLanguage.get(language); - verifyLanguage(language, profiles); - - for (Map.Entry> entry : groupByName(profiles).entrySet()) { - String name = entry.getKey(); - if (shouldRegister(language, name)) { - register(language, name, entry.getValue()); + SqlSession session = myBatis.openSession(); + try { + ListMultimap profilesByLanguage = profilesByLanguage(); + for (String language : profilesByLanguage.keySet()) { + List profiles = profilesByLanguage.get(language); + verifyLanguage(language, profiles); + + for (Map.Entry> entry : profilesByName(profiles).entrySet()) { + String name = entry.getKey(); + if (shouldRegister(language, name, session)) { + register(language, name, entry.getValue(), session); + } } + setDefault(language, profiles, session); } - - setDefault(language, profiles); + session.commit(); + esActiveRule.bulkRegisterActiveRules(); + } finally { + MyBatis.closeQuietly(session); + profiler.stop(); } - session.commit(); - profiler.stop(); - - esActiveRule.bulkRegisterActiveRules(); } - private void setDefault(String language, List profiles) { - String propertyKey = "sonar.profile." + language; - if (settings.getString(propertyKey) == null) { - String defaultProfileName = defaultProfileName(profiles); - LOGGER.info("Set default " + language + " profile: " + defaultProfileName); - settings.saveProperty(propertyKey, defaultProfileName); + private static void verifyLanguage(String language, List profiles) { + if (profiles.isEmpty()) { + LOGGER.warn("No Quality Profile defined for language: " + language); } - } - private Map> groupByName(List profiles) { - return Multimaps.index(profiles, - new Function() { - public String apply(RulesProfile profile) { - return profile.getName(); - } - }).asMap(); - } - - private boolean shouldRegister(String language, String profileName) { - return loadedTemplateDao.countByTypeAndKey(LoadedTemplateDto.QUALITY_PROFILE_TYPE, templateKey(language, profileName)) == 0; - } - - private static String templateKey(String language, String profileName) { - return StringUtils.lowerCase(language) + ":" + profileName; + Set defaultProfileNames = defaultProfileNames(profiles); + if (defaultProfileNames.size() > 1) { + throw new SonarException("Several Quality Profiles are flagged as default for the language " + language + ": " + defaultProfileNames); + } } - private void register(String language, String name, Collection profiles) { + private void register(String language, String name, Collection profiles, SqlSession session) { LOGGER.info("Register " + language + " profile: " + name); - clean(language, name); - insert(language, name, profiles); - loadedTemplateDao.insert(new LoadedTemplateDto(templateKey(language, name), LoadedTemplateDto.QUALITY_PROFILE_TYPE)); - } + QProfile profile = qProfileLookup.profile(name, language, session); + if (profile != null) { + qProfileOperations.deleteProfile(profile.id(), session); + } + profile = qProfileOperations.newProfile(name, language, true, UserSession.get(), session); - private void verifyLanguage(String language, List profiles) { - if (profiles.isEmpty()) { - LOGGER.warn("No Quality Profile defined for language: " + language); + for (RulesProfile currentRulesProfile : profiles) { + qProfileBackup.restoreFromActiveRules(profile, currentRulesProfile, session); } - Set defaultProfileNames = defaultProfileNames(profiles); - if (defaultProfileNames.size() > 1) { - throw new SonarException("Several Quality Profiles are flagged as default for the language " + language + ": " + - defaultProfileNames); + loadedTemplateDao.insert(new LoadedTemplateDto(templateKey(language, name), LoadedTemplateDto.QUALITY_PROFILE_TYPE), session); + } + + private void setDefault(String language, List profiles, SqlSession session) { + String propertyKey = "sonar.profile." + language; + if (settings.getString(propertyKey) == null) { + String defaultProfileName = defaultProfileName(profiles); + LOGGER.info("Set default " + language + " profile: " + defaultProfileName); + settings.saveProperty(propertyKey, defaultProfileName); } } /** * @return profiles by language */ - private ListMultimap loadDefinitions() { + private ListMultimap profilesByLanguage() { ListMultimap byLang = ArrayListMultimap.create(); for (ProfileDefinition definition : definitions) { ValidationMessages validation = ValidationMessages.create(); @@ -169,6 +168,14 @@ public class RegisterQualityProfiles { return byLang; } + private static Map> profilesByName(List profiles) { + return Multimaps.index(profiles, new Function() { + public String apply(@Nullable RulesProfile profile) { + return profile != null ? profile.getName() : null; + } + }).asMap(); + } + private static String defaultProfileName(List profiles) { String defaultName = null; boolean hasSonarWay = false; @@ -198,47 +205,11 @@ public class RegisterQualityProfiles { return names; } - // - // PERSISTENCE - // - - private void insert(String language, String name, Collection profiles) { - RulesProfile persisted = RulesProfile.create(name, language); - for (RulesProfile profile : profiles) { - for (ActiveRule activeRule : profile.getActiveRules()) { - Rule rule = persistedRule(activeRule); - ActiveRule persistedActiveRule = persisted.activateRule(rule, activeRule.getSeverity()); - for (RuleParam param : rule.getParams()) { - String value = StringUtils.defaultString(activeRule.getParameter(param.getKey()), param.getDefaultValue()); - if (value != null) { - persistedActiveRule.setParameter(param.getKey(), value); - } - } - } - } - session.saveWithoutFlush(persisted); + private boolean shouldRegister(String language, String profileName, SqlSession session) { + return loadedTemplateDao.countByTypeAndKey(LoadedTemplateDto.QUALITY_PROFILE_TYPE, templateKey(language, profileName), session) == 0; } - private Rule persistedRule(ActiveRule activeRule) { - Rule rule = activeRule.getRule(); - if (rule != null && rule.getId() == null) { - if (rule.getKey() != null) { - rule = ruleFinder.findByKey(rule.getRepositoryKey(), rule.getKey()); - - } else if (rule.getConfigKey() != null) { - rule = ruleFinder.find(RuleQuery.create().withRepositoryKey(rule.getRepositoryKey()).withConfigKey(rule.getConfigKey())); - } - } - if (rule == null) { - throw new IllegalStateException(String.format("Rule '%s' has not been found.", activeRule.getRule())); - } - return rule; - } - - private void clean(String language, String name) { - List existingProfiles = session.getResults(RulesProfile.class, "language", language, "name", name); - for (RulesProfile profile : existingProfiles) { - session.removeWithoutFlush(profile); - } + private static String templateKey(String language, String profileName) { + return StringUtils.lowerCase(language) + ":" + profileName; } } diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperationsTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperationsTest.java index e917baa9c55..296436bfe3d 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperationsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperationsTest.java @@ -30,6 +30,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import org.sonar.api.PropertyType; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.rules.RulePriority; import org.sonar.api.server.rule.RuleParamType; @@ -43,6 +44,7 @@ import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleParamDto; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.user.MockUserSession; import org.sonar.server.user.UserSession; import org.sonar.server.util.TypeValidations; @@ -161,6 +163,47 @@ public class QProfileActiveRuleOperationsTest { verify(esActiveRule).bulkIndexActiveRuleIds(eq(newArrayList(idActiveRuleToUpdate)), eq(session)); } + @Test + public void create_active_rule() throws Exception { + RuleKey ruleKey = RuleKey.of("repo", "key"); + when(ruleDao.selectByKey(ruleKey, session)).thenReturn(new RuleDto().setId(10)); + + when(ruleDao.selectParametersByRuleId(eq(10), eq(session))).thenReturn(newArrayList(new RuleParamDto().setId(20).setName("max").setDefaultValue("10"))); + + operations.createActiveRule(1, ruleKey, Severity.CRITICAL, session); + + ArgumentCaptor activeRuleArgument = ArgumentCaptor.forClass(ActiveRuleDto.class); + verify(activeRuleDao).insert(activeRuleArgument.capture(), eq(session)); + assertThat(activeRuleArgument.getValue().getRulId()).isEqualTo(10); + assertThat(activeRuleArgument.getValue().getSeverityString()).isEqualTo(Severity.CRITICAL); + + ArgumentCaptor activeRuleParamArgument = ArgumentCaptor.forClass(ActiveRuleParamDto.class); + verify(activeRuleDao).insert(activeRuleParamArgument.capture(), eq(session)); + assertThat(activeRuleParamArgument.getValue().getKey()).isEqualTo("max"); + assertThat(activeRuleParamArgument.getValue().getValue()).isEqualTo("10"); + + verifyZeroInteractions(session); + verifyZeroInteractions(profilesManager); + verifyZeroInteractions(esActiveRule); + } + + @Test + public void fail_create_active_rule_when_rule_does_not_exists() throws Exception { + RuleKey ruleKey = RuleKey.of("repo", "key"); + when(ruleDao.selectByKey(ruleKey, session)).thenReturn(null); + + try { + operations.createActiveRule(1, ruleKey, Severity.CRITICAL, session); + } catch(Exception e) { + assertThat(e).isInstanceOf(NotFoundException.class); + } + + verifyZeroInteractions(session); + verifyZeroInteractions(activeRuleDao); + verifyZeroInteractions(profilesManager); + verifyZeroInteractions(esActiveRule); + } + @Test public void update_severity() throws Exception { when(profileDao.selectById(1, session)).thenReturn(new QualityProfileDto().setId(1).setName("Default").setLanguage("java")); @@ -365,6 +408,27 @@ public class QProfileActiveRuleOperationsTest { verify(typeValidations).validate(eq(newArrayList("30", "31", "32")), eq("SINGLE_SELECT_LIST"), anyList()); } + @Test + public void update_active_rule_param_from_active_rule() throws Exception { + ActiveRuleDto activeRule = new ActiveRuleDto().setId(5).setProfileId(1).setRuleId(10).setSeverity(Severity.MINOR); + RuleParamDto ruleParam = new RuleParamDto().setRuleId(10).setName("max").setDefaultValue("20").setType(PropertyType.INTEGER.name()); + when(ruleDao.selectParamByRuleAndKey(10, "max", session)).thenReturn(ruleParam); + ActiveRuleParamDto activeRuleParam = new ActiveRuleParamDto().setId(100).setActiveRuleId(5).setKey("max").setValue("20"); + when(activeRuleDao.selectParamByActiveRuleAndKey(5, "max", session)).thenReturn(activeRuleParam); + + operations.updateActiveRuleParam(activeRule, "max", "30", session); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(ActiveRuleParamDto.class); + verify(activeRuleDao).update(argumentCaptor.capture(), eq(session)); + assertThat(argumentCaptor.getValue().getId()).isEqualTo(100); + assertThat(argumentCaptor.getValue().getValue()).isEqualTo("30"); + + verify(typeValidations).validate(eq("30"), eq("INTEGER"), anyList()); + verifyZeroInteractions(session); + verifyZeroInteractions(profilesManager); + verifyZeroInteractions(esActiveRule); + } + @Test public void remove_active_rule_param() throws Exception { ActiveRuleDto activeRule = new ActiveRuleDto().setId(5).setProfileId(1).setRuleId(10).setSeverity(Severity.MINOR); diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackupTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackupTest.java index 790b3042433..725674ba53b 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackupTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackupTest.java @@ -29,21 +29,30 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import org.sonar.api.database.DatabaseSession; +import org.sonar.api.profiles.ProfileDefinition; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.profiles.XMLProfileParser; import org.sonar.api.profiles.XMLProfileSerializer; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.ActiveRule; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RulePriority; import org.sonar.api.utils.ValidationMessages; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.MyBatis; import org.sonar.core.preview.PreviewCache; +import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.jpa.session.DatabaseSessionFactory; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.user.MockUserSession; +import org.sonar.server.user.UserSession; import java.io.Reader; import java.io.Writer; +import java.util.List; +import static com.google.common.collect.Lists.newArrayList; import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Fail.fail; import static org.mockito.Matchers.any; @@ -70,17 +79,25 @@ public class QProfileBackupTest { XMLProfileParser xmlProfileParser; @Mock - XMLProfileSerializer xmlProfileSerializer;; + XMLProfileSerializer xmlProfileSerializer; @Mock QProfileLookup qProfileLookup; + @Mock + QProfileOperations qProfileOperations; + + @Mock + QProfileActiveRuleOperations qProfileActiveRuleOperations; + @Mock ESActiveRule esActiveRule; @Mock PreviewCache dryRunCache; + List definitions; + QProfileBackup backup; @Before @@ -88,7 +105,10 @@ public class QProfileBackupTest { when(myBatis.openSession()).thenReturn(session); when(sessionFactory.getSession()).thenReturn(hibernateSession); - backup = new QProfileBackup(sessionFactory, xmlProfileParser, xmlProfileSerializer, myBatis, qProfileLookup, esActiveRule, dryRunCache); + definitions = newArrayList(); + + backup = new QProfileBackup(sessionFactory, xmlProfileParser, xmlProfileSerializer, myBatis, qProfileLookup, qProfileOperations, qProfileActiveRuleOperations, + esActiveRule, definitions, dryRunCache); MockUserSession.set().setLogin("nicolas").setName("Nicolas").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); } @@ -275,4 +295,77 @@ public class QProfileBackupTest { verifyZeroInteractions(esActiveRule); verifyZeroInteractions(dryRunCache); } + + @Test + public void restore_default_profiles_from_language() throws Exception { + String name = "Default"; + String language = "java"; + + RulesProfile profile = RulesProfile.create(name, language); + Rule rule = Rule.create("pmd", "rule"); + rule.createParameter("max"); + ActiveRule activeRule = profile.activateRule(rule, RulePriority.BLOCKER); + activeRule.setParameter("max", "10"); + + ProfileDefinition profileDefinition = mock(ProfileDefinition.class); + when(profileDefinition.createProfile(any(ValidationMessages.class))).thenReturn(profile); + + definitions.add(profileDefinition); + + when(qProfileOperations.newProfile(eq(name), eq(language), eq(true), any(UserSession.class), eq(session))).thenReturn(new QProfile().setId(1)); + + backup.restoreDefaultProfilesFromLanguage(language); + + verify(qProfileActiveRuleOperations).createActiveRule(eq(1), eq(RuleKey.of("pmd", "rule")), eq("BLOCKER"), eq(session)); + verify(qProfileActiveRuleOperations).updateActiveRuleParam(any(ActiveRuleDto.class), eq("max"), eq("10"), eq(session)); + verifyNoMoreInteractions(qProfileActiveRuleOperations); + + verify(esActiveRule).bulkIndexProfile(eq(1), eq(session)); + verify(dryRunCache).reportGlobalModification(session); + verify(session).commit(); + } + + @Test + public void restore_default_profiles_from_language_with_multiple_profiles_with_same_name_and_same_language() throws Exception { + RulesProfile profile1 = RulesProfile.create("Default", "java"); + profile1.activateRule(Rule.create("pmd", "rule").setSeverity(RulePriority.BLOCKER), null); + ProfileDefinition profileDefinition1 = mock(ProfileDefinition.class); + when(profileDefinition1.createProfile(any(ValidationMessages.class))).thenReturn(profile1); + definitions.add(profileDefinition1); + + RulesProfile profile2 = RulesProfile.create("Default", "java"); + profile2.activateRule(Rule.create("checkstyle", "rule").setSeverity(RulePriority.MAJOR), null); + ProfileDefinition profileDefinition2 = mock(ProfileDefinition.class); + when(profileDefinition2.createProfile(any(ValidationMessages.class))).thenReturn(profile2); + definitions.add(profileDefinition2); + + when(qProfileOperations.newProfile(eq("Default"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(new QProfile().setId(1)); + + backup.restoreDefaultProfilesFromLanguage("java"); + + verify(qProfileActiveRuleOperations).createActiveRule(eq(1), eq(RuleKey.of("pmd", "rule")), eq("BLOCKER"), eq(session)); + verify(qProfileActiveRuleOperations).createActiveRule(eq(1), eq(RuleKey.of("checkstyle", "rule")), eq("MAJOR"), eq(session)); + verifyNoMoreInteractions(qProfileActiveRuleOperations); + + verify(esActiveRule).bulkIndexProfile(eq(1), eq(session)); + verify(dryRunCache).reportGlobalModification(session); + verify(session).commit(); + } + + @Test + public void not_restore_default_profiles_from_another_language() throws Exception { + RulesProfile profile = RulesProfile.create("Default", "java"); + profile.activateRule(Rule.create("pmd", "rule").setSeverity(RulePriority.BLOCKER), null); + ProfileDefinition profileDefinition = mock(ProfileDefinition.class); + when(profileDefinition.createProfile(any(ValidationMessages.class))).thenReturn(profile); + + definitions.add(profileDefinition); + + backup.restoreDefaultProfilesFromLanguage("js"); + + verifyZeroInteractions(qProfileOperations); + verifyZeroInteractions(qProfileActiveRuleOperations); + + verifyZeroInteractions(esActiveRule); + } } diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileLookupTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileLookupTest.java index c0c54d6484d..4a7b871165e 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileLookupTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileLookupTest.java @@ -81,7 +81,7 @@ public class QProfileLookupTest { @Test public void find_by_name_and_language() throws Exception { - when(dao.selectByNameAndLanguage("Sonar Way", "java")).thenReturn(new QualityProfileDto().setId(1).setName("Sonar Way").setLanguage("java")); + when(dao.selectByNameAndLanguage("Sonar Way", "java", session)).thenReturn(new QualityProfileDto().setId(1).setName("Sonar Way").setLanguage("java")); assertThat(search.profile("Sonar Way", "java")).isNotNull(); } @@ -115,12 +115,11 @@ public class QProfileLookupTest { verify(dao).selectByLanguage("java"); } - @Test public void find_parent() throws Exception { - when(dao.selectByNameAndLanguage("Sonar Way", "java")).thenReturn(new QualityProfileDto().setId(1).setName("Sonar Way").setLanguage("java")); + when(dao.selectByNameAndLanguage("Sonar Way", "java", session)).thenReturn(new QualityProfileDto().setId(1).setName("Sonar Way").setLanguage("java")); search.parent(new QProfile().setName("Sonar Way with Findbugs").setLanguage("java").setParent("Sonar Way")); - verify(dao).selectByNameAndLanguage("Sonar Way", "java"); + verify(dao).selectByNameAndLanguage("Sonar Way", "java", session); } @Test diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRepositoryExporterTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRepositoryExporterTest.java index 8c10a98a024..5aec9e51aa6 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRepositoryExporterTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRepositoryExporterTest.java @@ -44,6 +44,7 @@ import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; import org.sonar.jpa.session.DatabaseSessionFactory; import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.NotFoundException; import java.io.Reader; import java.io.Writer; @@ -246,6 +247,26 @@ public class QProfileRepositoryExporterTest { verify(exporter, never()).exportProfile(any(RulesProfile.class), any(Writer.class)); } + @Test + public void fail_to_export_profile_when_profile_is_missing() throws Exception { + RulesProfile profile = mock(RulesProfile.class); + when(profile.getId()).thenReturn(1); + when(hibernateSession.getSingleResult(any(Class.class), eq("id"), eq(1))).thenReturn(null); + + ProfileExporter exporter = mock(ProfileExporter.class); + when(exporter.getKey()).thenReturn("pmd"); + exporters.add(exporter); + + try { + operations.exportToXml(new QProfile().setId(1), "pmd"); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("This profile does not exists."); + } + + verify(exporter, never()).exportProfile(any(RulesProfile.class), any(Writer.class)); + } + @Test public void get_profile_exporter_mime_type() throws Exception { ProfileExporter exporter = mock(ProfileExporter.class); diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesMediumTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesMediumTest.java new file mode 100644 index 00000000000..941c5859068 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesMediumTest.java @@ -0,0 +1,148 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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 org.junit.Test; +import org.sonar.api.profiles.ProfileDefinition; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.ActiveRule; +import org.sonar.api.rules.RuleParam; +import org.sonar.api.rules.RulePriority; +import org.sonar.api.server.rule.RuleParamType; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.server.paging.Paging; +import org.sonar.server.rule.Rule; +import org.sonar.server.rule.RuleQuery; +import org.sonar.server.rule.Rules; +import org.sonar.server.tester.ServerTester; +import org.sonar.server.user.MockUserSession; + +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; +import static org.fest.assertions.Assertions.assertThat; + +public class QProfilesMediumTest { + + @org.junit.Rule + public ServerTester serverTester = new ServerTester().addExtensions(XooRulesDefinition.class, XooProfileDefinition.class); + + @Test + public void register_profile_at_startup() throws Exception { + QProfiles qProfiles = serverTester.get(QProfiles.class); + List profiles = qProfiles.profilesByLanguage("xoo"); + assertThat(profiles).hasSize(1); + + QProfile profile = profiles.get(0); + assertThat(profile.id()).isNotNull(); + assertThat(profile.name()).isEqualTo("Basic"); + assertThat(profile.language()).isEqualTo("xoo"); + assertThat(qProfiles.defaultProfile("xoo").name()).isEqualTo("Basic"); + + assertThat(qProfiles.searchProfileRules(ProfileRuleQuery.create(profile.id()), Paging.create(10, 1)).rules()).hasSize(2); + + QProfileRule qProfileRule = qProfiles.searchProfileRules(ProfileRuleQuery.create(profile.id()).setNameOrKey("x1"), Paging.create(10, 1)).rules().get(0); + assertThat(qProfileRule.key()).isEqualTo("x1"); + assertThat(qProfileRule.severity()).isEqualTo("MAJOR"); + assertThat(qProfileRule.params()).hasSize(1); + + QProfileRuleParam qProfileRuleParam = qProfileRule.params().get(0); + assertThat(qProfileRuleParam.key()).isEqualTo("acceptWhitespace"); + assertThat(qProfileRuleParam.value()).isEqualTo("true"); + } + + @Test + public void restore_default_profile_from_language() throws Exception { + MockUserSession.set().setLogin("julien").setName("Julien").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + + QProfiles qProfiles = serverTester.get(QProfiles.class); + QProfileBackup qProfileBackup = serverTester.get(QProfileBackup.class); + Rules rules = serverTester.get(Rules.class); + + QProfile profile = qProfiles.profile("Basic", "xoo"); + + // Update rule x1 : update severity and update param value + Rule rule1 = rules.find(RuleQuery.builder().searchQuery("x1").build()).results().iterator().next(); + qProfiles.updateActiveRuleParam(qProfiles.findByProfileAndRule(profile.id(), rule1.id()).activeRuleId(), "acceptWhitespace", "false"); + qProfiles.activateRule(profile.id(), rule1.id(), "INFO"); + + // Disable rule x2 + Rule rule2 = rules.find(RuleQuery.builder().searchQuery("x2").build()).results().iterator().next(); + qProfiles.deactivateRule(qProfiles.profile("Basic", "xoo").id(), rule2.id()); + + // Renamed profile + qProfiles.renameProfile(profile.id(), "Old Basic"); + + // Restore default profiles of xoo + qProfileBackup.restoreDefaultProfilesFromLanguage("xoo"); + + // Reload profile + profile = qProfiles.profile("Basic", "xoo"); + + // Verify rule x1 + QProfileRule qProfileRule = qProfiles.searchProfileRules(ProfileRuleQuery.create(profile.id()).setNameOrKey("x1"), Paging.create(10, 1)).rules().get(0); + assertThat(qProfileRule.severity()).isEqualTo("MAJOR"); + QProfileRuleParam qProfileRuleParam = qProfileRule.params().get(0); + assertThat(qProfileRuleParam.key()).isEqualTo("acceptWhitespace"); + assertThat(qProfileRuleParam.value()).isEqualTo("true"); + + // Verify rule x2 + assertThat(qProfiles.searchProfileRules(ProfileRuleQuery.create(profile.id()).setNameOrKey("x2"), Paging.create(10, 1)).rules().get(0)).isNotNull(); + } + + public static class XooProfileDefinition extends ProfileDefinition { + @Override + public RulesProfile createProfile(ValidationMessages validation) { + final RulesProfile profile = RulesProfile.create("Basic", "xoo"); + ActiveRule activeRule1 = profile.activateRule( + org.sonar.api.rules.Rule.create("xoo", "x1").setParams(newArrayList(new RuleParam().setKey("acceptWhitespace"))), + RulePriority.MAJOR); + activeRule1.setParameter("acceptWhitespace", "true"); + + profile.activateRule(org.sonar.api.rules.Rule.create("xoo", "x2"), RulePriority.BLOCKER); + return profile; + } + } + + public static class XooRulesDefinition implements RulesDefinition { + @Override + public void define(Context context) { + NewRepository repository = context.createRepository("xoo", "xoo").setName("Xoo Repo"); + repository.createRule("x1") + .setName("x1 name") + .setHtmlDescription("x1 desc") + .setSeverity(Severity.MINOR) + .createParam("acceptWhitespace") + .setDefaultValue("false") + .setType(RuleParamType.BOOLEAN) + .setDescription("Accept whitespaces on the line"); + + repository.createRule("x2") + .setName("x2 name") + .setHtmlDescription("x2 desc") + .setSeverity(Severity.MAJOR); + repository.done(); + } + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java index 64a30746787..a30b1cd8da9 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java @@ -37,11 +37,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.core.persistence.MyBatis; import org.sonar.core.profiling.Profiling; -import org.sonar.core.rule.RuleDao; -import org.sonar.core.rule.RuleDto; -import org.sonar.core.rule.RuleParamDto; -import org.sonar.core.rule.RuleRuleTagDto; -import org.sonar.core.rule.RuleTagType; +import org.sonar.core.rule.*; import org.sonar.core.technicaldebt.db.CharacteristicDao; import org.sonar.core.technicaldebt.db.CharacteristicDto; import org.sonar.server.es.ESIndex; @@ -490,7 +486,6 @@ public class RuleRegistryTest { assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("MODULARITY")).build()).results()).hasSize(1); assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("REUSABILITY")).build()).results()).hasSize(1); assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("PORTABILITY")).build()).results()).hasSize(1); - // FIXME query has to be updated assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("MODULARITY", "PORTABILITY")).build()).results()).hasSize(2); assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("MODULARITY", "REUSABILITY")).build()).results()).hasSize(1); assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("unknown")).build()).results()).isEmpty(); diff --git a/sonar-server/src/test/java/org/sonar/server/startup/RegisterQualityProfilesTest.java b/sonar-server/src/test/java/org/sonar/server/startup/RegisterQualityProfilesTest.java new file mode 100644 index 00000000000..41665aff4a0 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/startup/RegisterQualityProfilesTest.java @@ -0,0 +1,335 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.startup; + +import org.apache.ibatis.session.SqlSession; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.profiles.ProfileDefinition; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.utils.SonarException; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.qualityprofile.db.QualityProfileDao; +import org.sonar.core.template.LoadedTemplateDao; +import org.sonar.core.template.LoadedTemplateDto; +import org.sonar.server.platform.PersistentSettings; +import org.sonar.server.qualityprofile.*; +import org.sonar.server.user.UserSession; + +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class RegisterQualityProfilesTest { + + @Mock + QualityProfileDao qualityProfileDao; + + @Mock + LoadedTemplateDao loadedTemplateDao; + + @Mock + QProfileBackup qProfileBackup; + + @Mock + QProfileOperations qProfileOperations; + + @Mock + QProfileLookup qProfileLookup; + + @Mock + ESActiveRule esActiveRule; + + @Mock + MyBatis myBatis; + + @Mock + SqlSession session; + + @Mock + PersistentSettings settings; + + List definitions; + + RegisterQualityProfiles registerQualityProfiles; + + @Before + public void setUp() throws Exception { + when(myBatis.openSession()).thenReturn(session); + + definitions = newArrayList(); + registerQualityProfiles = new RegisterQualityProfiles(myBatis, settings, esActiveRule, loadedTemplateDao, qProfileBackup, qProfileOperations, qProfileLookup, null, definitions); + } + + @Test + public void register_profile() throws Exception { + RulesProfile rulesProfile = RulesProfile.create("Default", "java"); + ProfileDefinition profileDefinition = mock(ProfileDefinition.class); + when(profileDefinition.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile); + definitions.add(profileDefinition); + + QProfile profile = new QProfile(); + when(qProfileOperations.newProfile(eq("Default"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile); + + registerQualityProfiles.start(); + + verify(qProfileBackup).restoreFromActiveRules(eq(profile), eq(rulesProfile), eq(session)); + + ArgumentCaptor templateCaptor = ArgumentCaptor.forClass(LoadedTemplateDto.class); + verify(loadedTemplateDao).insert(templateCaptor.capture(), eq(session)); + assertThat(templateCaptor.getValue().getKey()).isEqualTo("java:Default"); + assertThat(templateCaptor.getValue().getType()).isEqualTo("QUALITY_PROFILE"); + + verify(settings).saveProperty("sonar.profile.java", "Default"); + + verify(session).commit(); + + verify(esActiveRule).bulkRegisterActiveRules(); + } + + @Test + public void register_profiles_with_different_languages() throws Exception { + RulesProfile rulesProfile1 = RulesProfile.create("Default", "java"); + ProfileDefinition profileDefinition1 = mock(ProfileDefinition.class); + when(profileDefinition1.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile1); + definitions.add(profileDefinition1); + + RulesProfile rulesProfile2 = RulesProfile.create("Default", "js"); + ProfileDefinition profileDefinition2 = mock(ProfileDefinition.class); + when(profileDefinition2.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile2); + definitions.add(profileDefinition2); + + QProfile profile1 = new QProfile(); + when(qProfileOperations.newProfile(eq("Default"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile1); + + QProfile profile2 = new QProfile(); + when(qProfileOperations.newProfile(eq("Default"), eq("js"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile2); + + registerQualityProfiles.start(); + + verify(qProfileBackup).restoreFromActiveRules(eq(profile1), eq(rulesProfile1), eq(session)); + verify(qProfileBackup).restoreFromActiveRules(eq(profile2), eq(rulesProfile2), eq(session)); + verify(loadedTemplateDao, times(2)).insert(any(LoadedTemplateDto.class), eq(session)); + verify(session).commit(); + + verify(settings).saveProperty("sonar.profile.java", "Default"); + verify(settings).saveProperty("sonar.profile.js", "Default"); + } + + @Test + public void register_two_profiles_with_one_rules_profile_being_default() throws Exception { + RulesProfile rulesProfile1 = RulesProfile.create("Basic", "java"); + ProfileDefinition profileDefinition1 = mock(ProfileDefinition.class); + when(profileDefinition1.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile1); + definitions.add(profileDefinition1); + + // Default profile for java + RulesProfile rulesProfile2 = RulesProfile.create("Default", "java"); + rulesProfile2.setDefaultProfile(true); + ProfileDefinition profileDefinition2 = mock(ProfileDefinition.class); + when(profileDefinition2.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile2); + definitions.add(profileDefinition2); + + QProfile profile1 = new QProfile(); + when(qProfileOperations.newProfile(eq("Basic"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile1); + + QProfile profile2 = new QProfile(); + when(qProfileOperations.newProfile(eq("Default"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile2); + + registerQualityProfiles.start(); + + verify(settings).saveProperty("sonar.profile.java", "Default"); + } + + @Test + public void register_two_profiles_with_no_rules_profile_being_default() throws Exception { + RulesProfile rulesProfile1 = RulesProfile.create("Basic", "java"); + ProfileDefinition profileDefinition1 = mock(ProfileDefinition.class); + when(profileDefinition1.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile1); + definitions.add(profileDefinition1); + + RulesProfile rulesProfile2 = RulesProfile.create("Default", "java"); + ProfileDefinition profileDefinition2 = mock(ProfileDefinition.class); + when(profileDefinition2.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile2); + definitions.add(profileDefinition2); + + QProfile profile1 = new QProfile(); + when(qProfileOperations.newProfile(eq("Basic"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile1); + + QProfile profile2 = new QProfile(); + when(qProfileOperations.newProfile(eq("Default"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile2); + + registerQualityProfiles.start(); + + // No rules profile is defined as default, first one will be the default one + verify(settings).saveProperty("sonar.profile.java", "Basic"); + } + + @Test + public void register_two_profiles_with_one_rules_profile_name_using_sonar_way() throws Exception { + RulesProfile rulesProfile1 = RulesProfile.create("Basic", "java"); + ProfileDefinition profileDefinition1 = mock(ProfileDefinition.class); + when(profileDefinition1.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile1); + definitions.add(profileDefinition1); + + // This profile is using 'Sonar way' name, it will be the default one + RulesProfile rulesProfile2 = RulesProfile.create("Sonar way", "java"); + ProfileDefinition profileDefinition2 = mock(ProfileDefinition.class); + when(profileDefinition2.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile2); + definitions.add(profileDefinition2); + + QProfile profile1 = new QProfile(); + when(qProfileOperations.newProfile(eq("Basic"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile1); + + QProfile profile2 = new QProfile(); + when(qProfileOperations.newProfile(eq("Sonar way"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile2); + + registerQualityProfiles.start(); + + verify(settings).saveProperty("sonar.profile.java", "Sonar way"); + } + + @Test + public void fail_to_register_two_profiles_both_being_default() throws Exception { + RulesProfile rulesProfile1 = RulesProfile.create("Basic", "java"); + rulesProfile1.setDefaultProfile(true); + ProfileDefinition profileDefinition1 = mock(ProfileDefinition.class); + when(profileDefinition1.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile1); + definitions.add(profileDefinition1); + + RulesProfile rulesProfile2 = RulesProfile.create("Default", "java"); + rulesProfile2.setDefaultProfile(true); + ProfileDefinition profileDefinition2 = mock(ProfileDefinition.class); + when(profileDefinition2.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile2); + definitions.add(profileDefinition2); + + try { + registerQualityProfiles.start(); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(SonarException.class); + } + + verifyZeroInteractions(qProfileLookup); + verifyZeroInteractions(qProfileBackup); + verifyZeroInteractions(qProfileOperations); + verifyZeroInteractions(settings); + } + + @Test + public void register_profile_from_multiple_rule_profiles_with_same_name_and_language() throws Exception { + RulesProfile rulesProfile1 = RulesProfile.create("Default", "java"); + ProfileDefinition profileDefinition1 = mock(ProfileDefinition.class); + when(profileDefinition1.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile1); + definitions.add(profileDefinition1); + + RulesProfile rulesProfile2 = RulesProfile.create("Default", "java"); + ProfileDefinition profileDefinition2 = mock(ProfileDefinition.class); + when(profileDefinition2.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile2); + definitions.add(profileDefinition2); + + QProfile profile = new QProfile(); + when(qProfileOperations.newProfile(eq("Default"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile); + + registerQualityProfiles.start(); + + ArgumentCaptor rulesProfileCaptor = ArgumentCaptor.forClass(RulesProfile.class); + verify(qProfileBackup, times(2)).restoreFromActiveRules(eq(profile), rulesProfileCaptor.capture(), eq(session)); + assertThat(rulesProfileCaptor.getAllValues().get(0)).isEqualTo(rulesProfile1); + assertThat(rulesProfileCaptor.getAllValues().get(1)).isEqualTo(rulesProfile2); + + verify(session).commit(); + } + + @Test + public void not_register_already_registered_profile() throws Exception { + ProfileDefinition profileDefinition = mock(ProfileDefinition.class); + when(profileDefinition.createProfile(any(ValidationMessages.class))).thenReturn(RulesProfile.create("Default", "java")); + definitions.add(profileDefinition); + + when(loadedTemplateDao.countByTypeAndKey(anyString(), anyString(), eq(session))).thenReturn(1); + + registerQualityProfiles.start(); + + verify(loadedTemplateDao, never()).insert(any(LoadedTemplateDto.class), eq(session)); + verifyZeroInteractions(qProfileBackup); + verifyZeroInteractions(qProfileOperations); + + verify(settings).saveProperty("sonar.profile.java", "Default"); + } + + @Test + public void not_set_profile_as_default_if_language_has_already_a_default_profile() throws Exception { + ProfileDefinition profileDefinition = mock(ProfileDefinition.class); + when(profileDefinition.createProfile(any(ValidationMessages.class))).thenReturn(RulesProfile.create("Default", "java")); + definitions.add(profileDefinition); + + QProfile profile = new QProfile(); + when(qProfileOperations.newProfile(eq("Default"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile); + + when(settings.getString(eq("sonar.profile.java"))).thenReturn("Existing Default Profile"); + + registerQualityProfiles.start(); + + verify(settings, never()).saveProperty(anyString(), anyString()); + } + + @Test + public void delete_existing_profile_if_template_is_empty() throws Exception { + RulesProfile rulesProfile = RulesProfile.create("Default", "java"); + ProfileDefinition profileDefinition = mock(ProfileDefinition.class); + when(profileDefinition.createProfile(any(ValidationMessages.class))).thenReturn(rulesProfile); + definitions.add(profileDefinition); + + QProfile profile = new QProfile(); + when(qProfileLookup.profile(eq("Default"), eq("java"), eq(session))).thenReturn(new QProfile()); + when(qProfileOperations.newProfile(eq("Default"), eq("java"), eq(true), any(UserSession.class), eq(session))).thenReturn(profile); + + when(loadedTemplateDao.countByTypeAndKey(anyString(), anyString(), eq(session))).thenReturn(0); + + registerQualityProfiles.start(); + + verify(qProfileOperations).deleteProfile(anyInt(), eq(session)); + verify(qProfileBackup).restoreFromActiveRules(eq(profile), eq(rulesProfile), eq(session)); + verify(session).commit(); + } + + @Test + public void not_fail_if_no_profile() throws Exception { + registerQualityProfiles.start(); + + verifyZeroInteractions(qProfileLookup); + verifyZeroInteractions(qProfileBackup); + verifyZeroInteractions(qProfileOperations); + verifyZeroInteractions(settings); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java b/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java index ff439395243..f5a443491dd 100644 --- a/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java +++ b/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java @@ -38,6 +38,8 @@ public class ServerTester extends ExternalResource { private File tempDir; private Platform platform; + private Object[] extensions; + @Override protected void before() { tempDir = createTempDir(); @@ -47,7 +49,7 @@ public class ServerTester extends ExternalResource { properties.setProperty(DatabaseProperties.PROP_URL, "jdbc:h2:" + tempDir.getAbsolutePath() + "/h2"); platform = new Platform(); platform.init(properties); - + platform.addExtensions(extensions); platform.doStart(); } @@ -70,6 +72,14 @@ public class ServerTester extends ExternalResource { FileUtils.deleteQuietly(tempDir); } + public ServerTester addExtensions(Object... extensions) { + this.extensions = extensions; + return this; + } + + /** + * Get a component from the platform + */ public C get(Class component) { if (platform == null) { throw new IllegalStateException("Not started");