]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5056 Refactoring of restore feature
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 26 Mar 2014 10:50:57 +0000 (11:50 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 26 Mar 2014 10:51:06 +0000 (11:51 +0100)
sonar-server/src/main/java/org/sonar/server/debt/DebtModelBackup.java
sonar-server/src/main/java/org/sonar/server/debt/DebtModelOperations.java
sonar-server/src/test/java/org/sonar/server/debt/DebtModelBackupTest.java

index 7c6ae137ce28513a9328da7d81b3fff8963980ee..068294006f14fbcd75dada2d4723e743e2fcb127 100644 (file)
@@ -124,107 +124,119 @@ public class DebtModelBackup implements ServerComponent {
   }
 
   /**
-   * Restore from provided model
+   * Restore from provided model (characteristics and rule debt are restored)
    */
   public void restore() {
-    restoreProvided(loadModelFromPlugin(DebtModelPluginRepository.DEFAULT_MODEL), null);
+    checkPermission();
+
+    Date updateDate = new Date(system2.now());
+    SqlSession session = mybatis.openSession();
+    try {
+      restoreCharacteristics(loadModelFromPlugin(DebtModelPluginRepository.DEFAULT_MODEL), updateDate, session);
+      restoreProvidedModel(ruleDao.selectEnablesAndNonManual(session), updateDate, session);
+      session.commit();
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
   }
 
   /**
-   * Restore from plugins providing rules for a given language
+   * Restore from plugins providing rules for a given language (only debt of rules on given language are restored)
    */
-  public void restore(String languageKey) {
-    restoreProvided(loadModelFromPlugin(DebtModelPluginRepository.DEFAULT_MODEL), languageKey);
-  }
-
-  private void restoreProvided(DebtModel modelToImport, @Nullable String languageKey) {
+  public void restore(final String languageKey) {
     checkPermission();
 
     Date updateDate = new Date(system2.now());
     SqlSession session = mybatis.openSession();
     try {
-      restoreCharacteristics(modelToImport, updateDate, session);
-      for (RuleDto rule : ruleDao.selectEnablesAndNonManual(session)) {
-        if (languageKey == null || languageKey.equals(rule.getLanguage())) {
-          rule.setCharacteristicId(null);
-          rule.setRemediationFunction(null);
-          rule.setRemediationFactor(null);
-          rule.setRemediationOffset(null);
-          rule.setUpdatedAt(updateDate);
-          ruleDao.update(rule, session);
-          // TODO index rules in E/S
+      List<RuleDto> rules = newArrayList(Iterables.filter(ruleDao.selectEnablesAndNonManual(session), new Predicate<RuleDto>() {
+        @Override
+        public boolean apply(RuleDto input) {
+          return languageKey.equals(input.getLanguage());
         }
-      }
+      }));
+      restoreProvidedModel(rules, updateDate, session);
       session.commit();
     } finally {
       MyBatis.closeQuietly(session);
     }
   }
 
+  private void restoreProvidedModel(List<RuleDto> rules, Date updateDate, SqlSession session) {
+    for (RuleDto rule : rules) {
+      disabledRuleDebt(rule, updateDate, session);
+    }
+  }
+
   /**
-   * Restore model from a given XML model
+   * Restore model from a given XML model (characteristics and rule debt are restored from XML)
    */
   public ValidationMessages restoreFromXml(String xml) {
-    DebtModel debtModel = characteristicsXMLImporter.importXML(xml);
+    checkPermission();
+
     ValidationMessages validationMessages = ValidationMessages.create();
-    List<RuleDebt> ruleDebts = rulesXMLImporter.importXML(xml, validationMessages);
-    restore(debtModel, ruleDebts, null, validationMessages);
+    Date updateDate = new Date(system2.now());
+    SqlSession session = mybatis.openSession();
+    try {
+      List<CharacteristicDto> characteristicDtos = restoreCharacteristics(characteristicsXMLImporter.importXML(xml), updateDate, session);
+      restoreRules(characteristicDtos, ruleDao.selectEnablesAndNonManual(session), rulesXMLImporter.importXML(xml, validationMessages), validationMessages, updateDate, session);
+
+      session.commit();
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
     return validationMessages;
   }
 
   /**
-   * Restore model from a given XML model and a given language
+   * Restore model from a given XML model and a given language (only debt of rules on given language are restored from XML)
    */
-  public ValidationMessages restoreFromXml(String xml, String languageKey) {
-    DebtModel debtModel = characteristicsXMLImporter.importXML(xml);
-    ValidationMessages validationMessages = ValidationMessages.create();
-    List<RuleDebt> ruleDebts = rulesXMLImporter.importXML(xml, validationMessages);
-    restore(debtModel, ruleDebts, languageKey, validationMessages);
-    return validationMessages;
-  }
-
-  private void restore(DebtModel modelToImport, List<RuleDebt> ruleDebts, @Nullable String languageKey, ValidationMessages validationMessages) {
+  public ValidationMessages restoreFromXml(String xml, final String languageKey) {
     checkPermission();
 
+    ValidationMessages validationMessages = ValidationMessages.create();
     Date updateDate = new Date(system2.now());
     SqlSession session = mybatis.openSession();
     try {
-      List<CharacteristicDto> characteristicDtos = restoreCharacteristics(modelToImport, updateDate, session);
-      restoreRules(characteristicDtos, languageKey, ruleDebts, validationMessages, updateDate, session);
+      List<CharacteristicDto> characteristicDtos = dao.selectEnabledCharacteristics(session);
+      List<RuleDto> rules = newArrayList(Iterables.filter(ruleDao.selectEnablesAndNonManual(session), new Predicate<RuleDto>() {
+        @Override
+        public boolean apply(RuleDto input) {
+          return languageKey.equals(input.getLanguage());
+        }
+      }));
+      restoreRules(characteristicDtos, rules, rulesXMLImporter.importXML(xml, validationMessages), validationMessages, updateDate, session);
 
       session.commit();
     } finally {
       MyBatis.closeQuietly(session);
     }
+    return validationMessages;
   }
 
-  private void restoreRules(List<CharacteristicDto> characteristicDtos, @Nullable String languageKey, List<RuleDebt> ruleDebts,
+  private void restoreRules(List<CharacteristicDto> characteristicDtos, List<RuleDto> rules, List<RuleDebt> ruleDebts,
                             ValidationMessages validationMessages, Date updateDate, SqlSession session) {
-    for (RuleDto rule : ruleDao.selectEnablesAndNonManual(session)) {
-      if (languageKey == null || languageKey.equals(rule.getLanguage())) {
-        RuleDebt ruleDebt = ruleDebtByRule(rule, ruleDebts);
-        if (ruleDebt == null) {
-          rule.setCharacteristicId(rule.getDefaultCharacteristicId() != null ? RuleDto.DISABLED_CHARACTERISTIC_ID : null);
-          rule.setRemediationFunction(null);
-          rule.setRemediationFactor(null);
-          rule.setRemediationOffset(null);
+    for (RuleDto rule : rules) {
+      RuleDebt ruleDebt = ruleDebtByRule(rule, ruleDebts);
+      if (ruleDebt == null) {
+        disabledRuleDebt(rule, updateDate, session);
+      } else {
+        CharacteristicDto characteristicDto = characteristicByKey(ruleDebt.characteristicKey(), characteristicDtos);
+        if (characteristicDto == null) {
+          disabledRuleDebt(rule, updateDate, session);
         } else {
-          CharacteristicDto characteristicDto = characteristicByKey(ruleDebt.characteristicKey(), characteristicDtos, false);
-          // Characteristic cannot be null as it has been created just before
-
           boolean isSameCharacteristic = characteristicDto.getId().equals(rule.getDefaultCharacteristicId());
           boolean isSameFunction = isSameRemediationFunction(ruleDebt, rule);
           rule.setCharacteristicId((!isSameCharacteristic ? characteristicDto.getId() : null));
           rule.setRemediationFunction((!isSameFunction ? ruleDebt.function().name() : null));
           rule.setRemediationFactor((!isSameFunction ? ruleDebt.factor() : null));
           rule.setRemediationOffset((!isSameFunction ? ruleDebt.offset() : null));
+          rule.setUpdatedAt(updateDate);
+          ruleDao.update(rule, session);
+          // TODO index rules in E/S
         }
-
-        ruleDebts.remove(ruleDebt);
-        rule.setUpdatedAt(updateDate);
-        ruleDao.update(rule, session);
-        // TODO index rules in E/S
       }
+      ruleDebts.remove(ruleDebt);
     }
 
     for (RuleDebt ruleDebt : ruleDebts) {
@@ -257,7 +269,7 @@ public class DebtModelBackup implements ServerComponent {
 
   private CharacteristicDto restoreCharacteristic(DebtCharacteristic targetCharacteristic, @Nullable Integer parentId, List<CharacteristicDto> sourceCharacteristics,
                                                   Date updateDate, SqlSession session) {
-    CharacteristicDto sourceCharacteristic = characteristicByKey(targetCharacteristic.key(), sourceCharacteristics, true);
+    CharacteristicDto sourceCharacteristic = characteristicByKey(targetCharacteristic.key(), sourceCharacteristics);
     if (sourceCharacteristic == null) {
       CharacteristicDto newCharacteristic = toDto(targetCharacteristic, parentId).setCreatedAt(updateDate);
       dao.insert(newCharacteristic, session);
@@ -283,6 +295,15 @@ public class DebtModelBackup implements ServerComponent {
       .isEquals();
   }
 
+  private void disabledRuleDebt(RuleDto rule, Date updateDate, SqlSession session){
+    rule.setCharacteristicId(rule.getDefaultCharacteristicId() != null ? RuleDto.DISABLED_CHARACTERISTIC_ID : null);
+    rule.setRemediationFunction(null);
+    rule.setRemediationFactor(null);
+    rule.setRemediationOffset(null);
+    rule.setUpdatedAt(updateDate);
+    ruleDao.update(rule, session);
+  }
+
   private DebtModel loadModelFromPlugin(String pluginKey) {
     Reader xmlFileReader = null;
     try {
@@ -306,17 +327,14 @@ public class DebtModelBackup implements ServerComponent {
     }, null);
   }
 
-  private static CharacteristicDto characteristicByKey(final String key, List<CharacteristicDto> characteristicDtos, boolean canByNull) {
-    CharacteristicDto dto = Iterables.find(characteristicDtos, new Predicate<CharacteristicDto>() {
+  @CheckForNull
+  private static CharacteristicDto characteristicByKey(final String key, List<CharacteristicDto> characteristicDtos) {
+    return Iterables.find(characteristicDtos, new Predicate<CharacteristicDto>() {
       @Override
       public boolean apply(CharacteristicDto input) {
         return key.equals(input.getKey());
       }
     }, null);
-    if (dto == null && !canByNull) {
-      throw new IllegalStateException(String.format("Characteristic with key '%s' has not been found ", key));
-    }
-    return dto;
   }
 
   private static List<CharacteristicDto> subCharacteristics(final Integer parentId, List<CharacteristicDto> allCharacteristics) {
index cbd9c4bcd6376e84872651e7e62e5e8e5a798318..521322cc48c6f210437b8e4ca813e0207ee39286 100644 (file)
@@ -183,12 +183,12 @@ public class DebtModelOperations implements ServerComponent {
       if (characteristicOrSubCharacteristic.getParentId() == null) {
         List<CharacteristicDto> subCharacteristics = dao.selectCharacteristicsByParentId(characteristicOrSubCharacteristic.getId(), session);
         for (CharacteristicDto subCharacteristic : subCharacteristics) {
-          disableSubChracteristic(subCharacteristic, updateDate, session);
+          disableSubCharacteristic(subCharacteristic, updateDate, session);
         }
         disableCharacteristic(characteristicOrSubCharacteristic, updateDate, session);
       } else {
         // When sub characteristic, disable rule debt on the sub characteristic then disable it
-        disableSubChracteristic(characteristicOrSubCharacteristic, updateDate, session);
+        disableSubCharacteristic(characteristicOrSubCharacteristic, updateDate, session);
       }
       session.commit();
     } finally {
@@ -196,7 +196,7 @@ public class DebtModelOperations implements ServerComponent {
     }
   }
 
-  private void disableSubChracteristic(CharacteristicDto subCharacteristic, Date updateDate, SqlSession session) {
+  private void disableSubCharacteristic(CharacteristicDto subCharacteristic, Date updateDate, SqlSession session) {
     disableDebtRules(ruleDao.selectBySubCharacteristicId(subCharacteristic.getId(), session), updateDate, session);
     disableCharacteristic(subCharacteristic, updateDate, session);
   }
index dbad6dea2ecbd8bd7254a53c25510b6dc2380736..f96bfdbfab14566c2d626f2d3a275c3b6aba3377 100644 (file)
@@ -105,8 +105,8 @@ public class DebtModelBackupTest {
 
   int currentId;
 
-  DebtModel debtModel = new DebtModel();
-  List<DebtModelXMLExporter.RuleDebt> rules = newArrayList();
+  DebtModel xmlDebtModel = new DebtModel();
+  List<RuleDebt> xmlDebtRules = newArrayList();
 
   DebtModelBackup debtModelBackup;
 
@@ -131,9 +131,9 @@ public class DebtModelBackupTest {
 
     Reader defaultModelReader = mock(Reader.class);
     when(debtModelPluginRepository.createReaderForXMLFile("technical-debt")).thenReturn(defaultModelReader);
-    when(characteristicsXMLImporter.importXML(eq(defaultModelReader))).thenReturn(debtModel);
-    when(characteristicsXMLImporter.importXML(anyString())).thenReturn(debtModel);
-    when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(rules);
+    when(characteristicsXMLImporter.importXML(any(Reader.class))).thenReturn(xmlDebtModel);
+//    when(characteristicsXMLImporter.importXML(anyString())).thenReturn(xmlDebtModel);
+//    when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(xmlDebtRules);
 
     debtModelBackup = new DebtModelBackup(myBatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, characteristicsXMLImporter, rulesXMLImporter,
       debtModelXMLExporter, system2);
@@ -320,9 +320,9 @@ public class DebtModelBackupTest {
 
   @Test
   public void restore_from_provided_model() throws Exception {
-    debtModel
+    when(characteristicsXMLImporter.importXML(any(Reader.class))).thenReturn(new DebtModel()
       .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
-      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"));
 
     when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
       new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate),
@@ -356,53 +356,79 @@ public class DebtModelBackupTest {
 
   @Test
   public void restore_from_language() throws Exception {
-    debtModel
-      .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
-      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+    when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+      new RuleDto().setId(1).setRepositoryKey("squid").setLanguage("java")
+        .setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
+        .setCreatedAt(oldDate).setUpdatedAt(oldDate),
+      // Should be ignored because linked on another language
+      new RuleDto().setId(2).setRepositoryKey("checkstyle").setLanguage("java2")
+        .setCharacteristicId(2).setRemediationFunction("LINEAR").setRemediationFactor("2h")
+        .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+    ));
+
+    debtModelBackup.restore("java");
+
+    verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
 
+    verify(ruleDao).selectEnablesAndNonManual(session);
+    verify(ruleDao).update(ruleArgument.capture(), eq(session));
+    verifyNoMoreInteractions(ruleDao);
+
+    RuleDto rule = ruleArgument.getValue();
+    assertThat(rule.getId()).isEqualTo(1);
+    assertThat(rule.getCharacteristicId()).isNull();
+    assertThat(rule.getRemediationFunction()).isNull();
+    assertThat(rule.getRemediationFactor()).isNull();
+    assertThat(rule.getRemediationOffset()).isNull();
+    assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
+    verify(session).commit();
+  }
+
+  @Test
+  public void restore_from_language_with_rule_linked_on_disabled_default_characteristic() throws Exception {
     when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
       new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate),
       new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate)
     ));
 
     when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
-      new RuleDto().setId(1).setRepositoryKey("squid").setLanguage("java")
+      // Linked on a disabled default characteristic -> Rule debt should be disabled
+      new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck").setLanguage("java")
+        .setDefaultCharacteristicId(3).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
         .setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
-        .setCreatedAt(oldDate).setUpdatedAt(oldDate),
-      // Should be ignored
-      new RuleDto().setId(2).setRepositoryKey("checkstyle").setLanguage("java2")
-        .setCharacteristicId(3).setRemediationFunction("LINEAR").setRemediationFactor("2h")
         .setCreatedAt(oldDate).setUpdatedAt(oldDate)
     ));
 
     debtModelBackup.restore("java");
 
-    verify(dao).selectEnabledCharacteristics(session);
-    verify(dao, times(2)).update(any(CharacteristicDto.class), eq(session));
-    verifyNoMoreInteractions(dao);
-
     verify(ruleDao).selectEnablesAndNonManual(session);
     verify(ruleDao).update(ruleArgument.capture(), eq(session));
     verifyNoMoreInteractions(ruleDao);
 
     RuleDto rule = ruleArgument.getValue();
     assertThat(rule.getId()).isEqualTo(1);
+    assertThat(rule.getCharacteristicId()).isEqualTo(-1);
+    assertThat(rule.getRemediationFunction()).isNull();
+    assertThat(rule.getRemediationFactor()).isNull();
+    assertThat(rule.getRemediationOffset()).isNull();
+    assertThat(rule.getUpdatedAt()).isEqualTo(now);
 
     verify(session).commit();
   }
 
   @Test
   public void restore_from_xml_with_different_characteristic_and_same_function() throws Exception {
-    debtModel
+    when(characteristicsXMLImporter.importXML(anyString())).thenReturn(new DebtModel()
       .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
-      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"));
 
     when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
       new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
       new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
 
-    rules.add(new RuleDebt()
-      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h"));
+    when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(newArrayList(new RuleDebt()
+      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h")));
 
     when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
       new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
@@ -429,16 +455,16 @@ public class DebtModelBackupTest {
 
   @Test
   public void restore_from_xml_with_same_characteristic_and_different_function() throws Exception {
-    debtModel
+    when(characteristicsXMLImporter.importXML(anyString())).thenReturn(new DebtModel()
       .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
-      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"));
 
     when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
       new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
       new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
 
-    rules.add(new RuleDebt()
-      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("12h").setOffset("11min"));
+    when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(newArrayList(new RuleDebt()
+      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("12h").setOffset("11min")));
 
     when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
       new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
@@ -465,16 +491,16 @@ public class DebtModelBackupTest {
 
   @Test
   public void restore_from_xml_with_same_characteristic_and_same_function() throws Exception {
-    debtModel
+    when(characteristicsXMLImporter.importXML(anyString())).thenReturn(new DebtModel()
       .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
-      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"));
 
     when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
       new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
       new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
 
-    rules.add(new RuleDebt()
-      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("2h").setOffset("15min"));
+    when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(newArrayList(new RuleDebt()
+      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("2h").setOffset("15min")));
 
     when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
       new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
@@ -501,9 +527,9 @@ public class DebtModelBackupTest {
 
   @Test
   public void restore_from_xml_disable_rule_debt_when_not_in_xml_and_rule_have_default_debt_values() throws Exception {
-    debtModel
+    when(characteristicsXMLImporter.importXML(anyString())).thenReturn(new DebtModel()
       .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
-      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"));
 
     when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
       new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
@@ -534,9 +560,9 @@ public class DebtModelBackupTest {
 
   @Test
   public void restore_from_xml_set_no_rule_debt_when_not_in_xml_and_rule_has_no_default_debt_values() throws Exception {
-    debtModel
+    when(characteristicsXMLImporter.importXML(anyString())).thenReturn(new DebtModel()
       .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
-      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"));
 
     when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
       new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
@@ -567,16 +593,12 @@ public class DebtModelBackupTest {
 
   @Test
   public void restore_from_xml_and_language() throws Exception {
-    debtModel
-      .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
-      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
-
     when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
       new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
       new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
 
-    rules.add(new RuleDebt()
-      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h"));
+    when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(newArrayList(new RuleDebt()
+      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h")));
 
     when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
       new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck").setLanguage("java")
@@ -590,6 +612,9 @@ public class DebtModelBackupTest {
 
     debtModelBackup.restoreFromXml("<xml/>", "java");
 
+    verify(characteristicsXMLImporter, never()).importXML(anyString());
+    verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
+
     verify(ruleDao).selectEnablesAndNonManual(session);
     verify(ruleDao).update(ruleArgument.capture(), eq(session));
     verifyNoMoreInteractions(ruleDao);
@@ -600,24 +625,96 @@ public class DebtModelBackupTest {
     verify(session).commit();
   }
 
+  @Test
+  public void restore_from_xml_and_language_with_rule_not_in_xml_and_linked_on_disabled_default_characteristic() throws Exception {
+    when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
+      new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate),
+      new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate)
+    ));
+
+    when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(Collections.<RuleDebt>emptyList());
+    when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+      // Linked on a default disabled characteristic -> Rule debt should be disabled
+      new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck").setLanguage("java")
+        .setDefaultCharacteristicId(3).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
+        .setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
+        .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+    ));
+
+    debtModelBackup.restoreFromXml("<xml/>", "java");
+
+    verify(characteristicsXMLImporter, never()).importXML(anyString());
+    verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
+
+    verify(ruleDao).selectEnablesAndNonManual(session);
+    verify(ruleDao).update(ruleArgument.capture(), eq(session));
+    verifyNoMoreInteractions(ruleDao);
+
+    RuleDto rule = ruleArgument.getValue();
+    assertThat(rule.getId()).isEqualTo(1);
+    assertThat(rule.getCharacteristicId()).isEqualTo(-1);
+    assertThat(rule.getRemediationFunction()).isNull();
+    assertThat(rule.getRemediationFactor()).isNull();
+    assertThat(rule.getRemediationOffset()).isNull();
+    assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
+    verify(session).commit();
+  }
+
+  @Test
+  public void restore_from_xml_and_language_with_rule_linked_on_disabled_characteristic2() throws Exception {
+    when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
+      new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate),
+      new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate)
+    ));
+
+    when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+      new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck").setLanguage("java")
+        .setDefaultCharacteristicId(3).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
+        .setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
+        .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+    ));
+
+    when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(newArrayList(new RuleDebt()
+      // Linked on a default disabled characteristic -> Rule debt should be disabled
+      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("HARDWARE").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h")));
+
+    debtModelBackup.restoreFromXml("<xml/>", "java");
+
+    verify(characteristicsXMLImporter, never()).importXML(anyString());
+    verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
+
+    verify(ruleDao).selectEnablesAndNonManual(session);
+    verify(ruleDao).update(ruleArgument.capture(), eq(session));
+    verifyNoMoreInteractions(ruleDao);
+
+    RuleDto rule = ruleArgument.getValue();
+    assertThat(rule.getId()).isEqualTo(1);
+    assertThat(rule.getCharacteristicId()).isEqualTo(-1);
+    assertThat(rule.getRemediationFunction()).isNull();
+    assertThat(rule.getRemediationFactor()).isNull();
+    assertThat(rule.getRemediationOffset()).isNull();
+    assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
+    verify(session).commit();
+  }
+
   @Test
   public void add_warning_message_when_rule_from_xml_is_not_found() throws Exception {
-    debtModel
+    when(characteristicsXMLImporter.importXML(anyString())).thenReturn(new DebtModel()
       .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
-      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+      .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"));
 
     when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
       new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
       new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
 
-    rules.add(new RuleDebt()
-      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h"));
+    when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(newArrayList(new RuleDebt()
+      .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h")));
 
     when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(Collections.<RuleDto>emptyList());
 
-    ValidationMessages validationMessages = debtModelBackup.restoreFromXml("<xml/>");
-
-    assertThat(validationMessages.getWarnings()).hasSize(1);
+    assertThat(debtModelBackup.restoreFromXml("<xml/>").getWarnings()).hasSize(1);
 
     verify(ruleDao).selectEnablesAndNonManual(session);
     verifyNoMoreInteractions(ruleDao);