]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5007 restore Q profile backup by keeping parent-child association
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 3 Jun 2014 13:25:11 +0000 (15:25 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 3 Jun 2014 19:45:31 +0000 (21:45 +0200)
26 files changed:
sonar-server/src/main/java/org/sonar/server/qualityprofile/ProfilesManager.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackup.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivationContext.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivationContextFactory.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/BulkRuleActivationActions.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ProfilesWs.java
sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/profiles_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/models/internal.rb
sonar-server/src/test/java/org/sonar/server/qualityprofile/ProfilesManagerTest.java
sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackupTest.java
sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackuperMediumTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java
sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleChangeTest.java
sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
sonar-server/src/test/java/org/sonar/server/rule/RuleBackendMediumTest.java
sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/expected-backup.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/not-xml-backup.txt [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore-child.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore-parent.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore_fails_to_deactivate_inherited_rule.xml [new file with mode: 0644]

index 80c3c507799f48e157651b3966d290f759607d0a..84f1e5916e82e38440acb37e2d191f62bb38f586 100644 (file)
@@ -91,35 +91,6 @@ public class ProfilesManager extends BaseDao {
     return actions;
   }
 
-  /**
-   * Deactivate all active rules from profiles using a rule, then remove then.
-   */
-  public void removeActivatedRules(int ruleId) {
-    List<ActiveRule> activeRules = getSession().createQuery("FROM " + ActiveRule.class.getSimpleName() + " WHERE rule.id=:ruleId").setParameter("ruleId", ruleId).getResultList();
-    List<ActiveRule> activeRulesToRemove = Lists.newArrayList();
-
-    for (ActiveRule activeRule : activeRules) {
-      if (!activeRule.isInherited()) {
-        RulesProfile profile = activeRule.getRulesProfile();
-        incrementProfileVersionIfNeeded(profile);
-        ruleDisabled(profile, activeRule, null);
-        for (RulesProfile child : getChildren(profile.getId())) {
-          deactivate(child, activeRule.getRule(), null);
-        }
-        activeRulesToRemove.add(activeRule);
-      }
-    }
-
-    for (ActiveRule activeRule : activeRulesToRemove) {
-      // Do not use getSingleResult as it can generate an EntityNotFoundException
-      ActiveRule activeRuleToRemove = getSession().getEntity(ActiveRule.class, activeRule.getId());
-      removeActiveRule(activeRuleToRemove);
-    }
-    getSession().commit();
-    dryRunCache.reportGlobalModification();
-  }
-
-
   /**
    * Rule was activated
    */
index 8a19ddda3eaec6cb647b79dd4667004d97f9dacb..6b8325461d673f7bfa9d78bd04ab7ab7a1bee96a 100644 (file)
@@ -23,11 +23,8 @@ package org.sonar.server.qualityprofile;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ListMultimap;
 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;
@@ -38,15 +35,11 @@ import org.sonar.core.preview.PreviewCache;
 import org.sonar.core.qualityprofile.db.ActiveRuleDto;
 import org.sonar.core.qualityprofile.db.QualityProfileKey;
 import org.sonar.core.rule.RuleDto;
-import org.sonar.jpa.session.DatabaseSessionFactory;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.rule.db.RuleDao;
 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;
@@ -56,12 +49,7 @@ import static com.google.common.collect.Lists.newArrayList;
 
 public class QProfileBackup implements ServerComponent {
 
-  private final DatabaseSessionFactory sessionFactory;
-  private final XMLProfileParser xmlProfileParser;
-  private final XMLProfileSerializer xmlProfileSerializer;
-
   private final MyBatis myBatis;
-  private final QProfileLookup qProfileLookup;
   private final QProfileOperations qProfileOperations;
   private final QProfileActiveRuleOperations qProfileActiveRuleOperations;
   private final RuleDao ruleDao;
@@ -69,21 +57,17 @@ public class QProfileBackup implements ServerComponent {
   private final DefaultProfilesCache defaultProfilesCache;
   private final PreviewCache dryRunCache;
 
-  public QProfileBackup(DatabaseSessionFactory sessionFactory, XMLProfileParser xmlProfileParser, XMLProfileSerializer xmlProfileSerializer, MyBatis myBatis,
-                        QProfileLookup qProfileLookup, QProfileOperations qProfileOperations, QProfileActiveRuleOperations qProfileActiveRuleOperations, RuleDao ruleDao,
+  public QProfileBackup(MyBatis myBatis,
+                        QProfileOperations qProfileOperations, QProfileActiveRuleOperations qProfileActiveRuleOperations, RuleDao ruleDao,
                         DefaultProfilesCache defaultProfilesCache, PreviewCache dryRunCache) {
-    this(sessionFactory, xmlProfileParser, xmlProfileSerializer, myBatis, qProfileLookup, qProfileOperations, qProfileActiveRuleOperations, ruleDao,
+    this(myBatis, qProfileOperations, qProfileActiveRuleOperations, ruleDao,
       Collections.<ProfileDefinition>emptyList(), defaultProfilesCache, dryRunCache);
   }
 
-  public QProfileBackup(DatabaseSessionFactory sessionFactory, XMLProfileParser xmlProfileParser, XMLProfileSerializer xmlProfileSerializer, MyBatis myBatis,
-                        QProfileLookup qProfileLookup, QProfileOperations qProfileOperations, QProfileActiveRuleOperations qProfileActiveRuleOperations, RuleDao ruleDao,
+  public QProfileBackup(MyBatis myBatis,
+                        QProfileOperations qProfileOperations, QProfileActiveRuleOperations qProfileActiveRuleOperations, RuleDao ruleDao,
                         List<ProfileDefinition> definitions, DefaultProfilesCache defaultProfilesCache, PreviewCache dryRunCache) {
-    this.sessionFactory = sessionFactory;
-    this.xmlProfileParser = xmlProfileParser;
-    this.xmlProfileSerializer = xmlProfileSerializer;
     this.myBatis = myBatis;
-    this.qProfileLookup = qProfileLookup;
     this.qProfileOperations = qProfileOperations;
     this.qProfileActiveRuleOperations = qProfileActiveRuleOperations;
     this.ruleDao = ruleDao;
@@ -92,47 +76,6 @@ public class QProfileBackup implements ServerComponent {
     this.dryRunCache = dryRunCache;
   }
 
-  public String backupProfile(QProfile profile) {
-    DatabaseSession session = sessionFactory.getSession();
-    RulesProfile rulesProfile = session.getSingleResult(RulesProfile.class, "id", profile.id());
-    Writer writer = new StringWriter();
-    xmlProfileSerializer.write(rulesProfile, writer);
-    return writer.toString();
-  }
-
-  /**
-   * @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());
-
-    DbSession session = myBatis.openSession(false);
-    QProfileResult result = new QProfileResult();
-    try {
-      ValidationMessages messages = ValidationMessages.create();
-      RulesProfile importedProfile = xmlProfileParser.parse(new StringReader(xmlBackup), messages);
-      processValidationMessages(messages, result);
-      if (importedProfile != null) {
-        DatabaseSession hibernateSession = sessionFactory.getSession();
-        checkProfileDoesNotExists(importedProfile, deleteExisting, hibernateSession);
-        hibernateSession.saveWithoutFlush(importedProfile);
-        hibernateSession.commit();
-
-        QProfile newProfile = qProfileLookup.profile(importedProfile.getId(), session);
-        if (newProfile == null) {
-          throw new BadRequestException("Restore of the profile has failed.");
-        }
-        //esActiveRule.bulkIndexProfile(newProfile.id(), session);
-        dryRunCache.reportGlobalModification(session);
-        session.commit();
-        result.setProfile(newProfile);
-      }
-    } finally {
-      MyBatis.closeQuietly(session);
-    }
-    return result;
-  }
-
   /**
    * Recreate built-in profile for a given language.
    * If a profile with same name than default profile already exists, an exception will be thrown.
@@ -201,19 +144,6 @@ public class QProfileBackup implements ServerComponent {
     return defaultProfilesCache.byLanguage(language);
   }
 
-  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) {
-      throw BadRequestException.of("The profile " + existingProfile + " already exists. Please delete it before restoring.");
-    }
-    if (existingProfile != null) {
-      // Warning, profile with children can be deleted as no check is done!
-      hibernateSession.removeWithoutFlush(existingProfile);
-      hibernateSession.commit();
-      //esActiveRule.deleteActiveRulesFromProfile(existingProfile.getId());
-    }
-  }
-
   private void processValidationMessages(ValidationMessages messages, QProfileResult result) {
     if (!messages.getErrors().isEmpty()) {
       List<BadRequestException.Message> errors = newArrayList();
diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java
new file mode 100644 (file)
index 0000000..d613238
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * 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 com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.commons.lang.StringUtils;
+import org.codehaus.staxmate.SMInputFactory;
+import org.codehaus.staxmate.in.SMHierarchicCursor;
+import org.codehaus.staxmate.in.SMInputCursor;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.text.XmlWriter;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.qualityprofile.db.ActiveRuleDto;
+import org.sonar.core.qualityprofile.db.ActiveRuleKey;
+import org.sonar.core.qualityprofile.db.QualityProfileDto;
+import org.sonar.core.qualityprofile.db.QualityProfileKey;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
+import org.sonar.server.search.IndexClient;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class QProfileBackuper implements ServerComponent {
+
+  private final RuleActivator activator;
+  private final DbClient db;
+  private final IndexClient index;
+
+  public QProfileBackuper(RuleActivator activator, DbClient db, IndexClient index) {
+    this.activator = activator;
+    this.db = db;
+    this.index = index;
+  }
+
+  public void backup(QualityProfileKey key, Writer writer) {
+    // TODO check permission
+    DbSession dbSession = db.openSession(false);
+    try {
+      QualityProfileDto profile = db.qualityProfileDao().getByKey(key, dbSession);
+      if (profile == null) {
+        throw new IllegalArgumentException("Quality profile does not exist: " + key);
+      }
+      List<ActiveRule> activeRules = index.get(ActiveRuleIndex.class).findByProfile(key);
+      writeXml(writer, profile, activeRules);
+
+    } finally {
+      dbSession.close();
+    }
+  }
+
+  private void writeXml(Writer writer, QualityProfileDto profile, List<ActiveRule> activeRules) {
+    XmlWriter xml = XmlWriter.of(writer).declaration();
+    xml.begin("profile");
+    xml.prop("name", profile.getName());
+    xml.prop("language", profile.getLanguage());
+    xml.begin("rules");
+    for (ActiveRule activeRule : activeRules) {
+      xml.begin("rule");
+      xml.prop("repositoryKey", activeRule.key().ruleKey().repository());
+      xml.prop("key", activeRule.key().ruleKey().rule());
+      xml.prop("priority", activeRule.severity());
+      xml.begin("parameters");
+      for (Map.Entry<String, String> param : activeRule.params().entrySet()) {
+        xml
+          .begin("parameter")
+          .prop("key", param.getKey())
+          .prop("value", param.getValue())
+          .end();
+      }
+      xml.end("parameters");
+      xml.end("rule");
+    }
+    xml.end("rules");
+    xml.end("profile").close();
+  }
+
+  public void restore(Reader reader) {
+    try {
+      String profileLang = null, profileName = null;
+      SMInputFactory inputFactory = initStax();
+      SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader);
+      rootC.advance(); // <profile>
+      SMInputCursor cursor = rootC.childElementCursor();
+      while (cursor.getNext() != null) {
+        String nodeName = cursor.getLocalName();
+        if (StringUtils.equals("name", nodeName)) {
+          profileName = StringUtils.trim(cursor.collectDescendantText(false));
+
+        } else if (StringUtils.equals("language", nodeName)) {
+          profileLang = StringUtils.trim(cursor.collectDescendantText(false));
+
+        } else if (StringUtils.equals("rules", nodeName)) {
+          QualityProfileKey profileKey = QualityProfileKey.of(profileName, profileLang);
+          SMInputCursor rulesCursor = cursor.childElementCursor("rule");
+          doRestore(rulesCursor, profileKey);
+        }
+      }
+    } catch (XMLStreamException e) {
+      throw new IllegalStateException("Fail to restore Quality profile backup", e);
+    }
+  }
+
+  private void doRestore(SMInputCursor rulesCursor, QualityProfileKey profileKey) throws XMLStreamException {
+    Set<RuleKey> rulesToDeactivate = Sets.newHashSet();
+    DbSession dbSession = db.openSession(false);
+    try {
+      // find or create profile
+      if (db.qualityProfileDao().getByKey(profileKey, dbSession) == null) {
+        // create new profile
+        db.qualityProfileDao().insert(dbSession, QualityProfileDto.createFor(profileKey));
+      } else {
+        // already exists. Keep reference to all the activated rules before backup restore
+        for (ActiveRuleDto activeRuleDto : db.activeRuleDao().findByProfileKey(dbSession, profileKey)) {
+          rulesToDeactivate.add(activeRuleDto.getKey().ruleKey());
+        }
+      }
+
+      while (rulesCursor.getNext() != null) {
+        SMInputCursor ruleCursor = rulesCursor.childElementCursor();
+        String repositoryKey = null, key = null, severity = null;
+        Map<String, String> parameters = Maps.newHashMap();
+        while (ruleCursor.getNext() != null) {
+          String nodeName = ruleCursor.getLocalName();
+          if (StringUtils.equals("repositoryKey", nodeName)) {
+            repositoryKey = StringUtils.trim(ruleCursor.collectDescendantText(false));
+
+          } else if (StringUtils.equals("key", nodeName)) {
+            key = StringUtils.trim(ruleCursor.collectDescendantText(false));
+
+          } else if (StringUtils.equals("priority", nodeName)) {
+            severity = StringUtils.trim(ruleCursor.collectDescendantText(false));
+
+          } else if (StringUtils.equals("parameters", nodeName)) {
+            SMInputCursor propsCursor = ruleCursor.childElementCursor("parameter");
+            readParameters(propsCursor, parameters);
+          }
+        }
+        RuleKey ruleKey = RuleKey.of(repositoryKey, key);
+        RuleActivation activation = new RuleActivation(ActiveRuleKey.of(profileKey, ruleKey));
+        activation.setSeverity(severity);
+        activation.setParameters(parameters);
+        activator.activate(dbSession, activation);
+        rulesToDeactivate.remove(ruleKey);
+      }
+      for (RuleKey ruleKey : rulesToDeactivate) {
+        // Will fail to deactivate a rule which is activated in a parent profile
+        activator.deactivate(ActiveRuleKey.of(profileKey, ruleKey), dbSession);
+      }
+      dbSession.commit();
+
+    } finally {
+      dbSession.close();
+    }
+  }
+
+  private void readParameters(SMInputCursor propsCursor, Map<String, String> parameters) throws XMLStreamException {
+    while (propsCursor.getNext() != null) {
+      SMInputCursor propCursor = propsCursor.childElementCursor();
+      String key = null;
+      String value = null;
+      while (propCursor.getNext() != null) {
+        String nodeName = propCursor.getLocalName();
+        if (StringUtils.equals("key", nodeName)) {
+          key = StringUtils.trim(propCursor.collectDescendantText(false));
+
+        } else if (StringUtils.equals("value", nodeName)) {
+          value = StringUtils.trim(propCursor.collectDescendantText(false));
+        }
+      }
+      if (key != null) {
+        parameters.put(key, value);
+      }
+    }
+  }
+
+  private SMInputFactory initStax() {
+    XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
+    xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
+    xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
+    // just so it won't try to load DTD in if there's DOCTYPE
+    xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
+    xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
+    return new SMInputFactory(xmlFactory);
+  }
+
+  public void resetForLang(String language) {
+    // TODO
+  }
+}
index b1346dd9e2878800ef61f49363898070439a107e..953dc925073e9983f53c4829f8df2ccef7f8aabe 100644 (file)
@@ -22,23 +22,32 @@ package org.sonar.server.qualityprofile;
 import com.google.common.collect.Multimap;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.qualityprofile.db.ActiveRuleKey;
 import org.sonar.core.qualityprofile.db.QualityProfileKey;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
 import org.sonar.server.rule.index.RuleQuery;
 import org.sonar.server.search.IndexClient;
+import org.sonar.server.user.UserSession;
 
 import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
 import java.util.List;
 
 public class QProfileService implements ServerComponent {
 
   private final IndexClient index;
   private final RuleActivator ruleActivator;
+  private final QProfileBackuper backuper;
 
-  public QProfileService(IndexClient index, RuleActivator ruleActivator) {
+  public QProfileService(IndexClient index, RuleActivator ruleActivator, QProfileBackuper backuper) {
     this.index = index;
     this.ruleActivator = ruleActivator;
+    this.backuper = backuper;
   }
 
   @CheckForNull
@@ -59,6 +68,7 @@ public class QProfileService implements ServerComponent {
    * activated.
    */
   public List<ActiveRuleChange> activate(RuleActivation activation) {
+    verifyAdminPermission();
     return ruleActivator.activate(activation);
   }
 
@@ -67,15 +77,89 @@ public class QProfileService implements ServerComponent {
    * fails (fast) if the rule or the profile does not exist.
    */
   public List<ActiveRuleChange> deactivate(ActiveRuleKey key) {
+    verifyAdminPermission();
     return ruleActivator.deactivate(key);
   }
 
 
   public Multimap<String, String> bulkActivate(RuleQuery ruleQuery, QualityProfileKey profile) {
+    verifyAdminPermission();
     return ruleActivator.bulkActivate(ruleQuery, profile);
   }
 
   public Multimap<String, String> bulkDeactivate(RuleQuery ruleQuery, QualityProfileKey profile) {
+    verifyAdminPermission();
     return ruleActivator.bulkDeactivate(ruleQuery, profile);
   }
+
+  public void backup(QualityProfileKey key, Writer writer) {
+    // Allowed to non-admin users (see http://jira.codehaus.org/browse/SONAR-2039)
+    backuper.backup(key, writer);
+  }
+
+  /**
+   * @deprecated used only by Ruby on Rails. Use {@link #backup(org.sonar.core.qualityprofile.db.QualityProfileKey, java.io.Writer)}
+   */
+  @Deprecated
+  public String backup(QualityProfileKey key) {
+    StringWriter output = new StringWriter();
+    backup(key, output);
+    return output.toString();
+  }
+
+  public void restore(Reader backup) {
+    verifyAdminPermission();
+    backuper.restore(backup);
+  }
+
+  /**
+   * @deprecated used only by Ruby on Rails. Use {@link #restore(java.io.Reader)}
+   */
+  @Deprecated
+  public void restore(String backup) {
+    restore(new StringReader(backup));
+  }
+
+  public void resetForLang(String lang) {
+    // TODO
+    verifyAdminPermission();
+  }
+
+  public void copy(QualityProfileKey key, String newName) {
+    // TODO
+    verifyAdminPermission();
+  }
+
+  public void delete(QualityProfileKey key) {
+    // TODO
+    verifyAdminPermission();
+  }
+
+  public void rename(QualityProfileKey key, String newName) {
+    // TODO
+    verifyAdminPermission();
+  }
+
+  //public void create(NewQualityProfile newProfile) {
+  // TODO
+  //verifyAdminPermission();
+  //}
+
+  public void setParent(QualityProfileKey key, @Nullable QualityProfileKey parent) {
+    // TODO
+    verifyAdminPermission();
+  }
+
+  /**
+   * Set the given quality profile as default for the related language
+   */
+  public void setDefault(QualityProfileKey key) {
+    // TODO
+    verifyAdminPermission();
+  }
+
+  private void verifyAdminPermission() {
+    UserSession.get().checkLoggedIn();
+    UserSession.get().checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN);
+  }
 }
index 687575078fc0f31584491dae89f0550b60028361..25ba2e92a45b4e6a5fadd7d4f4403544ff91ed5b 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.qualityprofile;
 
 import com.google.common.collect.Maps;
+import org.apache.commons.lang.StringUtils;
 import org.sonar.core.qualityprofile.db.ActiveRuleDto;
 import org.sonar.core.qualityprofile.db.ActiveRuleParamDto;
 import org.sonar.core.qualityprofile.db.QualityProfileDto;
@@ -29,7 +30,6 @@ import org.sonar.core.rule.RuleParamDto;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Map;
 
 class RuleActivationContext {
@@ -94,11 +94,6 @@ class RuleActivationContext {
     return this;
   }
 
-  @CheckForNull
-  ActiveRuleDto parentActiveRule() {
-    return parentActiveRule;
-  }
-
   RuleActivationContext setParentActiveRule(@Nullable ActiveRuleDto a) {
     this.parentActiveRule = a;
     return this;
@@ -109,9 +104,8 @@ class RuleActivationContext {
     return activeRuleParams;
   }
 
-  @CheckForNull
   Map<String, String> parentActiveRuleParamsAsStringMap() {
-    Map<String, String> params = new HashMap<String, String>();
+    Map<String, String> params = Maps.newHashMap();
     for (Map.Entry<String, ActiveRuleParamDto> param : parentActiveRuleParams.entrySet()) {
       params.put(param.getKey(), param.getValue().getValue());
     }
@@ -159,4 +153,20 @@ class RuleActivationContext {
     }
     return ruleParam.getDefaultValue();
   }
+
+  /**
+   * True if trying to override an inherited rule but with exactly the same values
+   */
+  boolean isSameAsParent(RuleActivation activation) {
+    if (parentActiveRule == null) {
+      return false;
+    }
+    if (activation.isReset()) {
+      return true;
+    }
+    if (StringUtils.equals(activation.getSeverity(), parentActiveRule.getSeverityString())) {
+      return Maps.difference(activation.getParameters(), parentActiveRuleParamsAsStringMap()).areEqual();
+    }
+    return false;
+  }
 }
index c4710af81719a2acb4f55b0bdbdc63de506c1abf..5188850c22a549b34dfefabd5bc12ab2e3e055dc 100644 (file)
@@ -31,6 +31,7 @@ import org.sonar.core.qualityprofile.db.QualityProfileDto;
 import org.sonar.core.qualityprofile.db.QualityProfileKey;
 import org.sonar.core.rule.RuleDto;
 import org.sonar.server.db.DbClient;
+import org.sonar.server.rule.Rule;
 
 import java.util.Collection;
 
@@ -71,7 +72,10 @@ public class RuleActivationContextFactory implements ServerComponent {
       throw new IllegalArgumentException("Rule was removed: " + ruleKey);
     }
     if (Cardinality.MULTIPLE.equals(rule.getCardinality())) {
-      throw new IllegalArgumentException("A rule template can't be activated on a Quality profile: " + ruleKey);
+      throw new IllegalArgumentException("Rule template can't be activated on a Quality profile: " + ruleKey);
+    }
+    if (Rule.MANUAL_REPOSITORY_KEY.equals(rule.getRepositoryKey())) {
+      throw new IllegalArgumentException("Manual rule can't be activated on a Quality profile: " + ruleKey);
     }
     context.setRule(rule);
     context.setRuleParams(db.ruleDao().findRuleParamsByRuleKey(dbSession, rule.getKey()));
index 19701f3d02208b2c7171296faf421d8110e6fcac..0f4b3bbd094d0b7498fb7938cbc9c3070fcab431 100644 (file)
@@ -27,7 +27,6 @@ import com.google.common.collect.Multimap;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.server.rule.RuleParamType;
-import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.preview.PreviewCache;
 import org.sonar.core.qualityprofile.db.ActiveRuleDto;
@@ -40,12 +39,10 @@ import org.sonar.server.db.DbClient;
 import org.sonar.server.qualityprofile.db.ActiveRuleDao;
 import org.sonar.server.rule.Rule;
 import org.sonar.server.rule.index.RuleIndex;
-import org.sonar.server.rule.index.RuleNormalizer;
 import org.sonar.server.rule.index.RuleQuery;
 import org.sonar.server.rule.index.RuleResult;
 import org.sonar.server.search.IndexClient;
 import org.sonar.server.search.QueryOptions;
-import org.sonar.server.user.UserSession;
 import org.sonar.server.util.TypeValidations;
 
 import javax.annotation.Nullable;
@@ -80,8 +77,7 @@ public class RuleActivator implements ServerComponent {
    * Activate a rule on a Quality profile. Update configuration (severity/parameters) if the rule is already
    * activated.
    */
-  public List<ActiveRuleChange> activate(RuleActivation activation) {
-    verifyPermission(UserSession.get());
+  List<ActiveRuleChange> activate(RuleActivation activation) {
     DbSession dbSession = db.openSession(false);
     try {
       List<ActiveRuleChange> changes = activate(dbSession, activation);
@@ -96,7 +92,7 @@ public class RuleActivator implements ServerComponent {
   }
 
   /**
-   * Activate the rule WITHOUT committing db session and WITHOUT checking permissions
+   * Activate the rule WITHOUT committing db session
    */
   List<ActiveRuleChange> activate(DbSession dbSession, RuleActivation activation) {
 
@@ -107,7 +103,7 @@ public class RuleActivator implements ServerComponent {
       change = new ActiveRuleChange(ActiveRuleChange.Type.ACTIVATED, activation.getKey());
 
       //Rules crated by default Inheritance
-      if (activation.isCascade()) {
+      if (activation.isCascade() || context.isSameAsParent(activation)) {
         change.setInheritance(ActiveRule.Inheritance.INHERITED);
       }
     } else {
@@ -119,7 +115,7 @@ public class RuleActivator implements ServerComponent {
 
       //Updates on rule that exists with a valid parent switch them to OVERRIDE
       if (!activation.isCascade() && context.parentProfile() != null) {
-        change.setInheritance(activation.isReset() ? ActiveRule.Inheritance.INHERITED : ActiveRule.Inheritance.OVERRIDES);
+        change.setInheritance(context.isSameAsParent(activation) ? ActiveRule.Inheritance.INHERITED : ActiveRule.Inheritance.OVERRIDES);
       }
     }
 
@@ -220,8 +216,7 @@ public class RuleActivator implements ServerComponent {
    * Deactivate a rule on a Quality profile. Does nothing if the rule is not activated, but
    * fails (fast) if the rule or the profile does not exist.
    */
-  public List<ActiveRuleChange> deactivate(ActiveRuleKey key) {
-    verifyPermission(UserSession.get());
+  List<ActiveRuleChange> deactivate(ActiveRuleKey key) {
     DbSession dbSession = db.openSession(false);
     List<ActiveRuleChange> changes = Lists.newArrayList();
     try {
@@ -236,17 +231,18 @@ public class RuleActivator implements ServerComponent {
   /**
    * Deactivate a rule on a Quality profile WITHOUT committing db session and WITHOUT checking permissions
    */
-  public List<ActiveRuleChange> deactivate(ActiveRuleKey key, DbSession dbSession) {
+  List<ActiveRuleChange> deactivate(ActiveRuleKey key, DbSession dbSession) {
     return cascadeDeactivation(key, dbSession, false);
   }
 
   private List<ActiveRuleChange> cascadeDeactivation(ActiveRuleKey key, DbSession dbSession, boolean isCascade) {
     List<ActiveRuleChange> changes = Lists.newArrayList();
     RuleActivationContext context = contextFactory.create(key, dbSession);
-    ActiveRuleChange change = null;
+    ActiveRuleChange change;
     if (context.activeRule() == null) {
       return changes;
-    } else if (!isCascade && (context.activeRule().isInherited() ||
+    }
+    if (!isCascade && (context.activeRule().isInherited() ||
       context.activeRule().doesOverride())) {
       throw new IllegalStateException("Cannot deactivate inherited rule '" + key.ruleKey() + "'");
     }
@@ -268,11 +264,6 @@ public class RuleActivator implements ServerComponent {
   }
 
 
-  private void verifyPermission(UserSession userSession) {
-    userSession.checkLoggedIn();
-    userSession.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN);
-  }
-
   private void verifyParam(RuleParamDto ruleParam, @Nullable String value) {
     if (value != null) {
       RuleParamType ruleParamType = RuleParamType.parse(ruleParam.getType());
@@ -285,12 +276,7 @@ public class RuleActivator implements ServerComponent {
     }
   }
 
-  public Multimap<String, String> bulkActivate(RuleQuery ruleQuery, QualityProfileKey profile) {
-    return bulkActivate(ruleQuery, profile, null);
-  }
-
-  public Multimap<String, String> bulkActivate(RuleQuery ruleQuery, QualityProfileKey profile, @Nullable String severity) {
-    verifyPermission(UserSession.get());
+  Multimap<String, String> bulkActivate(RuleQuery ruleQuery, QualityProfileKey profile) {
     RuleIndex ruleIndex = index.get(RuleIndex.class);
     Multimap<String, String> results = ArrayListMultimap.create();
     DbSession dbSession = db.openSession(false);
@@ -326,8 +312,7 @@ public class RuleActivator implements ServerComponent {
     return results;
   }
 
-  public Multimap<String, String> bulkDeactivate(RuleQuery ruleQuery, QualityProfileKey profile) {
-    verifyPermission(UserSession.get());
+  Multimap<String, String> bulkDeactivate(RuleQuery ruleQuery, QualityProfileKey profile) {
     RuleIndex ruleIndex = index.get(RuleIndex.class);
     Multimap<String, String> results = ArrayListMultimap.create();
     DbSession dbSession = db.openSession(false);
index 750c094d3c54e5fb3390e323d0fd6f0b468ef4d6..c90473c652fa7715459642628d46e4d11164c8dc 100644 (file)
@@ -29,7 +29,6 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.core.qualityprofile.db.QualityProfileKey;
 import org.sonar.server.qualityprofile.QProfileService;
-import org.sonar.server.qualityprofile.RuleActivator;
 import org.sonar.server.rule.RuleService;
 import org.sonar.server.rule.ws.SearchAction;
 
@@ -114,13 +113,13 @@ public class BulkRuleActivationActions implements ServerComponent {
     writeResponse(results, response);
   }
 
-  private void writeResponse(Multimap<String, String> results, Response response){
+  private void writeResponse(Multimap<String, String> results, Response response) {
     JsonWriter json = response.newJsonWriter().beginObject();
-    for(String action:results.keySet()){
+    for (String action : results.keySet()) {
       json.name(action).beginArray();
-      for(String key:results.get(action)){
+      for (String key : results.get(action)) {
         json.beginObject()
-          .prop("key",key)
+          .prop("key", key)
           .endObject();
       }
       json.endArray();
index 1baaccc40c3c7fa6886a193ffe5edb2cfca96d04..b7eb98ccc5edc369c64da5c6c8ec508aedbb12ce 100644 (file)
@@ -92,7 +92,6 @@ public class ProfilesWs implements WebService {
     action.createParam("name")
       .setDescription("Profile name. If not set, the default profile for the selected language is used")
       .setExampleValue("Sonar way");
-    RailsHandler.addFormatParam(action);
   }
 
   private void defineRestoreAction(NewController controller) {
@@ -105,7 +104,6 @@ public class ProfilesWs implements WebService {
     action.createParam("backup")
       .setRequired(true)
       .setDescription("Path to the file containing the backup (HTML format)");
-    RailsHandler.addJsonOnlyFormatParam(action);
   }
 
   private void defineDestroyAction(NewController controller) {
index 436759e135c71442ab016c90acf53103dae0e4cb..8846b04f7648b0f88c21054b13e7c94562d923c4 100644 (file)
@@ -41,7 +41,8 @@ import org.sonar.core.rule.RuleParamDto;
 import org.sonar.core.technicaldebt.db.CharacteristicDao;
 import org.sonar.core.technicaldebt.db.CharacteristicDto;
 import org.sonar.server.db.DbClient;
-import org.sonar.server.qualityprofile.ProfilesManager;
+import org.sonar.server.qualityprofile.ActiveRule;
+import org.sonar.server.qualityprofile.QProfileService;
 import org.sonar.server.search.IndexDefinition;
 import org.sonar.server.search.action.EmbeddedIndexAction;
 import org.sonar.server.search.action.IndexAction;
@@ -65,22 +66,22 @@ public class RegisterRules implements Startable {
   private static final Logger LOG = LoggerFactory.getLogger(RegisterRules.class);
 
   private final RuleDefinitionsLoader defLoader;
-  private final ProfilesManager profilesManager;
+  private final QProfileService profileService;
   private final DbClient dbClient;
   private final CharacteristicDao characteristicDao;
 
 
-  public RegisterRules(RuleDefinitionsLoader defLoader, ProfilesManager profilesManager,
+  public RegisterRules(RuleDefinitionsLoader defLoader, QProfileService profileService,
                        DbClient dbClient) {
-    this(defLoader, profilesManager, dbClient, System2.INSTANCE);
+    this(defLoader, profileService, dbClient, System2.INSTANCE);
   }
 
 
   @VisibleForTesting
-  RegisterRules(RuleDefinitionsLoader defLoader, ProfilesManager profilesManager,
+  RegisterRules(RuleDefinitionsLoader defLoader, QProfileService profileService,
                 DbClient dbClient, System2 system) {
     this.defLoader = defLoader;
-    this.profilesManager = profilesManager;
+    this.profileService = profileService;
     this.dbClient = dbClient;
     this.characteristicDao = dbClient.debtCharacteristicDao();
   }
@@ -395,7 +396,10 @@ public class RegisterRules implements Startable {
     for (RuleDto rule : removedRules) {
       // SONAR-4642 Remove active rules only when repository still exists
       if (repositoryKeys.contains(rule.getRepositoryKey())) {
-        profilesManager.removeActivatedRules(rule.getId());
+        List<ActiveRule> activeRules = profileService.findActiveRulesByRule(rule.getKey());
+        for (ActiveRule activeRule : activeRules) {
+          profileService.deactivate(activeRule.key());
+        }
       }
     }
   }
index c4781bf2f39d1e5f988ba0e296ab3e202ae5a78f..7a44ddd906aa185389f63efbb36a24293f435388 100644 (file)
@@ -135,8 +135,10 @@ class Api::ProfilesController < Api::ApiController
     else
       profile = Internal.quality_profiles.profile(params[:name], params[:language])
     end
-    not_found('Profile not found') unless profile
-    backup = Internal.profile_backup.backupProfile(profile)
+
+    profile_key=Java::OrgSonarCoreQualityprofileDb::QualityProfileKey.of(profile.name, profile.language)
+    backup = Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).backup(profile_key)
+
     respond_to do |format|
       format.xml { render :xml => backup }
       format.json { render :json => jsonp({:backup => backup}) }
@@ -150,10 +152,12 @@ class Api::ProfilesController < Api::ApiController
     verify_post_request
     require_parameters :backup
 
-    result = Internal.profile_backup.restore(Api::Utils.read_post_request_param(params[:backup]), true)
+    backup = Api::Utils.read_post_request_param(params[:backup])
+    Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).restore(backup)
 
     respond_to do |format|
-      format.json { render :json => jsonp(validation_result_to_json(result)), :status => 200 }
+      #TODO format.json { render :json => jsonp(validation_result_to_json(result)), :status => 200 }
+      format.json { render :json => jsonp({}), :status => 200 }
     end
   end
 
index aba0138c600e36c2676bb6304bd0696c15396225..11dbb691da3cadd6b63f18cd92416951f57261aa 100644 (file)
@@ -144,11 +144,11 @@ class ProfilesController < ApplicationController
     verify_post_request
     require_parameters 'id'
 
-    profile = Internal.quality_profiles.profile(params[:id].to_i)
-    not_found('Profile not found') unless profile
-    xml = Internal.profile_backup.backupProfile(profile)
-    filename = profile.name().gsub(' ', '_')
-    send_data(xml, :type => 'text/xml', :disposition => "attachment; filename=#{filename}_#{profile.language()}.xml")
+    profile_key=profile_id_to_key(params[:id].to_i)
+
+    xml = Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).backup(profile_key)
+    filename = profile_key.toString().gsub(' ', '_')
+    send_data(xml, :type => 'text/xml', :disposition => "attachment; filename=#{filename}.xml")
   end
 
 
@@ -165,8 +165,9 @@ class ProfilesController < ApplicationController
       flash[:warning] = message('quality_profiles.please_upload_backup_file')
     else
       call_backend do
-        result = Internal.profile_backup.restore(Api::Utils.read_post_request_param(params[:backup]), false)
-        flash_result(result)
+        xml=Api::Utils.read_post_request_param(params[:backup])
+        Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).restore(xml)
+        #flash_result(result)
       end
     end
     redirect_to :action => 'index'
@@ -551,4 +552,10 @@ class ProfilesController < ApplicationController
   def set_profile_breadcrumbs
     add_breadcrumbs ProfilesController::root_breadcrumb, Api::Utils.language_name(@profile.language), {:name => @profile.name, :url => {:controller => 'rules_configuration', :action => 'index', :id => @profile.id}}
   end
+
+  def profile_id_to_key(profile_id)
+    profile = Profile.find(profile_id)
+    not_found('Profile not found') unless profile
+    Java::OrgSonarCoreQualityprofileDb::QualityProfileKey.of(profile.name, profile.language)
+  end
 end
index c4091a7798bf881fdda2219b0de34e11b7321687..a0a74561c59df266a8003ab45ffc256d3019c1cc 100644 (file)
@@ -90,8 +90,6 @@ class Internal
     component(Java::OrgSonarServerUi::JRubyI18n.java_class)
   end
 
-  private
-
   def self.component(component_java_class)
     Java::OrgSonarServerPlatform::Platform.component(component_java_class)
   end
index 9d82a650a7885307c63c342b6f64048e581838ac..c201dbc64ebe69424d82916678e66ad1f46e93bf 100644 (file)
 package org.sonar.server.qualityprofile;
 
 import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.profiles.RulesProfile;
-import org.sonar.api.rules.ActiveRule;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RulePriority;
 import org.sonar.core.preview.PreviewCache;
 import org.sonar.jpa.test.AbstractDbUnitTestCase;
 
-import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 
 public class ProfilesManagerTest extends AbstractDbUnitTestCase {
@@ -40,21 +34,4 @@ public class ProfilesManagerTest extends AbstractDbUnitTestCase {
     manager = new ProfilesManager(getSession(), null, mock(PreviewCache.class));
   }
 
-  @Test
-  public void remove_activated_rules() {
-    Rule rule1 = Rule.create("repo", "key");
-
-    RulesProfile profile1 = RulesProfile.create("profile1", "xoo");
-    ActiveRule activeRule1 = new ActiveRule(profile1, rule1, RulePriority.BLOCKER);
-
-    RulesProfile profile2 = RulesProfile.create("profile2", "foo");
-    ActiveRule activeRule2 = new ActiveRule(profile2, rule1, RulePriority.BLOCKER);
-
-    getSession().save(profile1, rule1, activeRule1, profile2, activeRule2);
-
-    manager.removeActivatedRules(rule1.getId());
-
-    assertThat(getHQLCount(ActiveRule.class)).isEqualTo(0);
-  }
-
 }
index 5e9effdcab67d3603ad508b5b9771495b9024a5d..27f77ac8cd7d3fd82502fc428b4683c1989db047 100644 (file)
@@ -24,14 +24,9 @@ import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-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;
@@ -44,16 +39,11 @@ import org.sonar.core.preview.PreviewCache;
 import org.sonar.core.qualityprofile.db.ActiveRuleDto;
 import org.sonar.core.qualityprofile.db.QualityProfileKey;
 import org.sonar.core.rule.RuleDto;
-import org.sonar.jpa.session.DatabaseSessionFactory;
-import org.sonar.server.exceptions.BadRequestException;
-import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.rule.db.RuleDao;
 import org.sonar.server.user.MockUserSession;
 import org.sonar.server.user.UserSession;
 
-import java.io.Reader;
-import java.io.Writer;
 import java.util.Collection;
 import java.util.List;
 
@@ -61,40 +51,18 @@ 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.anyInt;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 @RunWith(MockitoJUnitRunner.class)
 public class QProfileBackupTest {
 
-  @Mock
-  DatabaseSessionFactory sessionFactory;
-
-  @Mock
-  DatabaseSession hibernateSession;
-
   @Mock
   MyBatis myBatis;
 
   @Mock
   DbSession session;
 
-  @Mock
-  XMLProfileParser xmlProfileParser;
-
-  @Mock
-  XMLProfileSerializer xmlProfileSerializer;
-
-  @Mock
-  QProfileLookup qProfileLookup;
-
   @Mock
   QProfileOperations qProfileOperations;
 
@@ -116,191 +84,15 @@ public class QProfileBackupTest {
   @Before
   public void setUp() throws Exception {
     when(myBatis.openSession(false)).thenReturn(session);
-    when(sessionFactory.getSession()).thenReturn(hibernateSession);
 
     definitions = newArrayList();
 
-    backup = new QProfileBackup(sessionFactory, xmlProfileParser, xmlProfileSerializer, myBatis, qProfileLookup, qProfileOperations, qProfileActiveRuleOperations, ruleDao,
+    backup = new QProfileBackup(myBatis, qProfileOperations, qProfileActiveRuleOperations, ruleDao,
       definitions, defaultProfilesCache, dryRunCache);
 
     MockUserSession.set().setLogin("nicolas").setName("Nicolas").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN);
   }
 
-  @Test
-  public void backup() throws Exception {
-    RulesProfile profile = mock(RulesProfile.class);
-    when(profile.getId()).thenReturn(1);
-    when(hibernateSession.getSingleResult(any(Class.class), eq("id"), eq(1))).thenReturn(profile);
-
-    backup.backupProfile(new QProfile().setId(1));
-
-    verify(xmlProfileSerializer).write(eq(profile), any(Writer.class));
-  }
-
-  @Test
-  public void restore() throws Exception {
-    RulesProfile profile = mock(RulesProfile.class);
-    when(profile.getName()).thenReturn("Default");
-    when(profile.getLanguage()).thenReturn("java");
-    when(profile.getId()).thenReturn(1);
-    when(xmlProfileParser.parse(any(Reader.class), any(ValidationMessages.class))).thenReturn(profile);
-    when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(null);
-    when(qProfileLookup.profile(anyInt(), eq(session))).thenReturn(new QProfile().setId(1));
-
-    QProfileResult result = backup.restore("<xml/>", false);
-
-    assertThat(result.profile()).isNotNull();
-    verify(hibernateSession).saveWithoutFlush(profile);
-    verify(dryRunCache).reportGlobalModification(session);
-    verify(session).commit();
-  }
-
-  @Test
-  public void fail_to_restore_without_profile_admin_permission() throws Exception {
-    try {
-      MockUserSession.set().setLogin("nicolas").setName("Nicolas");
-      backup.restore("<xml/>", false);
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(ForbiddenException.class);
-    }
-    verify(hibernateSession, never()).saveWithoutFlush(any(RulesProfile.class));
-    verifyZeroInteractions(dryRunCache);
-  }
-
-  @Test
-  public void fail_to_restore_if_profile_already_exist() throws Exception {
-    RulesProfile profile = mock(RulesProfile.class);
-    when(profile.getName()).thenReturn("Default");
-    when(profile.getLanguage()).thenReturn("java");
-    when(xmlProfileParser.parse(any(Reader.class), any(ValidationMessages.class))).thenReturn(profile);
-    when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(RulesProfile.create("Default", "java"));
-    when(qProfileLookup.profile(anyInt())).thenReturn(new QProfile().setId(1));
-
-    try {
-      backup.restore("<xml/>", false);
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("The profile [name=Default,language=java] already exists. Please delete it before restoring.");
-    }
-
-    verify(hibernateSession, never()).saveWithoutFlush(any(RulesProfile.class));
-    verifyZeroInteractions(dryRunCache);
-  }
-
-  @Test
-  public void restore_should_delete_existing_profile() throws Exception {
-    RulesProfile profile = mock(RulesProfile.class);
-    when(profile.getName()).thenReturn("Default");
-    when(profile.getLanguage()).thenReturn("java");
-    when(profile.getId()).thenReturn(1);
-    when(xmlProfileParser.parse(any(Reader.class), any(ValidationMessages.class))).thenReturn(profile);
-
-    RulesProfile existingProfile = mock(RulesProfile.class);
-    when(existingProfile.getId()).thenReturn(1);
-    when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(existingProfile);
-    when(qProfileLookup.profile(anyInt(), eq(session))).thenReturn(new QProfile().setId(1));
-
-    QProfileResult result = backup.restore("<xml/>", true);
-
-    assertThat(result.profile()).isNotNull();
-    verify(hibernateSession).removeWithoutFlush(eq(existingProfile));
-    verify(dryRunCache).reportGlobalModification(session);
-    verify(session).commit();
-  }
-
-  @Test
-  public void restore_should_add_warnings_and_infos_from_xml_parsing() throws Exception {
-    final RulesProfile profile = mock(RulesProfile.class);
-    when(profile.getName()).thenReturn("Default");
-    when(profile.getLanguage()).thenReturn("java");
-    when(profile.getId()).thenReturn(1);
-    doAnswer(new Answer() {
-      public Object answer(InvocationOnMock invocation) {
-        Object[] args = invocation.getArguments();
-        ValidationMessages validationMessages = (ValidationMessages) args[1];
-        validationMessages.addInfoText("an info message");
-        validationMessages.addWarningText("a warning message");
-        return profile;
-      }
-    }).when(xmlProfileParser).parse(any(Reader.class), any(ValidationMessages.class));
-
-    when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(null);
-    when(qProfileLookup.profile(anyInt(), eq(session))).thenReturn(new QProfile().setId(1));
-
-    QProfileResult result = backup.restore("<xml/>", true);
-
-    assertThat(result.profile()).isNotNull();
-    assertThat(result.warnings()).isNotEmpty();
-    assertThat(result.infos()).isNotEmpty();
-  }
-
-  @Test
-  public void restore_should_fail_if_errors_when_parsing_xml() throws Exception {
-    final RulesProfile profile = mock(RulesProfile.class);
-    when(profile.getName()).thenReturn("Default");
-    when(profile.getLanguage()).thenReturn("java");
-    when(profile.getId()).thenReturn(1);
-    doAnswer(new Answer() {
-      public Object answer(InvocationOnMock invocation) {
-        Object[] args = invocation.getArguments();
-        ValidationMessages validationMessages = (ValidationMessages) args[1];
-        validationMessages.addErrorText("error!");
-        return profile;
-      }
-    }).when(xmlProfileParser).parse(any(Reader.class), any(ValidationMessages.class));
-
-    when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(null);
-    when(qProfileLookup.profile(anyInt(), eq(session))).thenReturn(new QProfile().setId(1));
-
-    try {
-      backup.restore("<xml/>", false);
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("Fail to restore profile");
-      BadRequestException badRequestException = (BadRequestException) e;
-      assertThat(badRequestException.errors()).hasSize(1);
-      assertThat(badRequestException.errors().get(0).text()).isEqualTo("error!");
-    }
-
-    verify(hibernateSession, never()).saveWithoutFlush(any(RulesProfile.class));
-    verifyZeroInteractions(dryRunCache);
-  }
-
-  @Test
-  public void do_not_restore_if_xml_is_empty() throws Exception {
-    when(xmlProfileParser.parse(any(Reader.class), any(ValidationMessages.class))).thenReturn(null);
-    when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(null);
-    when(qProfileLookup.profile(anyInt(), eq(session))).thenReturn(new QProfile().setId(1));
-
-    QProfileResult result = backup.restore("<xml/>", false);
-
-    assertThat(result.profile()).isNull();
-    verify(hibernateSession, never()).saveWithoutFlush(any(RulesProfile.class));
-    verifyZeroInteractions(dryRunCache);
-  }
-
-  @Test
-  public void do_not_restore_if_new_profile_is_null_after_import() throws Exception {
-    RulesProfile profile = mock(RulesProfile.class);
-    when(profile.getName()).thenReturn("Default");
-    when(profile.getLanguage()).thenReturn("java");
-    when(profile.getId()).thenReturn(1);
-    when(xmlProfileParser.parse(any(Reader.class), any(ValidationMessages.class))).thenReturn(profile);
-
-    when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(null);
-    when(qProfileLookup.profile(anyInt(), eq(session))).thenReturn(null);
-
-    try {
-      backup.restore("<xml/>", false);
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("Restore of the profile has failed.");
-    }
-
-    verifyZeroInteractions(dryRunCache);
-  }
-
   @Test
   public void recreate_built_in_profiles_from_language() throws Exception {
     String name = "Default";
diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackuperMediumTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackuperMediumTest.java
new file mode 100644 (file)
index 0000000..bae892c
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * 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 com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.server.rule.RuleParamType;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.qualityprofile.db.ActiveRuleKey;
+import org.sonar.core.qualityprofile.db.QualityProfileDto;
+import org.sonar.core.qualityprofile.db.QualityProfileKey;
+import org.sonar.core.rule.RuleDto;
+import org.sonar.core.rule.RuleParamDto;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.rule.RuleTesting;
+import org.sonar.server.tester.ServerTester;
+
+import javax.xml.stream.XMLStreamException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+
+public class QProfileBackuperMediumTest {
+
+  static final QualityProfileKey XOO_PROFILE_KEY = QualityProfileKey.of("P1", "xoo");
+  static final QualityProfileKey XOO_CHILD_PROFILE_KEY = QualityProfileKey.of("P2", "xoo");
+
+  @ClassRule
+  public static ServerTester tester = new ServerTester();
+
+  DbClient db;
+  DbSession dbSession;
+
+  @Before
+  public void before() {
+    tester.clearDbAndIndexes();
+    db = tester.get(DbClient.class);
+    dbSession = db.openSession(false);
+
+    // create pre-defined rules
+    RuleDto xooRule1 = RuleTesting.newDto(RuleKey.of("xoo", "x1"))
+      .setSeverity("MINOR").setLanguage("xoo");
+    RuleDto xooRule2 = RuleTesting.newDto(RuleKey.of("xoo", "x2"))
+      .setSeverity("MAJOR").setLanguage("xoo");
+    db.ruleDao().insert(dbSession, xooRule1, xooRule2);
+    db.ruleDao().addRuleParam(dbSession, xooRule1, RuleParamDto.createFor(xooRule1)
+      .setName("max").setDefaultValue("10").setType(RuleParamType.INTEGER.type()));
+    dbSession.commit();
+    dbSession.clearCache();
+  }
+
+  @After
+  public void after() throws Exception {
+    dbSession.close();
+  }
+
+  @Test
+  public void backup() throws Exception {
+    // create profile with rule x1 activated
+    db.qualityProfileDao().insert(dbSession, QualityProfileDto.createFor(XOO_PROFILE_KEY));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation.setSeverity(Severity.BLOCKER);
+    activation.setParameter("max", "7");
+    tester.get(RuleActivator.class).activate(dbSession, activation);
+    dbSession.commit();
+    dbSession.clearCache();
+
+    StringWriter output = new StringWriter();
+    tester.get(QProfileBackuper.class).backup(XOO_PROFILE_KEY, output);
+
+    XMLUnit.setIgnoreWhitespace(true);
+    Diff diff = XMLUnit.compareXML(output.toString(),
+      Resources.toString(getClass().getResource("QProfileBackuperMediumTest/expected-backup.xml"), Charsets.UTF_8));
+
+    assertThat(diff.identical()).as(diff.toString()).isTrue();
+  }
+
+  @Test
+  public void fail_to_backup_unknown_profile() throws Exception {
+    try {
+      tester.get(QProfileBackuper.class).backup(QualityProfileKey.of("unknown", "xoo"), new StringWriter());
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Quality profile does not exist: unknown:xoo");
+    }
+  }
+
+  @Test
+  public void restore_and_create_profile() throws Exception {
+    tester.get(QProfileBackuper.class).restore(new StringReader(
+      Resources.toString(getClass().getResource("QProfileBackuperMediumTest/restore.xml"), Charsets.UTF_8)));
+
+    List<ActiveRule> activeRules = tester.get(QProfileService.class).findActiveRulesByProfile(XOO_PROFILE_KEY);
+    assertThat(activeRules).hasSize(1);
+    assertThat(activeRules.get(0).severity()).isEqualTo("BLOCKER");
+    assertThat(activeRules.get(0).inheritance()).isEqualTo(ActiveRule.Inheritance.NONE);
+    assertThat(activeRules.get(0).params().get("max")).isEqualTo("7");
+  }
+
+  @Test
+  public void restore_and_update_profile() throws Exception {
+    // create profile with rules x1 and x2 activated
+    db.qualityProfileDao().insert(dbSession, QualityProfileDto.createFor(XOO_PROFILE_KEY));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation.setSeverity(Severity.INFO);
+    activation.setParameter("max", "10");
+    tester.get(RuleActivator.class).activate(dbSession, activation);
+
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x2")));
+    activation.setSeverity(Severity.INFO);
+    tester.get(RuleActivator.class).activate(dbSession, activation);
+    dbSession.commit();
+    dbSession.clearCache();
+
+    // restore backup, which activates only x1
+    // -> update x1 and deactivate x2
+    tester.get(QProfileBackuper.class).restore(new StringReader(
+      Resources.toString(getClass().getResource("QProfileBackuperMediumTest/restore.xml"), Charsets.UTF_8)));
+
+
+    List<ActiveRule> activeRules = tester.get(QProfileService.class).findActiveRulesByProfile(XOO_PROFILE_KEY);
+    assertThat(activeRules).hasSize(1);
+    assertThat(activeRules.get(0).severity()).isEqualTo("BLOCKER");
+    assertThat(activeRules.get(0).inheritance()).isEqualTo(ActiveRule.Inheritance.NONE);
+    assertThat(activeRules.get(0).params().get("max")).isEqualTo("7");
+  }
+
+  @Test
+  public void restore_child_profile() throws Exception {
+    // define two parent/child profiles
+    db.qualityProfileDao().insert(dbSession,
+      QualityProfileDto.createFor(XOO_PROFILE_KEY),
+      QualityProfileDto.createFor(XOO_CHILD_PROFILE_KEY).setParent(XOO_PROFILE_KEY.name()));
+    dbSession.commit();
+
+    // rule x1 is activated on parent profile (so inherited by child profile)
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation.setSeverity(Severity.INFO);
+    activation.setParameter("max", "10");
+    tester.get(RuleActivator.class).activate(dbSession, activation);
+    dbSession.commit();
+    dbSession.clearCache();
+
+    // restore backup of child profile -> overrides x1
+    tester.get(QProfileBackuper.class).restore(new StringReader(
+      Resources.toString(getClass().getResource("QProfileBackuperMediumTest/restore-child.xml"), Charsets.UTF_8)));
+
+    // parent profile is unchanged
+    List<ActiveRule> activeRules = tester.get(QProfileService.class).findActiveRulesByProfile(XOO_PROFILE_KEY);
+    assertThat(activeRules).hasSize(1);
+    assertThat(activeRules.get(0).severity()).isEqualTo("INFO");
+    assertThat(activeRules.get(0).inheritance()).isEqualTo(ActiveRule.Inheritance.NONE);
+    assertThat(activeRules.get(0).params().get("max")).isEqualTo("10");
+
+    // child profile overrides parent
+    activeRules = tester.get(QProfileService.class).findActiveRulesByProfile(XOO_CHILD_PROFILE_KEY);
+    assertThat(activeRules).hasSize(1);
+    assertThat(activeRules.get(0).severity()).isEqualTo("BLOCKER");
+    assertThat(activeRules.get(0).inheritance()).isEqualTo(ActiveRule.Inheritance.OVERRIDES);
+    assertThat(activeRules.get(0).params().get("max")).isEqualTo("7");
+  }
+
+  @Test
+  public void restore_parent_profile() throws Exception {
+    // define two parent/child profiles
+    db.qualityProfileDao().insert(dbSession,
+      QualityProfileDto.createFor(XOO_PROFILE_KEY),
+      QualityProfileDto.createFor(XOO_CHILD_PROFILE_KEY).setParent(XOO_PROFILE_KEY.name()));
+    dbSession.commit();
+
+    // rule x1 is activated on parent profile (so inherited by child profile)
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation.setSeverity(Severity.INFO);
+    activation.setParameter("max", "10");
+    tester.get(RuleActivator.class).activate(dbSession, activation);
+    dbSession.commit();
+    dbSession.clearCache();
+
+    // restore backup of parent profile -> update x1 and propagates to child
+    tester.get(QProfileBackuper.class).restore(new StringReader(
+      Resources.toString(getClass().getResource("QProfileBackuperMediumTest/restore-parent.xml"), Charsets.UTF_8)));
+
+    // parent profile is updated
+    List<ActiveRule> activeRules = tester.get(QProfileService.class).findActiveRulesByProfile(XOO_PROFILE_KEY);
+    assertThat(activeRules).hasSize(1);
+    assertThat(activeRules.get(0).severity()).isEqualTo("BLOCKER");
+    assertThat(activeRules.get(0).inheritance()).isEqualTo(ActiveRule.Inheritance.NONE);
+    assertThat(activeRules.get(0).params().get("max")).isEqualTo("7");
+
+    // child profile is inherited
+    activeRules = tester.get(QProfileService.class).findActiveRulesByProfile(XOO_CHILD_PROFILE_KEY);
+    assertThat(activeRules).hasSize(1);
+    assertThat(activeRules.get(0).severity()).isEqualTo("BLOCKER");
+    assertThat(activeRules.get(0).inheritance()).isEqualTo(ActiveRule.Inheritance.INHERITED);
+    assertThat(activeRules.get(0).params().get("max")).isEqualTo("7");
+  }
+
+  @Test
+  public void restore_fails_to_deactivate_inherited_rule() throws Exception {
+    // define two parent/child profiles
+    db.qualityProfileDao().insert(dbSession,
+      QualityProfileDto.createFor(XOO_PROFILE_KEY),
+      QualityProfileDto.createFor(XOO_CHILD_PROFILE_KEY).setParent(XOO_PROFILE_KEY.name()));
+    dbSession.commit();
+
+    // rule x1 is activated on parent profile (so inherited by child profile)
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation.setSeverity(Severity.INFO);
+    activation.setParameter("max", "10");
+    tester.get(RuleActivator.class).activate(dbSession, activation);
+    dbSession.commit();
+    dbSession.clearCache();
+
+    // backup of child profile does not contain x1
+    try {
+      tester.get(QProfileBackuper.class).restore(new StringReader(
+        Resources.toString(getClass().getResource("QProfileBackuperMediumTest/restore_fails_to_deactivate_inherited_rule.xml"), Charsets.UTF_8)));
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("Cannot deactivate inherited rule 'xoo:x1'");
+    }
+  }
+
+  @Test
+  public void fail_to_restore_if_not_xml_backup() throws Exception {
+    try {
+      tester.get(QProfileBackuper.class).restore(new StringReader(
+        Resources.toString(getClass().getResource("QProfileBackuperMediumTest/not-xml-backup.txt"), Charsets.UTF_8)));
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("Fail to restore Quality profile backup");
+      assertThat(e.getCause()).isInstanceOf(XMLStreamException.class);
+    }
+  }
+}
index 23956e864777148bba30ba28cfaaa73964c27bd1..d50466f20a3459fc6882b39c3dd3e7d9d5d93d79 100644 (file)
@@ -25,10 +25,10 @@ import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.server.rule.RuleParamType;
 import org.sonar.check.Cardinality;
-import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.qualityprofile.db.ActiveRuleDto;
 import org.sonar.core.qualityprofile.db.ActiveRuleKey;
@@ -39,11 +39,10 @@ import org.sonar.core.rule.RuleDto;
 import org.sonar.core.rule.RuleParamDto;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.exceptions.BadRequestException;
-import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
+import org.sonar.server.rule.Rule;
 import org.sonar.server.rule.RuleTesting;
 import org.sonar.server.tester.ServerTester;
-import org.sonar.server.user.MockUserSession;
 
 import javax.annotation.Nullable;
 import java.util.List;
@@ -57,6 +56,8 @@ public class RuleActivatorMediumTest {
   static final QualityProfileKey XOO_PROFILE_KEY = QualityProfileKey.of("P1", "xoo");
   static final QualityProfileKey XOO_CHILD_PROFILE_KEY = QualityProfileKey.of("P2", "xoo");
   static final QualityProfileKey XOO_GRAND_CHILD_PROFILE_KEY = QualityProfileKey.of("P3", "xoo");
+  static final RuleKey MANUAL_RULE_KEY = RuleKey.of(Rule.MANUAL_REPOSITORY_KEY, "m1");
+  static final RuleKey XOO_RULE_1 = RuleKey.of("xoo", "x1");
 
   @ClassRule
   public static ServerTester tester = new ServerTester();
@@ -77,11 +78,12 @@ public class RuleActivatorMediumTest {
     // create pre-defined rules
     RuleDto javaRule = RuleTesting.newDto(RuleKey.of("squid", "j1"))
       .setSeverity("MAJOR").setLanguage("java");
-    RuleDto xooRule1 = RuleTesting.newDto(RuleKey.of("xoo", "x1"))
+    RuleDto xooRule1 = RuleTesting.newDto(XOO_RULE_1)
       .setSeverity("MINOR").setLanguage("xoo");
     RuleDto xooTemplateRule1 = RuleTesting.newDto(RuleKey.of("xoo", "template1"))
       .setSeverity("MINOR").setLanguage("xoo").setCardinality(Cardinality.MULTIPLE);
-    db.ruleDao().insert(dbSession, javaRule, xooRule1, xooTemplateRule1);
+    RuleDto manualRule = RuleTesting.newDto(MANUAL_RULE_KEY);
+    db.ruleDao().insert(dbSession, javaRule, xooRule1, xooTemplateRule1, manualRule);
     db.ruleDao().addRuleParam(dbSession, xooRule1, RuleParamDto.createFor(xooRule1)
       .setName("max").setDefaultValue("10").setType(RuleParamType.INTEGER.type()));
 
@@ -99,9 +101,7 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void activate() throws Exception {
-    grantPermission();
-
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "7");
     ruleActivator.activate(activation);
@@ -111,8 +111,16 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void activate_with_default_severity_and_parameter() throws Exception {
-    grantPermission();
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
+    ruleActivator.activate(activation);
+
+    verifyOneActiveRule(XOO_PROFILE_KEY, Severity.MINOR, null, ImmutableMap.of("max", "10"));
+  }
+
+  @Test
+  public void activation_ignores_unsupported_parameters() throws Exception {
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
+    activation.setParameter("xxx", "yyy");
     ruleActivator.activate(activation);
 
     verifyOneActiveRule(XOO_PROFILE_KEY, Severity.MINOR, null, ImmutableMap.of("max", "10"));
@@ -121,13 +129,12 @@ public class RuleActivatorMediumTest {
   @Test
   public void update_activation_severity_and_parameters() throws Exception {
     // initial activation
-    grantPermission();
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     ruleActivator.activate(activation);
 
     // update
-    RuleActivation update = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation update = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     update.setSeverity(Severity.CRITICAL);
     update.setParameter("max", "42");
     ruleActivator.activate(update);
@@ -138,8 +145,7 @@ public class RuleActivatorMediumTest {
   @Test
   public void update_activation_but_new_parameter() throws Exception {
     // initial activation
-    grantPermission();
-    ActiveRuleKey activeRuleKey = ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1"));
+    ActiveRuleKey activeRuleKey = ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1);
     RuleActivation activation = new RuleActivation(activeRuleKey);
     activation.setSeverity(Severity.BLOCKER);
     ruleActivator.activate(activation);
@@ -152,7 +158,7 @@ public class RuleActivatorMediumTest {
     dbSession.clearCache();
 
     // update
-    RuleActivation update = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation update = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     update.setSeverity(Severity.CRITICAL);
     update.setParameter("max", "42");
     // contrary to activerule, the param 'max' is supposed to be inserted but not updated
@@ -164,42 +170,27 @@ public class RuleActivatorMediumTest {
   @Test
   public void revert_activation_to_default_severity_and_parameters() throws Exception {
     // initial activation
-    grantPermission();
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "7");
     ruleActivator.activate(activation);
 
     // update
-    RuleActivation update = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation update = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     ruleActivator.activate(update);
 
     verifyOneActiveRule(XOO_PROFILE_KEY, Severity.MINOR, null, ImmutableMap.of("max", "10"));
   }
 
-  @Test
-  public void fail_to_activate_if_not_granted() throws Exception {
-    MockUserSession.set().setLogin("marius");
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
-
-    try {
-      ruleActivator.activate(activation);
-      fail();
-    } catch (ForbiddenException e) {
-      verifyZeroActiveRules(XOO_PROFILE_KEY);
-    }
-  }
-
   @Test
   public void fail_to_activate_if_template() throws Exception {
-    grantPermission();
     RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "template1")));
 
     try {
       ruleActivator.activate(activation);
       fail();
     } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("A rule template can't be activated on a Quality profile: xoo:template1");
+      assertThat(e).hasMessage("Rule template can't be activated on a Quality profile: xoo:template1");
       verifyZeroActiveRules(XOO_PROFILE_KEY);
     }
   }
@@ -207,7 +198,6 @@ public class RuleActivatorMediumTest {
   @Test
   public void fail_to_activate_if_different_languages() throws Exception {
     // profile and rule have different languages
-    grantPermission();
     RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("squid", "j1")));
 
     try {
@@ -222,7 +212,6 @@ public class RuleActivatorMediumTest {
   @Test
   public void fail_to_activate_if_unknown_rule() throws Exception {
     // profile and rule have different languages
-    grantPermission();
     RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x3")));
 
     try {
@@ -234,11 +223,42 @@ public class RuleActivatorMediumTest {
     }
   }
 
+  @Test
+  public void fail_to_activate_if_rule_with_removed_status() throws Exception {
+    RuleDto ruleDto = db.ruleDao().getByKey(dbSession, XOO_RULE_1);
+    ruleDto.setStatus(RuleStatus.REMOVED);
+    db.ruleDao().update(dbSession, ruleDto);
+    dbSession.commit();
+    dbSession.clearCache();
+
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
+
+    try {
+      ruleActivator.activate(activation);
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Rule was removed: xoo:x1");
+      verifyZeroActiveRules(XOO_PROFILE_KEY);
+    }
+  }
+
+  @Test
+  public void fail_to_activate_if_manual_rule() throws Exception {
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, MANUAL_RULE_KEY));
+
+    try {
+      ruleActivator.activate(activation);
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Manual rule can't be activated on a Quality profile: manual:m1");
+      verifyZeroActiveRules(XOO_PROFILE_KEY);
+    }
+  }
+
   @Test
   public void fail_to_activate_if_unknown_profile() throws Exception {
     // profile and rule have different languages
-    grantPermission();
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(QualityProfileKey.of("other", "js"), RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(QualityProfileKey.of("other", "js"), XOO_RULE_1));
 
     try {
       ruleActivator.activate(activation);
@@ -250,8 +270,7 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void fail_to_activate_if_invalid_parameter() throws Exception {
-    grantPermission();
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setParameter("max", "foo");
 
     try {
@@ -266,8 +285,7 @@ public class RuleActivatorMediumTest {
   @Test
   public void deactivate() throws Exception {
     // activation
-    grantPermission();
-    ActiveRuleKey key = ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1"));
+    ActiveRuleKey key = ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1);
     RuleActivation activation = new RuleActivation(key);
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "7");
@@ -281,10 +299,8 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void ignore_deactivation_if_rule_not_activated() throws Exception {
-    grantPermission();
-
     // deactivation
-    ActiveRuleKey key = ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1"));
+    ActiveRuleKey key = ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1);
     ruleActivator.deactivate(key);
 
     verifyZeroActiveRules(XOO_PROFILE_KEY);
@@ -292,7 +308,6 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void deactivation_fails_if_rule_not_found() throws Exception {
-    grantPermission();
     ActiveRuleKey key = ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x3"));
     try {
       ruleActivator.deactivate(key);
@@ -305,8 +320,7 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void deactivation_fails_if_profile_not_found() throws Exception {
-    grantPermission();
-    ActiveRuleKey key = ActiveRuleKey.of(QualityProfileKey.of("other", "js"), RuleKey.of("xoo", "x1"));
+    ActiveRuleKey key = ActiveRuleKey.of(QualityProfileKey.of("other", "js"), XOO_RULE_1);
     try {
       ruleActivator.deactivate(key);
       fail();
@@ -318,11 +332,10 @@ public class RuleActivatorMediumTest {
   // INHERITANCE OF PROFILES
   @Test
   public void activate_on_child_profile() throws Exception {
-    grantPermission();
     createChildProfiles();
 
     // activate on child profile, but not on root
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "7");
     ruleActivator.activate(activation);
@@ -334,11 +347,10 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void propagate_activation_on_child_profiles() throws Exception {
-    grantPermission();
     createChildProfiles();
 
     // activate on root profile
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "7");
     ruleActivator.activate(activation);
@@ -350,11 +362,10 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void propagate_activation_update_on_child_profiles() throws Exception {
-    grantPermission();
     createChildProfiles();
 
     // activate on root profile
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "7");
     ruleActivator.activate(activation);
@@ -363,7 +374,7 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7"));
 
     // update on parent
-    activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.INFO);
     activation.setParameter("max", "8");
     ruleActivator.activate(activation);
@@ -373,7 +384,7 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8"));
 
     // update on child -> propagate on grand child only
-    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.MINOR);
     activation.setParameter("max", "9");
     ruleActivator.activate(activation);
@@ -383,7 +394,7 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.MINOR, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "9"));
 
     // update on grand child
-    activation = new RuleActivation(ActiveRuleKey.of(XOO_GRAND_CHILD_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "10");
     ruleActivator.activate(activation);
@@ -395,11 +406,10 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void do_not_propagate_activation_update_on_child_overrides() throws Exception {
-    grantPermission();
     createChildProfiles();
 
     // activate on root profile
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.INFO);
     activation.setParameter("max", "7");
     ruleActivator.activate(activation);
@@ -408,7 +418,7 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7"));
 
     // override on child
-    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "8");
     ruleActivator.activate(activation);
@@ -418,7 +428,7 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8"));
 
     // change on parent -> do not propagate on children because they're overriding values
-    activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.CRITICAL);
     activation.setParameter("max", "10");
     ruleActivator.activate(activation);
@@ -428,13 +438,34 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8"));
   }
 
+  @Test
+  public void do_not_override_on_child_if_same_values() throws Exception {
+    createChildProfiles();
+
+    // activate on root profile
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
+    activation.setSeverity(Severity.INFO);
+    activation.setParameter("max", "7");
+    ruleActivator.activate(activation);
+    verifyOneActiveRule(XOO_PROFILE_KEY, Severity.INFO, null, ImmutableMap.of("max", "7"));
+    verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7"));
+
+    // override on child with same severity and params -> do nothing (still INHERITED but not OVERRIDDEN)
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1));
+    activation.setSeverity(Severity.INFO);
+    activation.setParameter("max", "7");
+    ruleActivator.activate(activation);
+    dbSession.clearCache();
+    verifyOneActiveRule(XOO_PROFILE_KEY, Severity.INFO, null, ImmutableMap.of("max", "7"));
+    verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7"));
+  }
+
   @Test
   public void propagate_deactivation_on_child_profiles() throws Exception {
-    grantPermission();
     createChildProfiles();
 
     // activate on root profile
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "7");
     ruleActivator.activate(activation);
@@ -452,11 +483,10 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void do_not_deactivate_inherited_or_overridden_rule() throws Exception {
-    grantPermission();
     createChildProfiles();
 
     // activate on root profile
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "7");
     ruleActivator.activate(activation);
@@ -466,7 +496,7 @@ public class RuleActivatorMediumTest {
 
     // try to deactivate on child
     try {
-      ruleActivator.deactivate(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+      ruleActivator.deactivate(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1));
       fail();
     } catch (IllegalStateException e) {
       assertThat(e).hasMessage("Cannot deactivate inherited rule 'xoo:x1'");
@@ -475,11 +505,10 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void reset_activation_on_child_profile() throws Exception {
-    grantPermission();
     createChildProfiles();
 
     // activate on root profile
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "7");
     ruleActivator.activate(activation);
@@ -488,7 +517,7 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7"));
 
     // override
-    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.INFO);
     activation.setParameter("max", "10");
     ruleActivator.activate(activation);
@@ -498,7 +527,7 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "10"));
 
     // reset -> remove overridden values
-    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1));
     ruleActivator.activate(activation);
     dbSession.clearCache();
     verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7"));
@@ -508,11 +537,10 @@ public class RuleActivatorMediumTest {
 
   @Test
   public void activation_reset_does_not_propagate_to_child_overrides() throws Exception {
-    grantPermission();
     createChildProfiles();
 
     // activate on root profile
-    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.BLOCKER);
     activation.setParameter("max", "7");
     ruleActivator.activate(activation);
@@ -521,7 +549,7 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7"));
 
     // override on child
-    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.INFO);
     activation.setParameter("max", "10");
     ruleActivator.activate(activation);
@@ -531,7 +559,7 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "10"));
 
     // override on grand child
-    activation = new RuleActivation(ActiveRuleKey.of(XOO_GRAND_CHILD_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1));
     activation.setSeverity(Severity.MINOR);
     activation.setParameter("max", "20");
     ruleActivator.activate(activation);
@@ -541,7 +569,7 @@ public class RuleActivatorMediumTest {
     verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.MINOR, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "20"));
 
     // reset child -> keep the overridden grand-child
-    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, RuleKey.of("xoo", "x1")));
+    activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1));
     ruleActivator.activate(activation);
     dbSession.clearCache();
     verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7"));
@@ -591,10 +619,6 @@ public class RuleActivatorMediumTest {
     dbSession.commit();
   }
 
-  private void grantPermission() {
-    MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN).setLogin("marius");
-  }
-
   private void verifyZeroActiveRules(QualityProfileKey key) {
     // verify db
     dbSession.clearCache();
index cde6ad18da4765883c038c9a6a2c6cca45c50552..194456efe1b3e5b16730d4b7d0e7bfda754d529c 100644 (file)
@@ -101,12 +101,4 @@ public class RuleChangeTest extends AbstractDbUnitTestCase {
     checkTables("changeParentProfile", new String[]{"change_date"}, "active_rule_changes");
   }
 
-  @Test
-  public void should_track_remove_activated_rules() {
-    setupData("initialData");
-    Rule rule = getSession().reattach(Rule.class, 1);
-    profilesManager.removeActivatedRules(rule.getId());
-    checkTables("removeActivatedRules", new String[]{"change_date"}, "active_rule_changes", "active_rules");
-  }
-
 }
index 77330f4a7ce725bcfffee19a3bd5a1f1ac3eb7b5..f4b55bfcbc0a44732c5c7d7f96b0464efde948e6 100644 (file)
@@ -36,7 +36,7 @@ import org.sonar.core.rule.RuleDto;
 import org.sonar.core.rule.RuleParamDto;
 import org.sonar.core.technicaldebt.db.CharacteristicDao;
 import org.sonar.server.db.DbClient;
-import org.sonar.server.qualityprofile.ProfilesManager;
+import org.sonar.server.qualityprofile.QProfileService;
 import org.sonar.server.qualityprofile.db.ActiveRuleDao;
 import org.sonar.server.rule.db.RuleDao;
 
@@ -53,7 +53,7 @@ public class RegisterRulesTest extends AbstractDaoTestCase {
   static final Date DATE2 = DateUtils.parseDateTime("2014-02-01T12:10:03+0100");
   static final Date DATE3 = DateUtils.parseDateTime("2014-03-01T12:10:03+0100");
 
-  ProfilesManager profilesManager = mock(ProfilesManager.class);
+  QProfileService profileService = mock(QProfileService.class);
   System2 system;
   DbClient dbClient;
   DbSession dbSession;
@@ -200,7 +200,7 @@ public class RegisterRulesTest extends AbstractDaoTestCase {
 
   private void execute(RulesDefinition... defs) {
     RuleDefinitionsLoader loader = new RuleDefinitionsLoader(mock(RuleRepositories.class), defs);
-    RegisterRules task = new RegisterRules(loader, profilesManager, dbClient, system);
+    RegisterRules task = new RegisterRules(loader, profileService, dbClient, system);
     task.start();
   }
 
index e146c532c958ee743976d39defa90258aff5ffcb..b3944a21efe3ec1e8ca89b773b1300b43c6f6914 100644 (file)
@@ -299,13 +299,11 @@ public class RuleBackendMediumTest {
   @Test
   public void should_not_find_removed() {
     // insert db
-    RuleKey ruleKey = RuleKey.of("javascript", "S001");
-    RuleDto ruleDto = newRuleDto(ruleKey).setStatus(RuleStatus.READY);
-
+    RuleKey readyKey = RuleKey.of("javascript", "S001");
+    RuleDto readyDto = RuleTesting.newDto(readyKey).setStatus(RuleStatus.READY);
     RuleKey removedKey = RuleKey.of("javascript", "S002");
-    RuleDto removedDto = newRuleDto(removedKey).setStatus(RuleStatus.REMOVED);
-    dao.insert(dbSession, ruleDto);
-    dao.insert(dbSession, removedDto);
+    RuleDto removedDto = RuleTesting.newDto(removedKey).setStatus(RuleStatus.REMOVED);
+    dao.insert(dbSession, readyDto, removedDto);
     dbSession.commit();
 
     // 0. Assert rules are in DB
@@ -315,8 +313,9 @@ public class RuleBackendMediumTest {
     assertThat(index.getByKey(removedKey)).isNotNull();
 
     // 2. assert find does not get REMOVED
-    assertThat(index.search(new RuleQuery(), QueryOptions.DEFAULT)
-      .getRules()).hasSize(1);
+    List<Rule> rules = index.search(new RuleQuery(), QueryOptions.DEFAULT).getHits();
+    assertThat(rules).hasSize(1);
+    assertThat(rules.get(0).key()).isEqualTo(readyKey);
   }
 
   private RuleDto newRuleDto(RuleKey ruleKey) {
diff --git a/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/expected-backup.xml b/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/expected-backup.xml
new file mode 100644 (file)
index 0000000..1786ed9
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<profile>
+  <name>P1</name>
+  <language>xoo</language>
+  <rules>
+    <rule>
+      <repositoryKey>xoo</repositoryKey>
+      <key>x1</key>
+      <priority>BLOCKER</priority>
+      <parameters>
+        <parameter>
+          <key>max</key>
+          <value>7</value>
+        </parameter>
+      </parameters>
+    </rule>
+  </rules>
+</profile>
diff --git a/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/not-xml-backup.txt b/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/not-xml-backup.txt
new file mode 100644 (file)
index 0000000..53451f2
--- /dev/null
@@ -0,0 +1 @@
+invalid backup
diff --git a/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore-child.xml b/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore-child.xml
new file mode 100644 (file)
index 0000000..94c63aa
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<profile>
+  <name>P2</name>
+  <language>xoo</language>
+  <rules>
+    <rule>
+      <repositoryKey>xoo</repositoryKey>
+      <key>x1</key>
+      <priority>BLOCKER</priority>
+      <parameters>
+        <parameter>
+          <key>max</key>
+          <value>7</value>
+        </parameter>
+      </parameters>
+    </rule>
+  </rules>
+</profile>
diff --git a/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore-parent.xml b/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore-parent.xml
new file mode 100644 (file)
index 0000000..1786ed9
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<profile>
+  <name>P1</name>
+  <language>xoo</language>
+  <rules>
+    <rule>
+      <repositoryKey>xoo</repositoryKey>
+      <key>x1</key>
+      <priority>BLOCKER</priority>
+      <parameters>
+        <parameter>
+          <key>max</key>
+          <value>7</value>
+        </parameter>
+      </parameters>
+    </rule>
+  </rules>
+</profile>
diff --git a/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore.xml b/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore.xml
new file mode 100644 (file)
index 0000000..1786ed9
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<profile>
+  <name>P1</name>
+  <language>xoo</language>
+  <rules>
+    <rule>
+      <repositoryKey>xoo</repositoryKey>
+      <key>x1</key>
+      <priority>BLOCKER</priority>
+      <parameters>
+        <parameter>
+          <key>max</key>
+          <value>7</value>
+        </parameter>
+      </parameters>
+    </rule>
+  </rules>
+</profile>
diff --git a/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore_fails_to_deactivate_inherited_rule.xml b/sonar-server/src/test/resources/org/sonar/server/qualityprofile/QProfileBackuperMediumTest/restore_fails_to_deactivate_inherited_rule.xml
new file mode 100644 (file)
index 0000000..368fa6c
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<profile>
+  <name>P2</name>
+  <language>xoo</language>
+  <rules>
+    <rule>
+      <repositoryKey>xoo</repositoryKey>
+      <key>x2</key>
+      <priority>BLOCKER</priority>
+    </rule>
+  </rules>
+</profile>