]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6730 Move RangeDistrubtionBuilder to API and allow constructor to take bottom...
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 27 Aug 2015 14:05:18 +0000 (16:05 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 31 Aug 2015 07:52:02 +0000 (09:52 +0200)
server/sonar-server/src/main/java/org/sonar/server/computation/formula/DistributionFormula.java
server/sonar-server/src/main/java/org/sonar/server/computation/formula/RangeDistributionBuilder.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/formula/RangeDistributionBuilderTest.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/RangeDistributionBuilder.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/measures/RangeDistributionBuilder.java
sonar-plugin-api/src/test/java/org/sonar/api/ce/measure/RangeDistributionBuilderTest.java [new file with mode: 0644]

index cdeca70332635595cf385cc1d8f27fd1f0bc0c08..a2b3b4745b0e3befe1107fcf5c04c24508cc3f5f 100644 (file)
@@ -21,6 +21,7 @@
 package org.sonar.server.computation.formula;
 
 import com.google.common.base.Optional;
+import org.sonar.api.ce.measure.RangeDistributionBuilder;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.component.CrawlerDepthLimit;
 import org.sonar.server.computation.measure.Measure;
@@ -81,7 +82,7 @@ public class DistributionFormula implements Formula<DistributionFormula.Distribu
 
     public Optional<String> getValue() {
       if (initialized) {
-        return distribution.build();
+        return Optional.fromNullable(distribution.build());
       }
       return Optional.absent();
     }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/RangeDistributionBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/RangeDistributionBuilder.java
deleted file mode 100644 (file)
index a78418a..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * 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.computation.formula;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.Multiset;
-import com.google.common.collect.TreeMultiset;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import org.sonar.api.utils.KeyValueFormat;
-
-/**
- * Utility to build a distribution based on defined ranges
- * <p/>
- * <p>An example of usage : you wish to record the percentage of lines of code that belong to method
- * with pre-defined ranges of complexity.</p>
- *
- */
-public class RangeDistributionBuilder {
-
-  private Multiset<Number> distributionSet;
-  private boolean isEmpty = true;
-  private Number[] bottomLimits;
-  private boolean isValid = true;
-
-  /**
-   * Adds an existing Distribution to the current one.
-   * It will create the entries if they don't exist.
-   * Can be used to add the values of children resources for example
-   * <p/>
-   * Since 2.2, the distribution returned will be invalidated in case the
-   * measure given does not use the same bottom limits
-   *
-   * @param data the data to add to the current one
-   * @return the current object
-   */
-  public RangeDistributionBuilder add(String data) {
-    Map<Double, Double> map = KeyValueFormat.parse(data, KeyValueFormat.newDoubleConverter(), KeyValueFormat.newDoubleConverter());
-    Number[] limits = map.keySet().toArray(new Number[map.size()]);
-    if (bottomLimits == null) {
-      init(limits);
-
-    } else if (!areSameLimits(bottomLimits, map.keySet())) {
-      isValid = false;
-    }
-
-    if (isValid) {
-      for (Map.Entry<Double, Double> entry : map.entrySet()) {
-        addLimitCount(entry.getKey(), entry.getValue().intValue());
-      }
-    }
-    return this;
-  }
-
-  private void init(Number[] bottomLimits) {
-    this.bottomLimits = new Number[bottomLimits.length];
-    System.arraycopy(bottomLimits, 0, this.bottomLimits, 0, this.bottomLimits.length);
-    Arrays.sort(this.bottomLimits);
-    changeDoublesToInts();
-    distributionSet = TreeMultiset.create(NumberComparator.INSTANCE);
-  }
-
-  private void changeDoublesToInts() {
-    boolean onlyInts = true;
-    for (Number bottomLimit : bottomLimits) {
-      if (NumberComparator.INSTANCE.compare(bottomLimit.intValue(), bottomLimit.doubleValue()) != 0) {
-        onlyInts = false;
-      }
-    }
-    if (onlyInts) {
-      for (int i = 0; i < bottomLimits.length; i++) {
-        bottomLimits[i] = bottomLimits[i].intValue();
-      }
-    }
-  }
-
-  private static boolean areSameLimits(Number[] bottomLimits, Set<Double> limits) {
-    if (limits.size() == bottomLimits.length) {
-      for (Number l : bottomLimits) {
-        if (!limits.contains(l.doubleValue())) {
-          return false;
-        }
-      }
-      return true;
-    }
-    return false;
-  }
-
-  private RangeDistributionBuilder addLimitCount(Number limit, int count) {
-    for (Number bottomLimit : bottomLimits) {
-      if (NumberComparator.INSTANCE.compare(bottomLimit.doubleValue(), limit.doubleValue()) == 0) {
-        addValue(limit, count);
-        isEmpty = false;
-        return this;
-      }
-    }
-    isValid = false;
-    return this;
-  }
-
-  private void addValue(Number value, int count) {
-    for (int i = bottomLimits.length - 1; i >= 0; i--) {
-      if (greaterOrEqualsThan(value, bottomLimits[i])) {
-        this.distributionSet.add(bottomLimits[i], count);
-        return;
-      }
-    }
-  }
-
-  /**
-   * @return whether the current object is empty or not
-   */
-  public boolean isEmpty() {
-    return isEmpty;
-  }
-
-  /**
-   * Used to build a measure from the current object
-   *
-   * @return the built measure
-   */
-  public Optional<String> build() {
-    if (isValid) {
-      return Optional.of(KeyValueFormat.format(toMap()));
-    }
-    return Optional.absent();
-  }
-
-  private Map<Number, Integer> toMap() {
-    if (bottomLimits == null || bottomLimits.length == 0) {
-      return Collections.emptyMap();
-    }
-    Map<Number, Integer> map = new TreeMap<>();
-    for (int i = 0; i < bottomLimits.length; i++) {
-      Number value = bottomLimits[i];
-      map.put(value, distributionSet.count(value));
-    }
-    return map;
-  }
-
-  private static boolean greaterOrEqualsThan(Number n1, Number n2) {
-    return NumberComparator.INSTANCE.compare(n1, n2) >= 0;
-  }
-
-  private enum NumberComparator implements Comparator<Number> {
-    INSTANCE;
-
-    @Override
-    public int compare(Number n1, Number n2) {
-      return ((Double) n1.doubleValue()).compareTo(n2.doubleValue());
-    }
-  }
-
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/formula/RangeDistributionBuilderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/formula/RangeDistributionBuilderTest.java
deleted file mode 100644 (file)
index ea8dfbc..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.computation.formula;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class RangeDistributionBuilderTest {
-
-  @Test
-  public void build_integer_distribution() {
-    RangeDistributionBuilder builder = new RangeDistributionBuilder();
-    String data = builder
-      .add("0=0;2=2;4=1")
-      .add("0=1;2=2;4=2")
-      .build().get();
-
-    assertThat(data).isEqualTo("0=1;2=4;4=3");
-  }
-
-  @Test
-  public void build_double_distribution() {
-    RangeDistributionBuilder builder = new RangeDistributionBuilder();
-    String data = builder
-      .add("0.5=0;1.9=2;4.5=1")
-      .add("0.5=1;1.9=3;4.5=1")
-      .build().get();
-
-    assertThat(data).isEqualTo("0.5=1;1.9=5;4.5=2");
-  }
-
-  @Test
-  public void add_distribution_measure_with_identical_limits() {
-    RangeDistributionBuilder builder = new RangeDistributionBuilder();
-    String data = builder
-      .add("0=1;2=0")
-      .add("0=3;2=5")
-      .build().get();
-
-    assertThat(data).isEqualTo("0=4;2=5");
-  }
-
-  @Test
-  public void add_distribution_measure_with_different_int_limits() {
-    RangeDistributionBuilder builder = new RangeDistributionBuilder();
-
-    assertThat(builder
-      .add("0=1")
-      .add("0=3;2=5")
-      .build().isPresent()).isFalse();
-  }
-
-  @Test
-  public void add_distribution_measure_with_different_double_limits() {
-    RangeDistributionBuilder builder = new RangeDistributionBuilder();
-
-    assertThat(builder
-      .add("0.0=3;3.0=5")
-      .add("0.0=3;3.0=5;6.0=9")
-      .build().isPresent()).isFalse();
-  }
-
-  @Test
-  public void init_limits_at_the_first_add() {
-    RangeDistributionBuilder builder = new RangeDistributionBuilder();
-    String data = builder
-      .add("0.5=3;3.5=5;6.5=9")
-      .add("0.5=0;3.5=2;6.5=1")
-      .build().get();
-
-    assertThat(data).isEqualTo("0.5=3;3.5=7;6.5=10");
-  }
-
-  @Test
-  public void keep_int_ranges_when_merging_distributions() {
-    RangeDistributionBuilder builder = new RangeDistributionBuilder();
-    String data = builder
-      .add("0=3;3=5;6=9")
-      .add("0=0;3=2;6=1")
-      .build().get();
-
-    assertThat(data).isEqualTo("0=3;3=7;6=10");
-  }
-
-  @Test
-  public void return_empty_string_when_empty_data() {
-    RangeDistributionBuilder builder = new RangeDistributionBuilder();
-
-    assertThat(builder.isEmpty()).isTrue();
-    assertThat(builder.build().get()).isEmpty();
-  }
-
-  @Test
-  public void aggregate_empty_distribution() {
-    RangeDistributionBuilder builder = new RangeDistributionBuilder();
-    String distribution = builder.build().get();
-    assertThat(distribution).isEmpty();
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/RangeDistributionBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/RangeDistributionBuilder.java
new file mode 100644 (file)
index 0000000..0640846
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * 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.api.ce.measure;
+
+import com.google.common.collect.Multiset;
+import com.google.common.collect.TreeMultiset;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import javax.annotation.CheckForNull;
+import org.sonar.api.utils.KeyValueFormat;
+
+/**
+ * Utility to build a distribution based on defined ranges
+ * <p/>
+ * <p>An example of usage : you wish to record the percentage of lines of code that belong to method
+ * with pre-defined ranges of complexity.</p>
+ *
+ */
+public class RangeDistributionBuilder {
+
+  private Multiset<Number> distributionSet;
+  private boolean isEmpty = true;
+  private Number[] bottomLimits;
+  private boolean isValid = true;
+
+  public RangeDistributionBuilder() {
+    // Nothing to be done here, bottom limits will be automatically calculated when adding the first value
+  }
+
+  /**
+   * RangeDistributionBuilder for a defined range
+   * Each entry is initialized at zero
+   *
+   * @param bottomLimits the bottom limits of ranges to be used
+   */
+  public RangeDistributionBuilder(Number[] bottomLimits) {
+    init(bottomLimits);
+  }
+
+  /**
+   * Increments an entry by 1
+   *
+   * @param value the value to use to pick the entry to increment
+   */
+  public RangeDistributionBuilder add(Number value) {
+    return add(value, 1);
+  }
+
+  /**
+   * Increments an entry
+   *
+   * @param value the value to use to pick the entry to increment
+   * @param count the number by which to increment
+   */
+  public RangeDistributionBuilder add(Number value, int count) {
+    if (greaterOrEqualsThan(value, bottomLimits[0])) {
+      addValue(value, count);
+      isEmpty = false;
+    }
+    return this;
+  }
+
+  /**
+   * Adds an existing Distribution to the current one.
+   * It will create the entries if they don't exist.
+   * Can be used to add the values of children resources for example
+   * <p/>
+   * The returned distribution will be invalidated in case the given value does not use the same bottom limits
+   *
+   * @param data the data to add to the current one
+   */
+  public RangeDistributionBuilder add(String data) {
+    Map<Double, Double> map = KeyValueFormat.parse(data, KeyValueFormat.newDoubleConverter(), KeyValueFormat.newDoubleConverter());
+    Number[] limits = map.keySet().toArray(new Number[map.size()]);
+    if (bottomLimits == null) {
+      init(limits);
+
+    } else if (!areSameLimits(bottomLimits, map.keySet())) {
+      isValid = false;
+    }
+
+    if (isValid) {
+      for (Map.Entry<Double, Double> entry : map.entrySet()) {
+        addLimitCount(entry.getKey(), entry.getValue().intValue());
+      }
+    }
+    return this;
+  }
+
+  private void init(Number[] bottomLimits) {
+    this.bottomLimits = new Number[bottomLimits.length];
+    System.arraycopy(bottomLimits, 0, this.bottomLimits, 0, this.bottomLimits.length);
+    Arrays.sort(this.bottomLimits);
+    changeDoublesToInts();
+    distributionSet = TreeMultiset.create(NumberComparator.INSTANCE);
+  }
+
+  private void changeDoublesToInts() {
+    boolean onlyInts = true;
+    for (Number bottomLimit : bottomLimits) {
+      if (NumberComparator.INSTANCE.compare(bottomLimit.intValue(), bottomLimit.doubleValue()) != 0) {
+        onlyInts = false;
+      }
+    }
+    if (onlyInts) {
+      for (int i = 0; i < bottomLimits.length; i++) {
+        bottomLimits[i] = bottomLimits[i].intValue();
+      }
+    }
+  }
+
+  private static boolean areSameLimits(Number[] bottomLimits, Set<Double> limits) {
+    if (limits.size() == bottomLimits.length) {
+      for (Number l : bottomLimits) {
+        if (!limits.contains(l.doubleValue())) {
+          return false;
+        }
+      }
+      return true;
+    }
+    return false;
+  }
+
+  private RangeDistributionBuilder addLimitCount(Number limit, int count) {
+    for (Number bottomLimit : bottomLimits) {
+      if (NumberComparator.INSTANCE.compare(bottomLimit.doubleValue(), limit.doubleValue()) == 0) {
+        addValue(limit, count);
+        isEmpty = false;
+        return this;
+      }
+    }
+    isValid = false;
+    return this;
+  }
+
+  private void addValue(Number value, int count) {
+    for (int i = bottomLimits.length - 1; i >= 0; i--) {
+      if (greaterOrEqualsThan(value, bottomLimits[i])) {
+        this.distributionSet.add(bottomLimits[i], count);
+        return;
+      }
+    }
+  }
+
+  /**
+   * @return whether the current object is empty or not
+   */
+  public boolean isEmpty() {
+    return isEmpty;
+  }
+
+  /**
+   * Used to build a measure from the current object
+   *
+   * @return the built measure
+   */
+  @CheckForNull
+  public String build() {
+    if (isValid) {
+      return KeyValueFormat.format(toMap());
+    }
+    return null;
+  }
+
+  private Map<Number, Integer> toMap() {
+    if (bottomLimits == null || bottomLimits.length == 0) {
+      return Collections.emptyMap();
+    }
+    Map<Number, Integer> map = new TreeMap<>();
+    for (Number value : bottomLimits) {
+      map.put(value, distributionSet.count(value));
+    }
+    return map;
+  }
+
+  private static boolean greaterOrEqualsThan(Number n1, Number n2) {
+    return NumberComparator.INSTANCE.compare(n1, n2) >= 0;
+  }
+
+  private enum NumberComparator implements Comparator<Number> {
+    INSTANCE;
+
+    @Override
+    public int compare(Number n1, Number n2) {
+      return ((Double) n1.doubleValue()).compareTo(n2.doubleValue());
+    }
+  }
+
+}
index 26d82c1fdcfb6e50073bb43dd28f77af9cf11475..6e2fd0049b709888685e087f34ab189fb47b895e 100644 (file)
  */
 package org.sonar.api.measures;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nullable;
 import org.apache.commons.collections.SortedBag;
 import org.apache.commons.collections.Transformer;
 import org.apache.commons.collections.bag.TransformedSortedBag;
@@ -27,13 +32,6 @@ import org.apache.commons.lang.NumberUtils;
 import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.api.utils.SonarException;
 
-import javax.annotation.Nullable;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * Utility to build a distribution based on defined ranges
  * <p/>
@@ -41,7 +39,9 @@ import java.util.Set;
  * with pre-defined ranges of complexity.</p>
  *
  * @since 1.10
+ * @deprecated since 5.2 use {@link org.sonar.api.ce.measure.RangeDistributionBuilder instead}
  */
+@Deprecated
 public class RangeDistributionBuilder implements MeasureBuilder {
 
   private Metric<String> metric;
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/ce/measure/RangeDistributionBuilderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/ce/measure/RangeDistributionBuilderTest.java
new file mode 100644 (file)
index 0000000..f59ee7b
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * 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.api.ce.measure;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RangeDistributionBuilderTest {
+
+  @Test
+  public void work_on_an_limits_array_copy() {
+    Integer[] limits = new Integer[] {4, 2, 0};
+    RangeDistributionBuilder builder = new RangeDistributionBuilder(limits);
+    builder.add(3.2).add(2.0).add(6.2).build();
+
+    assertThat(limits[0]).isEqualTo(4);
+    assertThat(limits[1]).isEqualTo(2);
+    assertThat(limits[2]).isEqualTo(0);
+  }
+
+  @Test
+  public void build_integer_distribution() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder(new Integer[] {0, 2, 4});
+    String data = builder
+      .add(3.2)
+      .add(2.0)
+      .add(6.2)
+      .build();
+
+    assertThat(data).isEqualTo("0=0;2=2;4=1");
+  }
+
+  @Test
+  public void build_double_distribution() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder(new Double[] {0.0, 2.0, 4.0});
+    String data = builder
+      .add(3.2)
+      .add(2.0)
+      .add(6.2)
+      .build();
+
+    assertThat(data).isEqualTo("0=0;2=2;4=1");
+  }
+
+  @Test
+  public void value_lesser_than_minimum_is_ignored() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder(new Integer[] {0, 2, 4});
+    String data = builder
+      .add(3.2)
+      .add(2.0)
+      .add(-3.0)
+      .build();
+
+    assertThat(data).isEqualTo("0=0;2=2;4=0");
+  }
+
+  @Test
+  public void add_existing_integer_distribution() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder();
+    String data = builder
+      .add("0=0;2=2;4=1")
+      .add("0=1;2=2;4=2")
+      .build();
+
+    assertThat(data).isEqualTo("0=1;2=4;4=3");
+  }
+
+  @Test
+  public void add_existing_double_distribution() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder();
+    String data = builder
+      .add("0.5=0;1.9=2;4.5=1")
+      .add("0.5=1;1.9=3;4.5=1")
+      .build();
+
+    assertThat(data).isEqualTo("0.5=1;1.9=5;4.5=2");
+  }
+
+  @Test
+  public void add_distribution_with_identical_limits() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder();
+    String data = builder
+      .add("0=1;2=0")
+      .add("0=3;2=5")
+      .build();
+
+    assertThat(data).isEqualTo("0=4;2=5");
+  }
+
+  @Test
+  public void add_distribution_with_different_int_limits() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder();
+
+    assertThat(builder
+      .add("0=1")
+      .add("0=3;2=5")
+      .build()).isNull();
+  }
+
+  @Test
+  public void add_distribution_with_different_double_limits() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder();
+
+    assertThat(builder
+      .add("0.0=3;3.0=5")
+      .add("0.0=3;3.0=5;6.0=9")
+      .build()).isNull();
+  }
+
+  @Test
+  public void init_limits_at_the_first_add() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder();
+    String data = builder
+      .add("0.5=3;3.5=5;6.5=9")
+      .add("0.5=0;3.5=2;6.5=1")
+      .build();
+
+    assertThat(data).isEqualTo("0.5=3;3.5=7;6.5=10");
+  }
+
+  @Test
+  public void keep_int_ranges_when_merging_distributions() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder();
+    String data = builder
+      .add("0=3;3=5;6=9")
+      .add("0=0;3=2;6=1")
+      .build();
+
+    assertThat(data).isEqualTo("0=3;3=7;6=10");
+  }
+
+
+  @Test
+  public void is_empty_is_true_when_no_data() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder();
+
+    assertThat(builder.isEmpty()).isTrue();
+  }
+
+  @Test
+  public void is_empty_is_true_when_no_data_on_distribution_with_limits() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder(new Integer[] {4, 2, 0});
+
+    assertThat(builder.isEmpty()).isTrue();
+  }
+
+  @Test
+  public void aggregate_empty_distribution() {
+    RangeDistributionBuilder builder = new RangeDistributionBuilder();
+    String distribution = builder.build();
+    assertThat(distribution).isEmpty();
+  }
+
+}