]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5174 Improve migration of Requirements to Rules when offset or coefficient...
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 2 Apr 2014 06:29:39 +0000 (08:29 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 2 Apr 2014 06:29:39 +0000 (08:29 +0200)
sonar-core/src/main/java/org/sonar/core/technicaldebt/db/RequirementDto.java
sonar-core/src/main/resources/org/sonar/core/technicaldebt/db/RequirementMapper.xml
sonar-core/src/test/java/org/sonar/core/technicaldebt/db/RequirementDaoTest.java
sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/RequirementDaoTest/select_requirement.xml [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/startup/CopyRequirementsFromCharacteristicsToRules.java
sonar-server/src/test/java/org/sonar/server/startup/CopyRequirementsFromCharacteristicsToRulesTest.java
sonar-server/src/test/resources/org/sonar/server/startup/CopyRequirementsFromCharacteristicsToRulesTest/copy_requirements_from_characteristics_to_rules.xml
sonar-server/src/test/resources/org/sonar/server/startup/CopyRequirementsFromCharacteristicsToRulesTest/copy_requirements_from_characteristics_to_rules_result.xml
sonar-server/src/test/resources/org/sonar/server/startup/CopyRequirementsFromCharacteristicsToRulesTest/requirements.xml

index 80ea26f3e4ed838009fe09862a5fd846d27c3e03..3cd3a8f9fff891d933fc0c9ea400fc0c2012b881 100644 (file)
@@ -35,8 +35,8 @@ public class RequirementDto implements Serializable {
   private Integer rootId;
   private Integer ruleId;
   private String functionKey;
-  private Double factorValue;
-  private String factorUnit;
+  private Double coefficientValue;
+  private String coefficientUnit;
   private Double offsetValue;
   private String offsetUnit;
   private Date createdAt;
@@ -89,22 +89,22 @@ public class RequirementDto implements Serializable {
   }
 
   @CheckForNull
-  public Double getFactorValue() {
-    return factorValue;
+  public Double getCoefficientValue() {
+    return coefficientValue;
   }
 
-  public RequirementDto setFactorValue(@Nullable Double factor) {
-    this.factorValue = factor;
+  public RequirementDto setCoefficientValue(@Nullable Double coefficientValue) {
+    this.coefficientValue = coefficientValue;
     return this;
   }
 
   @CheckForNull
-  public String getFactorUnit() {
-    return factorUnit;
+  public String getCoefficientUnit() {
+    return coefficientUnit;
   }
 
-  public RequirementDto setFactorUnit(@Nullable String factorUnit) {
-    this.factorUnit = factorUnit;
+  public RequirementDto setCoefficientUnit(@Nullable String coefficientUnit) {
+    this.coefficientUnit = coefficientUnit;
     return this;
   }
 
index 983b39e06676446016b073e0ca5578bf2c7b61f0..9ef11bbd4e01c59526655010c8b57cbf3f8099e9 100644 (file)
@@ -9,8 +9,8 @@
     c.root_id as rootId,
     c.rule_id as ruleId,
     c.function_key as functionKey,
-    c.factor_value as factorValue,
-    c.factor_unit as factorUnit,
+    c.factor_value as coefficientValue,
+    c.factor_unit as coefficientUnit,
     c.offset_value as offsetValue,
     c.offset_unit as offsetUnit,
     c.enabled as enabled,
index d16c7af1e0acd9dc30472bb0c033f6599b55a142..4d589151296d361a7a6c55490a11ea17a05df06b 100644 (file)
@@ -23,6 +23,8 @@ import org.junit.Before;
 import org.junit.Test;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 
+import java.util.List;
+
 import static org.fest.assertions.Assertions.assertThat;
 
 public class RequirementDaoTest extends AbstractDaoTestCase {
@@ -43,4 +45,25 @@ public class RequirementDaoTest extends AbstractDaoTestCase {
     assertThat(dao.selectRequirements()).hasSize(2);
   }
 
+  @Test
+  public void select_requirement() {
+    setupData("select_requirement");
+
+    List<RequirementDto> dtos = dao.selectRequirements();
+    assertThat(dtos).hasSize(1);
+
+    RequirementDto dto = dtos.get(0);
+    assertThat(dto.getId()).isEqualTo(3);
+    assertThat(dto.getParentId()).isEqualTo(2);
+    assertThat(dto.getRuleId()).isEqualTo(10);
+    assertThat(dto.getFunction()).isEqualTo("linear_offset");
+    assertThat(dto.getCoefficientValue()).isEqualTo(20d);
+    assertThat(dto.getCoefficientUnit()).isEqualTo("mn");
+    assertThat(dto.getOffsetValue()).isEqualTo(30d);
+    assertThat(dto.getOffsetUnit()).isEqualTo("h");
+    assertThat(dto.isEnabled()).isTrue();
+    assertThat(dto.getCreatedAt()).isNotNull();
+    assertThat(dto.getUpdatedAt()).isNull();
+  }
+
 }
diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/RequirementDaoTest/select_requirement.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/db/RequirementDaoTest/select_requirement.xml
new file mode 100644 (file)
index 0000000..844c901
--- /dev/null
@@ -0,0 +1,8 @@
+<dataset>
+
+  <!-- Requirement -->
+  <characteristics id="3" kee="[null]" name="[null]" parent_id="2" root_id="1" rule_id="10" characteristic_order="[null]"
+                   function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[true]"
+                   created_at="2013-11-20" updated_at="[null]"/>
+
+</dataset>
index e20ed4bb7a9ce179e3e81ccc4bbff16efee1677b..a7153b0db2ad0b1fd6531a7aa86085e60f4b5efc 100644 (file)
@@ -51,7 +51,7 @@ import java.util.List;
 
 /**
  * This script copy every requirements from characteristics table (every row where rule_id is not null) to the rules table.
- *
+ * <p/>
  * This script need to be executed after rules registration because default debt columns (characteristics, function, coefficient and offset) has to be populated
  * in order to be able to compare default values with overridden values.
  *
@@ -122,7 +122,7 @@ public class CopyRequirementsFromCharacteristicsToRules {
     );
   }
 
-  private static class RuleInputLoader implements MassUpdater.InputLoader<RuleRow>{
+  private static class RuleInputLoader implements MassUpdater.InputLoader<RuleRow> {
     @Override
     public String selectSql() {
       return "SELECT r.id,r.characteristic_id,r.remediation_function,r.remediation_coeff,r.remediation_offset," +
@@ -136,18 +136,18 @@ public class CopyRequirementsFromCharacteristicsToRules {
       ruleRow.setId(SqlUtil.getInt(rs, 1));
       ruleRow.setCharacteristicId(SqlUtil.getInt(rs, 2));
       ruleRow.setFunction(rs.getString(3));
-      ruleRow.setFactor(rs.getString(4));
+      ruleRow.setCoefficient(rs.getString(4));
       ruleRow.setOffset(rs.getString(5));
       ruleRow.setDefaultCharacteristicId(SqlUtil.getInt(rs, 6));
       ruleRow.setDefaultFunction(rs.getString(7));
-      ruleRow.setDefaultFactor(rs.getString(8));
+      ruleRow.setDefaultCoefficient(rs.getString(8));
       ruleRow.setDefaultOffset(rs.getString(9));
       ruleRow.setStatus(rs.getString(10));
       return ruleRow;
     }
   }
 
-  private static class RuleInputConvertor implements MassUpdater.InputConverter<RuleRow>{
+  private static class RuleInputConvertor implements MassUpdater.InputConverter<RuleRow> {
 
     private final Multimap<Integer, RequirementDto> requirementsByRuleId;
     private final System2 system2;
@@ -195,14 +195,22 @@ public class CopyRequirementsFromCharacteristicsToRules {
 
         ruleRow.setCharacteristicId(enabledRequirement.getParentId());
         ruleRow.setFunction(enabledRequirement.getFunction().toUpperCase());
-        ruleRow.setFactor(convertDuration(enabledRequirement.getFactorValue(), enabledRequirement.getFactorUnit()));
+        ruleRow.setCoefficient(convertDuration(enabledRequirement.getCoefficientValue(), enabledRequirement.getCoefficientUnit()));
         ruleRow.setOffset(convertDuration(enabledRequirement.getOffsetValue(), enabledRequirement.getOffsetUnit()));
 
+        // If the coefficient of a linear or linear with offset function is null, it should be replaced by 0
+        if (("LINEAR".equals(ruleRow.getFunction()) || "LINEAR_OFFSET".equals(ruleRow.getFunction())) && ruleRow.getCoefficient() == null) {
+          ruleRow.setCoefficient("0" + convertUnit(enabledRequirement.getCoefficientUnit()));
+          // If the offset of a constant per issue or linear with offset function is null, it should be replaced by 0
+        } else if (("CONSTANT_ISSUE".equals(ruleRow.getFunction()) || "LINEAR_OFFSET".equals(ruleRow.getFunction())) && ruleRow.getOffset() == null) {
+          ruleRow.setOffset("0" + convertUnit(enabledRequirement.getOffsetUnit()));
+        }
+
         if (!isDebtDefaultValuesSameAsOverriddenValues(ruleRow)) {
           // Default values on debt are not the same that ones set by SQALE, update the rule
           updateStatement.setInt(1, ruleRow.getCharacteristicId());
           updateStatement.setString(2, ruleRow.getFunction());
-          updateStatement.setString(3, ruleRow.getFactor());
+          updateStatement.setString(3, ruleRow.getCoefficient());
           updateStatement.setString(4, ruleRow.getOffset());
           updateStatement.setTimestamp(5, new Timestamp(system2.now()));
           updateStatement.setInt(6, ruleRow.getId());
@@ -218,21 +226,24 @@ public class CopyRequirementsFromCharacteristicsToRules {
   @VisibleForTesting
   static String convertDuration(@Nullable Double oldValue, @Nullable String oldUnit) {
     if (oldValue != null && oldValue > 0) {
-      String unit = oldUnit != null ? oldUnit : Duration.DAY;
-      // min is replaced by mn
-      unit = "mn".equals(unit) ? Duration.MINUTE : unit;
       // As value is stored in double, we have to round it in order to have an integer (for instance, if it was 1.6, we'll use 2)
-      return Integer.toString((int) Math.round(oldValue)) + unit;
+      return Integer.toString((int) Math.round(oldValue)) + convertUnit(oldUnit);
     }
     return null;
   }
 
+  @VisibleForTesting
+  private static String convertUnit(@Nullable String oldUnit) {
+    String unit = oldUnit != null ? oldUnit : Duration.DAY;
+    return "mn".equals(unit) ? Duration.MINUTE : unit;
+  }
+
   @VisibleForTesting
   static boolean isDebtDefaultValuesSameAsOverriddenValues(RuleRow ruleRow) {
     return new EqualsBuilder()
       .append(ruleRow.getDefaultCharacteristicId(), ruleRow.getCharacteristicId())
       .append(ruleRow.getDefaultFunction(), ruleRow.getFunction())
-      .append(ruleRow.getDefaultFactor(), ruleRow.getFactor())
+      .append(ruleRow.getDefaultCoefficient(), ruleRow.getCoefficient())
       .append(ruleRow.getDefaultOffset(), ruleRow.getOffset())
       .isEquals();
   }
@@ -259,8 +270,8 @@ public class CopyRequirementsFromCharacteristicsToRules {
     private Integer defaultCharacteristicId;
     private String function;
     private String defaultFunction;
-    private String factor;
-    private String defaultFactor;
+    private String coefficient;
+    private String defaultCoefficient;
     private String offset;
     private String defaultOffset;
     private String status;
@@ -315,22 +326,22 @@ public class CopyRequirementsFromCharacteristicsToRules {
     }
 
     @CheckForNull
-    String getFactor() {
-      return factor;
+    String getCoefficient() {
+      return coefficient;
     }
 
-    RuleRow setFactor(@Nullable String factor) {
-      this.factor = factor;
+    RuleRow setCoefficient(@Nullable String coefficient) {
+      this.coefficient = coefficient;
       return this;
     }
 
     @CheckForNull
-    String getDefaultFactor() {
-      return defaultFactor;
+    String getDefaultCoefficient() {
+      return defaultCoefficient;
     }
 
-    RuleRow setDefaultFactor(@Nullable String defaultFactor) {
-      this.defaultFactor = defaultFactor;
+    RuleRow setDefaultCoefficient(@Nullable String defaultCoefficient) {
+      this.defaultCoefficient = defaultCoefficient;
       return this;
     }
 
index f13544ddb5da2797bf9663ea403755813f15cc4d..1c7f5b37dc5bf5947501f1d8cb7ea91baccb389a 100644 (file)
@@ -108,28 +108,28 @@ public class CopyRequirementsFromCharacteristicsToRulesTest extends AbstractDaoT
     assertThat(CopyRequirementsFromCharacteristicsToRules.isDebtDefaultValuesSameAsOverriddenValues(new CopyRequirementsFromCharacteristicsToRules.RuleRow()
       .setDefaultCharacteristicId(1).setCharacteristicId(1)
       .setDefaultFunction("LINEAR_OFFSET").setFunction("LINEAR_OFFSET")
-      .setDefaultFactor("5h").setFactor("5h")
+      .setDefaultCoefficient("5h").setCoefficient("5h")
       .setDefaultOffset("10min").setOffset("10min")
     )).isTrue();
 
     assertThat(CopyRequirementsFromCharacteristicsToRules.isDebtDefaultValuesSameAsOverriddenValues(new CopyRequirementsFromCharacteristicsToRules.RuleRow()
       .setDefaultCharacteristicId(1).setCharacteristicId(2)
       .setDefaultFunction("LINEAR_OFFSET").setFunction("LINEAR_OFFSET")
-      .setDefaultFactor("5h").setFactor("5h")
+      .setDefaultCoefficient("5h").setCoefficient("5h")
       .setDefaultOffset("10min").setOffset("10min")
     )).isFalse();
 
     assertThat(CopyRequirementsFromCharacteristicsToRules.isDebtDefaultValuesSameAsOverriddenValues(new CopyRequirementsFromCharacteristicsToRules.RuleRow()
       .setDefaultCharacteristicId(1).setCharacteristicId(1)
       .setDefaultFunction("LINEAR_OFFSET").setFunction("LINEAR_OFFSET")
-      .setDefaultFactor("5h").setFactor("4h")
+      .setDefaultCoefficient("5h").setCoefficient("4h")
       .setDefaultOffset("10min").setOffset("5min")
     )).isFalse();
 
     assertThat(CopyRequirementsFromCharacteristicsToRules.isDebtDefaultValuesSameAsOverriddenValues(new CopyRequirementsFromCharacteristicsToRules.RuleRow()
       .setDefaultCharacteristicId(1).setCharacteristicId(1)
       .setDefaultFunction("CONSTANT_ISSUE").setFunction("LINEAR")
-      .setDefaultFactor(null).setFactor("5h")
+      .setDefaultCoefficient(null).setCoefficient("5h")
       .setDefaultOffset("10min").setOffset(null)
     )).isFalse();
   }
index a90e960c94e0db050ab02378d91a0151f6bb29f4..05fb8c1765333a21de0d2eea323388587ba0f133 100644 (file)
          remediation_coeff="[null]" default_remediation_coeff="5d"
          remediation_offset="[null]" default_remediation_offset="[null]" updated_at="2014-02-19"/>
 
+  <!-- Rule linked to one enable requirement on Constant per issue function (with a coefficient of 0d -> to be replaced by null), with different value of debt -> Update rule -->
+  <rules id="6" plugin_rule_key="HiddenFieldCheck" plugin_name="squid" name="HiddenFieldCheck" description="HiddenFieldCheck" status="READY"
+         characteristic_id="[null]" default_characteristic_id="20"
+         remediation_function="[null]" default_remediation_function="LINEAR"
+         remediation_coeff="[null]" default_remediation_coeff="5d"
+         remediation_offset="[null]" default_remediation_offset="[null]" updated_at="2014-02-19"/>
+
+  <!-- Rule linked to one enable requirement on Linear function (with a coeeficient of 0min -> to be replaced by null), with different value of debt -> Update rule -->
+  <rules id="7" plugin_rule_key="ForLoopCounterChangedCheck" plugin_name="squid" name="ForLoopCounterChangedCheck" description="ForLoopCounterChangedCheck" status="READY"
+         characteristic_id="[null]" default_characteristic_id="20"
+         remediation_function="[null]" default_remediation_function="LINEAR"
+         remediation_coeff="[null]" default_remediation_coeff="5d"
+         remediation_offset="[null]" default_remediation_offset="[null]" updated_at="2014-02-19"/>
+
+  <!-- Rule linked to one enable requirement on Linear with offset function (with a offset of 0h -> should be replaced by 0d), with different value of debt -> Update rule -->
+  <rules id="8" plugin_rule_key="ClassVariableVisibilityCheck" plugin_name="squid" name="ClassVariableVisibilityCheck" description="ClassVariableVisibilityCheck" status="READY"
+         characteristic_id="[null]" default_characteristic_id="20"
+         remediation_function="[null]" default_remediation_function="LINEAR"
+         remediation_coeff="[null]" default_remediation_coeff="5d"
+         remediation_offset="[null]" default_remediation_offset="[null" updated_at="2014-02-19"/>
+
+  <!-- Rule linked to one enable requirement on Linear function (with a coefficient of 0d -> should keep 0d), with different value of debt -> Update rule -->
+  <rules id="9" plugin_rule_key="SwitchLastCaseIsDefaultCheck" plugin_name="squid" name="SwitchLastCaseIsDefaultCheck" description="SwitchLastCaseIsDefaultCheck" status="READY"
+         characteristic_id="[null]" default_characteristic_id="20"
+         remediation_function="[null]" default_remediation_function="LINEAR"
+         remediation_coeff="[null]" default_remediation_coeff="5d"
+         remediation_offset="[null]" default_remediation_offset="[null]" updated_at="2014-02-19"/>
+
 </dataset>
index 83fc74c8813634f8af753034d70b79e8577cec66..b7557ad1c88d165a743773613ad1cf525f1d4bb7 100644 (file)
          remediation_coeff="20min" default_remediation_coeff="5d"
          remediation_offset="30h" default_remediation_offset="[null]" updated_at="2014-03-13 19:10:03.0"/>
 
+  <!-- Rule linked to one enable requirement on Constant per issue function (with a coefficient of 0d -> to be replaced by null), with different value of debt -> Update rule -->
+  <rules id="6" plugin_rule_key="HiddenFieldCheck" plugin_name="squid" name="HiddenFieldCheck" description="HiddenFieldCheck" status="READY"
+         characteristic_id="10" default_characteristic_id="20"
+         remediation_function="CONSTANT_ISSUE" default_remediation_function="LINEAR"
+         remediation_coeff="[null]" default_remediation_coeff="5d"
+         remediation_offset="15min" default_remediation_offset="[null]" updated_at="2014-03-13 19:10:03.0"/>
+
+  <!-- Rule linked to one enable requirement on Linear function (with a coeeficient of 0min -> to be replaced by null), with different value of debt -> Update rule -->
+  <rules id="7" plugin_rule_key="ForLoopCounterChangedCheck" plugin_name="squid" name="ForLoopCounterChangedCheck" description="ForLoopCounterChangedCheck" status="READY"
+         characteristic_id="10" default_characteristic_id="20"
+         remediation_function="LINEAR" default_remediation_function="LINEAR"
+         remediation_coeff="0min" default_remediation_coeff="5d"
+         remediation_offset="[null]" default_remediation_offset="[null]" updated_at="2014-03-13 19:10:03.0"/>
+
+  <!-- Rule linked to one enable requirement on Linear with offset function (with a offset of 0h -> should keep 0h), with different value of debt -> Update rule -->
+  <rules id="8" plugin_rule_key="ClassVariableVisibilityCheck" plugin_name="squid" name="ClassVariableVisibilityCheck" description="ClassVariableVisibilityCheck" status="READY"
+         characteristic_id="10" default_characteristic_id="20"
+         remediation_function="LINEAR_OFFSET" default_remediation_function="LINEAR"
+         remediation_coeff="5d" default_remediation_coeff="5d"
+         remediation_offset="0h" default_remediation_offset="[null" updated_at="2014-03-13 19:10:03.0"/>
+
+  <!-- Rule linked to one enable requirement on Linear function (with a coefficient of 0d -> should keep 0d), with different value of debt -> Update rule -->
+  <rules id="9" plugin_rule_key="SwitchLastCaseIsDefaultCheck" plugin_name="squid" name="SwitchLastCaseIsDefaultCheck" description="SwitchLastCaseIsDefaultCheck" status="READY"
+         characteristic_id="10" default_characteristic_id="20"
+         remediation_function="LINEAR" default_remediation_function="LINEAR"
+         remediation_coeff="0d" default_remediation_coeff="5d"
+         remediation_offset="[null]" default_remediation_offset="[null]" updated_at="2014-03-13 19:10:03.0"/>
+
 </dataset>
index 0eb2db8b27a6d521af73ef1e8bd0d21bdac7097f..9e665763b1a7da93cf28e1e8dd330a7ba71b8852 100644 (file)
@@ -2,7 +2,7 @@
 
   <!-- No requirement for rule 1 -->
 
-  <!-- Requirement of rule 2 -->
+  <!-- Requirements of rule 2 -->
   <characteristics id="1" parent_id="10" rule_id="2"
                    function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[false]"
                    created_at="2013-11-20" updated_at="[null]"/>
@@ -11,7 +11,7 @@
                    function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
                    created_at="2013-11-20" updated_at="2013-11-22"/>
 
-  <!-- Requirement of rule 3 -->
+  <!-- Requirements of rule 3 -->
   <characteristics id="3" parent_id="10" rule_id="3"
                    function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[false]"
                    created_at="2013-11-20" updated_at="[null]"/>
@@ -20,7 +20,7 @@
                    function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
                    created_at="2013-11-20" updated_at="2013-11-22"/>
 
-  <!-- Requirement of rule 4 -->
+  <!-- Requirements of rule 4 -->
   <characteristics id="5" parent_id="10" rule_id="4"
                    function_key="linear_offset" factor_value="5.0" factor_unit="mn" offset_value="9.8" offset_unit="h" enabled="[true]"
                    created_at="2013-11-20" updated_at="[null]"/>
@@ -29,7 +29,7 @@
                    function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
                    created_at="2013-11-20" updated_at="2013-11-22"/>
 
-  <!-- Requirement of rule 5 -->
+  <!-- Requirements of rule 5 -->
   <characteristics id="7" parent_id="10" rule_id="5"
                    function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[true]"
                    created_at="2013-11-20" updated_at="[null]"/>
                    function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
                    created_at="2013-11-20" updated_at="2013-11-22"/>
 
+  <!-- Requirements of rule 6 -->
+  <characteristics id="9" parent_id="10" rule_id="6"
+                   function_key="constant_issue" factor_value="0.0" factor_unit="d" offset_value="15.0" offset_unit="mn" enabled="[true]"
+                   created_at="2013-11-20" updated_at="[null]"/>
+
+  <!-- Requirements of rule 7 -->
+  <characteristics id="10" parent_id="10" rule_id="7"
+                   function_key="linear" factor_value="0.0" factor_unit="mn" offset_value="0.0" offset_unit="min" enabled="[true]"
+                   created_at="2013-11-20" updated_at="[null]"/>
+
+  <!-- Requirements of rule 8 -->
+  <characteristics id="11" parent_id="10" rule_id="8"
+                   function_key="linear_offset" factor_value="5.0" factor_unit="d" offset_value="0.0" offset_unit="h" enabled="[true]"
+                   created_at="2013-11-20" updated_at="[null]"/>
+
+  <!-- Requirements of rule 9 -->
+  <characteristics id="12" parent_id="10" rule_id="9"
+                   function_key="linear" factor_value="0.0" factor_unit="d" offset_value="0.0" offset_unit="h" enabled="[true]"
+                   created_at="2013-11-20" updated_at="[null]"/>
+
 </dataset>