]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4764 Restore default profiles for a language (Server side, no ui)
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 24 Apr 2014 15:15:07 +0000 (17:15 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 24 Apr 2014 15:15:07 +0000 (17:15 +0200)
19 files changed:
sonar-core/src/main/java/org/sonar/core/template/LoadedTemplateDao.java
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperations.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackup.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLookup.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRepositoryExporter.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResult.java
sonar-server/src/main/java/org/sonar/server/startup/RegisterQualityProfiles.java
sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperationsTest.java
sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackupTest.java
sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileLookupTest.java
sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRepositoryExporterTest.java
sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesMediumTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java
sonar-server/src/test/java/org/sonar/server/startup/RegisterQualityProfilesTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java

index 4b793e8113205b9cf1732de66aae10f7314ba7ed..0077ed68ff004085fb9b5e988521881af63026f2 100644 (file)
@@ -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);
+  }
+
 }
index 510b3f6f620c8d943e4a90ad43efeabab22c53af..ff19bc981f3415ba84637264911d977b5680c731 100644 (file)
@@ -148,6 +148,10 @@ public class Platform {
     }
   }
 
+  public void addExtensions(Object... extensions){
+    serverComponents.addExtensions(extensions);
+  }
+
   public ComponentContainer getContainer() {
     return currentContainer;
   }
index 333305896a58df4d7f0f0a9d22d85b31a93a9c78..5d3c7fdcdc818a513614dc690f5134271cb3e012 100644 (file)
@@ -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);
index 4d5b8b2a6496fe5e475561eeddf9014d0b3a4b72..f8d8c64556f9a292fc8592f7a6bb6e68704f30dd 100644 (file)
@@ -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<QualityGateConditionDto> listConditions(long qGateId) {
     Collection<QualityGateConditionDto> 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<Message> validationMessages = Lists.newArrayList();
+    List<Message> validationMessages = newArrayList();
     validateMetric(metric, validationMessages);
     validateOperator(metric, operator, validationMessages);
     validateThresholds(warningThreshold, errorThreshold, validationMessages);
index 70e344d77e8499222784a2e60283b1eb80101b40..b5b00e3b6196b7dbb65dd55152bb03db063ad575 100644 (file)
@@ -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<Integer> 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);
index 2bc2c63b67b08413c0ba06bbac1897e6231746e3..c00e297cc86047079f6a229928f64051cb2a9d5e 100644 (file)
 
 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<ProfileDefinition> 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.<ProfileDefinition>emptyList(), dryRunCache);
+  }
 
+  public QProfileBackup(DatabaseSessionFactory sessionFactory, XMLProfileParser xmlProfileParser, XMLProfileSerializer xmlProfileSerializer, MyBatis myBatis,
+                        QProfileLookup qProfileLookup, QProfileOperations qProfileOperations, QProfileActiveRuleOperations qProfileActiveRuleOperations, ESActiveRule esActiveRule,
+                        List<ProfileDefinition> 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<String, RulesProfile> profilesByName = profilesByName(language, result);
+      for (Map.Entry<String, Collection<RulesProfile>> 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<String, RulesProfile> profilesByName(String language, QProfileResult result) {
+    ListMultimap<String, RulesProfile> 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) {
index 48d7179a08a2feeb70dae699c3fd2b4656b4827e..1b38d2a35ec27487e5e6c8cc92d367b600762b50 100644 (file)
@@ -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<QProfile> 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);
   }
 
 }
index 5996a6984eb7c36c0a95b7565d5427afc7f13231..114664637b1757eaeee762a63bde28597408512f 100644 (file)
@@ -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");
index dfbcf2a3c46ff689bc6ffbc1f9affc2f57d23519..a6a5becc61b507103104801198bc27b967859061 100644 (file)
@@ -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) {
index 96c47c98d3e67f33ab73989a89153c89dd571b10..81fbb63faa39a733ff269f07184c90466529c31d 100644 (file)
@@ -40,8 +40,8 @@ public class QProfileResult {
     return warnings;
   }
 
-  public QProfileResult setWarnings(List<String> warnings) {
-    this.warnings = warnings;
+  public QProfileResult addWarnings(List<String> warnings) {
+    this.warnings.addAll(warnings);
     return this;
   }
 
@@ -49,8 +49,8 @@ public class QProfileResult {
     return infos;
   }
 
-  public QProfileResult setInfos(List<String> infos) {
-    this.infos = infos;
+  public QProfileResult addInfos(List<String> infos) {
+    this.infos.addAll(infos);
     return this;
   }
 
index bf58747770f33b142baaf992c33eaaf00b19b6f3..1b76f943c6542c11b046c7ed77cafcd3cd9a68b3 100644 (file)
@@ -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<ProfileDefinition> 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<ProfileDefinition> definitions,
-                                 PersistentSettings settings,
-                                 RuleFinder ruleFinder,
-                                 ESActiveRule esActiveRule,
-                                 LoadedTemplateDao loadedTemplateDao,
-                                 DatabaseSessionFactory sessionFactory,
-                                 RegisterRules registerRulesBefore) {
+  private final List<ProfileDefinition> 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.<ProfileDefinition>emptyList());
+  }
+
+  public RegisterQualityProfiles(MyBatis myBatis,
+                                  PersistentSettings settings,
+                                  ESActiveRule esActiveRule,
+                                  LoadedTemplateDao loadedTemplateDao,
+                                  QProfileBackup qProfileBackup,
+                                  QProfileOperations qProfileOperations,
+                                  QProfileLookup qProfileLookup,
+                                  RegisterRules registerRulesBefore,
+                                  List<ProfileDefinition> 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.<ProfileDefinition>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<String, RulesProfile> profilesByLanguage = loadDefinitions();
-    for (String language : profilesByLanguage.keySet()) {
-      List<RulesProfile> profiles = profilesByLanguage.get(language);
-      verifyLanguage(language, profiles);
-
-      for (Map.Entry<String, Collection<RulesProfile>> entry : groupByName(profiles).entrySet()) {
-        String name = entry.getKey();
-        if (shouldRegister(language, name)) {
-          register(language, name, entry.getValue());
+    SqlSession session = myBatis.openSession();
+    try {
+      ListMultimap<String, RulesProfile> profilesByLanguage = profilesByLanguage();
+      for (String language : profilesByLanguage.keySet()) {
+        List<RulesProfile> profiles = profilesByLanguage.get(language);
+        verifyLanguage(language, profiles);
+
+        for (Map.Entry<String, Collection<RulesProfile>> 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<RulesProfile> 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<RulesProfile> profiles) {
+    if (profiles.isEmpty()) {
+      LOGGER.warn("No Quality Profile defined for language: " + language);
     }
-  }
 
-  private Map<String, Collection<RulesProfile>> groupByName(List<RulesProfile> profiles) {
-    return Multimaps.index(profiles,
-      new Function<RulesProfile, String>() {
-        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<String> 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<RulesProfile> profiles) {
+  private void register(String language, String name, Collection<RulesProfile> 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<RulesProfile> profiles) {
-    if (profiles.isEmpty()) {
-      LOGGER.warn("No Quality Profile defined for language: " + language);
+    for (RulesProfile currentRulesProfile : profiles) {
+      qProfileBackup.restoreFromActiveRules(profile, currentRulesProfile, session);
     }
 
-    Set<String> 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<RulesProfile> 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<String, RulesProfile> loadDefinitions() {
+  private ListMultimap<String, RulesProfile> profilesByLanguage() {
     ListMultimap<String, RulesProfile> byLang = ArrayListMultimap.create();
     for (ProfileDefinition definition : definitions) {
       ValidationMessages validation = ValidationMessages.create();
@@ -169,6 +168,14 @@ public class RegisterQualityProfiles {
     return byLang;
   }
 
+  private static Map<String, Collection<RulesProfile>> profilesByName(List<RulesProfile> profiles) {
+    return Multimaps.index(profiles, new Function<RulesProfile, String>() {
+      public String apply(@Nullable RulesProfile profile) {
+        return profile != null ? profile.getName() : null;
+      }
+    }).asMap();
+  }
+
   private static String defaultProfileName(List<RulesProfile> 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<RulesProfile> 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<RulesProfile> 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;
   }
 }
index e917baa9c55312ea925e4a610e8d34c78cf4c1f6..296436bfe3d4f2e99d9a79c15cf7827faf9b8388 100644 (file)
@@ -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<ActiveRuleDto> 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<ActiveRuleParamDto> 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<ActiveRuleParamDto> 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);
index 790b3042433848994eac7b25faed913dcab81dc0..725674ba53b0fbdc152d379e4398df679b35ae1b 100644 (file)
@@ -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<ProfileDefinition> 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);
+  }
 }
index c0c54d6484df254d22c3ec86816fafb1db7776b0..4a7b871165eee8ea14ff66473ecb3b3ae17b44fd 100644 (file)
@@ -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
index 8c10a98a024951891e9a754cc314fd6617dc224f..5aec9e51aa6d2af9700d08047329c379e6ae6216 100644 (file)
@@ -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 (file)
index 0000000..941c585
--- /dev/null
@@ -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<QProfile> 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();
+    }
+  }
+}
index 64a307467875a61dcfe1a24a80df30cbdfd234e4..a30b1cd8da964e04e308f3778b1d9a747ed43a08 100644 (file)
@@ -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 (file)
index 0000000..41665af
--- /dev/null
@@ -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<ProfileDefinition> 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<LoadedTemplateDto> 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<RulesProfile> 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);
+  }
+}
index ff439395243f274b433c02a8e0263835196b0daa..f5a443491dd1f23958a83aef8f79bdc4eb8f7e8f 100644 (file)
@@ -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> C get(Class<C> component) {
     if (platform == null) {
       throw new IllegalStateException("Not started");