Repository: assertj/assertj

Problem statement:
Assert "isCloseTo(double expected, Percentage p)" doesn't work for negative expected values
I have two tests whether an actual value is within a given percentage of the expected value.

The test for a positive expected value works without problems.
The test for a negative expected value throws `IllegalArgumentException`

My test methods are:

```
@Test
public void checkPositiveNumber() {
    assertThat(11d).isCloseTo(10d, withinPercentage(10));
}

@Test
public void checkNegativeNumber() {
    assertThat(-11d).isCloseTo(-10d, withinPercentage(10));
}
```

I've tested only for doubles but I suspect the same problem exists for all numeric types.



Hints:
True :crying_cat_face: 
We screwed up, the computed offset is negative, we should have used an absolute values.

Thanks for reporting this. will be fixed in  2.4.0/3.4.0


Patch:
diff --git a/src/main/java/org/assertj/core/internal/Longs.java b/src/main/java/org/assertj/core/internal/Longs.java
--- a/src/main/java/org/assertj/core/internal/Longs.java
+++ b/src/main/java/org/assertj/core/internal/Longs.java
@@ -73,7 +73,7 @@ public void assertIsCloseToPercentage(AssertionInfo info, Long actual, Long othe
     checkPercentageIsNotNull(percentage);
     checkNumberIsNotNull(other);
 
-    Offset<Double> calculatedOffset = offset(percentage.value * other / 100d);
+    Offset<Double> calculatedOffset = offset(abs(percentage.value * other / 100d));
 
     Long absDiff = abs(other - actual);
     if (absDiff > calculatedOffset.value)
diff --git a/src/main/java/org/assertj/core/internal/Numbers.java b/src/main/java/org/assertj/core/internal/Numbers.java
--- a/src/main/java/org/assertj/core/internal/Numbers.java
+++ b/src/main/java/org/assertj/core/internal/Numbers.java
@@ -184,7 +184,7 @@ protected double absDiff(NUMBER actual, NUMBER expected) {
   }
 
   private Offset<Double> computeOffset(NUMBER referenceValue, Percentage percentage) {
-    return offset(percentage.value * referenceValue.doubleValue() / 100d);
+    return offset(abs(percentage.value * referenceValue.doubleValue() / 100d));
   }
 
 }
\ No newline at end of file
diff --git a/src/main/java/org/assertj/core/internal/Shorts.java b/src/main/java/org/assertj/core/internal/Shorts.java
--- a/src/main/java/org/assertj/core/internal/Shorts.java
+++ b/src/main/java/org/assertj/core/internal/Shorts.java
@@ -73,7 +73,7 @@ public void assertIsCloseToPercentage(AssertionInfo info, Short actual, Short ot
     assertNotNull(info, actual);
     checkPercentageIsNotNull(percentage);
     checkNumberIsNotNull(other);
-    Offset<Double> calculatedOffset = offset(percentage.value * other / 100d);
+    Offset<Double> calculatedOffset = offset(abs(percentage.value * other / 100d));
     short absDiff = (short) abs(other - actual);
     if (absDiff > calculatedOffset.value)
       throw failures.failure(info, shouldBeEqualWithinPercentage(actual, other, percentage, absDiff));


Test patch:
diff --git a/src/test/java/org/assertj/core/internal/bigdecimals/BigDecimals_assertIsCloseToPercentage_Test.java b/src/test/java/org/assertj/core/internal/bigdecimals/BigDecimals_assertIsCloseToPercentage_Test.java
--- a/src/test/java/org/assertj/core/internal/bigdecimals/BigDecimals_assertIsCloseToPercentage_Test.java
+++ b/src/test/java/org/assertj/core/internal/bigdecimals/BigDecimals_assertIsCloseToPercentage_Test.java
@@ -28,10 +28,13 @@
 import org.assertj.core.api.AssertionInfo;
 import org.assertj.core.internal.BigDecimalsBaseTest;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class BigDecimals_assertIsCloseToPercentage_Test extends BigDecimalsBaseTest {
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
 
-  private static final BigDecimal TWO = new BigDecimal(2);
+@RunWith(DataProviderRunner.class)
+public class BigDecimals_assertIsCloseToPercentage_Test extends BigDecimalsBaseTest {
 
   @Test
   public void should_fail_if_actual_is_null() {
@@ -59,16 +62,32 @@ public void should_fail_if_percentage_is_greater_than_one_hundred() {
     bigDecimals.assertIsCloseToPercentage(someInfo(), ONE, ZERO, withPercentage(101));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_less_than_given_percentage() {
-    bigDecimals.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(1));
-    bigDecimals.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(100));
+  @DataProvider({
+    "1, 1, 1",
+    "1, 2, 100",
+    "-1, -1, 1",
+    "-1, -2, 100"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_less_than_given_percentage(BigDecimal actual, BigDecimal other, Integer percentage) {
+    bigDecimals.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_equal_to_given_percentage() {
-    bigDecimals.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(0));
-    bigDecimals.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(50));
+  @DataProvider({
+    "1, 1, 0",
+    "2, 1, 100",
+    "1, 2, 50",
+    "-1, -1, 0",
+    "-2, -1, 100",
+    "-1, -2, 50"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_equal_to_given_percentage(BigDecimal actual, BigDecimal other, Integer percentage) {
+    bigDecimals.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
   @Test
diff --git a/src/test/java/org/assertj/core/internal/bytes/Bytes_assertIsCloseToPercentage_Test.java b/src/test/java/org/assertj/core/internal/bytes/Bytes_assertIsCloseToPercentage_Test.java
--- a/src/test/java/org/assertj/core/internal/bytes/Bytes_assertIsCloseToPercentage_Test.java
+++ b/src/test/java/org/assertj/core/internal/bytes/Bytes_assertIsCloseToPercentage_Test.java
@@ -23,12 +23,16 @@
 import org.assertj.core.api.AssertionInfo;
 import org.assertj.core.internal.BytesBaseTest;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+
+@RunWith(DataProviderRunner.class)
 public class Bytes_assertIsCloseToPercentage_Test extends BytesBaseTest {
 
   private static final Byte ZERO = 0;
   private static final Byte ONE = 1;
-  private static final Byte TWO = 2;
   private static final Byte TEN = 10;
 
   @Test
@@ -57,16 +61,32 @@ public void should_fail_if_percentage_is_greater_than_one_hundred() {
     bytes.assertIsCloseToPercentage(someInfo(), ONE, ZERO, withPercentage(101));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_less_than_given_percentage() {
-    bytes.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(1));
-    bytes.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(100));
+  @DataProvider({
+    "1, 1, 1",
+    "1, 2, 100",
+    "-1, -1, 1",
+    "-1, -2, 100"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_less_than_given_percentage(Byte actual, Byte other, Byte percentage) {
+    bytes.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_equal_to_given_percentage() {
-    bytes.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(0));
-    bytes.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(50));
+  @DataProvider({
+    "1, 1, 0",
+    "2, 1, 100",
+    "1, 2, 50",
+    "-1, -1, 0",
+    "-2, -1, 100",
+    "-1, -2, 50"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_equal_to_given_percentage(Byte actual, Byte other, Byte percentage) {
+    bytes.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
   @Test
diff --git a/src/test/java/org/assertj/core/internal/doubles/Doubles_assertIsCloseToPercentage_Test.java b/src/test/java/org/assertj/core/internal/doubles/Doubles_assertIsCloseToPercentage_Test.java
--- a/src/test/java/org/assertj/core/internal/doubles/Doubles_assertIsCloseToPercentage_Test.java
+++ b/src/test/java/org/assertj/core/internal/doubles/Doubles_assertIsCloseToPercentage_Test.java
@@ -23,12 +23,16 @@
 import org.assertj.core.api.AssertionInfo;
 import org.assertj.core.internal.DoublesBaseTest;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+
+@RunWith(DataProviderRunner.class)
 public class Doubles_assertIsCloseToPercentage_Test extends DoublesBaseTest {
 
   private static final Double ZERO = 0d;
   private static final Double ONE = 1d;
-  private static final Double TWO = 2d;
   private static final Double TEN = 10d;
 
   @Test
@@ -57,16 +61,32 @@ public void should_fail_if_percentage_is_greater_than_one_hundred() {
     doubles.assertIsCloseToPercentage(someInfo(), ONE, ZERO, withPercentage(101.0));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_less_than_given_percentage() {
-    doubles.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(0.1));
-    doubles.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(100.0));
+  @DataProvider({
+    "1, 1, 1",
+    "1, 2, 100",
+    "-1, -1, 1",
+    "-1, -2, 100"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_less_than_given_percentage(Double actual, Double other, Double percentage) {
+    doubles.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_equal_to_given_percentage() {
-    doubles.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(ZERO));
-    doubles.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(50.0));
+  @DataProvider({
+    "1, 1, 0",
+    "2, 1, 100",
+    "1, 2, 50",
+    "-1, -1, 0",
+    "-2, -1, 100",
+    "-1, -2, 50"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_equal_to_given_percentage(Double actual, Double other, Double percentage) {
+    doubles.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
   @Test
diff --git a/src/test/java/org/assertj/core/internal/floats/Floats_assertIsCloseToPercentage_Test.java b/src/test/java/org/assertj/core/internal/floats/Floats_assertIsCloseToPercentage_Test.java
--- a/src/test/java/org/assertj/core/internal/floats/Floats_assertIsCloseToPercentage_Test.java
+++ b/src/test/java/org/assertj/core/internal/floats/Floats_assertIsCloseToPercentage_Test.java
@@ -23,12 +23,16 @@
 import org.assertj.core.api.AssertionInfo;
 import org.assertj.core.internal.FloatsBaseTest;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+
+@RunWith(DataProviderRunner.class)
 public class Floats_assertIsCloseToPercentage_Test extends FloatsBaseTest {
 
   private static final Float ZERO = 0f;
   private static final Float ONE = 1f;
-  private static final Float TWO = 2f;
   private static final Float TEN = 10f;
 
   @Test
@@ -57,16 +61,32 @@ public void should_fail_if_percentage_is_greater_than_one_hundred() {
     floats.assertIsCloseToPercentage(someInfo(), ONE, ZERO, withPercentage(101.0f));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_less_than_given_percentage() {
-    floats.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(0.1f));
-    floats.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(100.0f));
+  @DataProvider({
+    "1, 1, 1",
+    "1, 2, 100",
+    "-1, -1, 1",
+    "-1, -2, 100"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_less_than_given_percentage(Float actual, Float other, Float percentage) {
+    floats.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_equal_to_given_percentage() {
-    floats.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(ZERO));
-    floats.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(50.0f));
+  @DataProvider({
+    "1, 1, 0",
+    "2, 1, 100",
+    "1, 2, 50",
+    "-1, -1, 0",
+    "-2, -1, 100",
+    "-1, -2, 50"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_equal_to_given_percentage(Float actual, Float other, Float percentage) {
+    floats.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
   @Test
diff --git a/src/test/java/org/assertj/core/internal/integers/Integers_assertIsCloseToPercentage_Test.java b/src/test/java/org/assertj/core/internal/integers/Integers_assertIsCloseToPercentage_Test.java
--- a/src/test/java/org/assertj/core/internal/integers/Integers_assertIsCloseToPercentage_Test.java
+++ b/src/test/java/org/assertj/core/internal/integers/Integers_assertIsCloseToPercentage_Test.java
@@ -23,12 +23,16 @@
 import org.assertj.core.api.AssertionInfo;
 import org.assertj.core.internal.IntegersBaseTest;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+
+@RunWith(DataProviderRunner.class)
 public class Integers_assertIsCloseToPercentage_Test extends IntegersBaseTest {
 
   private static final Integer ZERO = 0;
   private static final Integer ONE = 1;
-  private static final Integer TWO = 2;
   private static final Integer TEN = 10;
 
   @Test
@@ -57,16 +61,32 @@ public void should_fail_if_percentage_is_greater_than_one_hundred() {
     integers.assertIsCloseToPercentage(someInfo(), ONE, ZERO, withPercentage(101));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_less_than_given_percentage() {
-    integers.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(1));
-    integers.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(100));
+  @DataProvider({
+    "1, 1, 1",
+    "1, 2, 100",
+    "-1, -1, 1",
+    "-1, -2, 100"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_less_than_given_percentage(Integer actual, Integer other, Integer percentage) {
+    integers.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_equal_to_given_percentage() {
-    integers.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(ZERO));
-    integers.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(50));
+  @DataProvider({
+    "1, 1, 0",
+    "2, 1, 100",
+    "1, 2, 50",
+    "-1, -1, 0",
+    "-2, -1, 100",
+    "-1, -2, 50"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_equal_to_given_percentage(Integer actual, Integer other, Integer percentage) {
+    integers.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
   @Test
diff --git a/src/test/java/org/assertj/core/internal/longs/Longs_assertIsCloseToPercentage_Test.java b/src/test/java/org/assertj/core/internal/longs/Longs_assertIsCloseToPercentage_Test.java
--- a/src/test/java/org/assertj/core/internal/longs/Longs_assertIsCloseToPercentage_Test.java
+++ b/src/test/java/org/assertj/core/internal/longs/Longs_assertIsCloseToPercentage_Test.java
@@ -15,6 +15,10 @@
 import org.assertj.core.api.AssertionInfo;
 import org.assertj.core.internal.LongsBaseTest;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
 
 import static org.assertj.core.api.Assertions.withinPercentage;
 import static org.assertj.core.data.Percentage.withPercentage;
@@ -24,11 +28,11 @@
 import static org.assertj.core.util.FailureMessages.actualIsNull;
 import static org.mockito.Mockito.verify;
 
+@RunWith(DataProviderRunner.class)
 public class Longs_assertIsCloseToPercentage_Test extends LongsBaseTest {
 
     private static final Long ZERO = 0L;
     private static final Long ONE = 1L;
-    private static final Long TWO = 2L;
     private static final Long TEN = 10L;
 
     @Test
@@ -57,16 +61,32 @@ public void should_fail_if_percentage_is_greater_than_one_hundred() {
         longs.assertIsCloseToPercentage(someInfo(), ONE, ZERO, withPercentage(101L));
     }
 
+    // @format:off
     @Test
-    public void should_pass_if_difference_is_less_than_given_percentage() {
-        longs.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(1L));
-        longs.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(100L));
+    @DataProvider({
+      "1, 1, 1",
+      "1, 2, 100",
+      "-1, -1, 1",
+      "-1, -2, 100"
+    })
+    // @format:on
+    public void should_pass_if_difference_is_less_than_given_percentage(Long actual, Long other, Long percentage) {
+      longs.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
     }
 
+    // @format:off
     @Test
-    public void should_pass_if_difference_is_equal_to_given_percentage() {
-        longs.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(ZERO));
-        longs.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage(50L));
+    @DataProvider({
+      "1, 1, 0",
+      "2, 1, 100",
+      "1, 2, 50",
+      "-1, -1, 0",
+      "-2, -1, 100",
+      "-1, -2, 50"
+    })
+    // @format:on
+    public void should_pass_if_difference_is_equal_to_given_percentage(Long actual, Long other, Long percentage) {
+      longs.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
     }
 
     @Test
diff --git a/src/test/java/org/assertj/core/internal/shorts/Shorts_assertIsCloseToPercentage_Test.java b/src/test/java/org/assertj/core/internal/shorts/Shorts_assertIsCloseToPercentage_Test.java
--- a/src/test/java/org/assertj/core/internal/shorts/Shorts_assertIsCloseToPercentage_Test.java
+++ b/src/test/java/org/assertj/core/internal/shorts/Shorts_assertIsCloseToPercentage_Test.java
@@ -23,12 +23,16 @@
 import org.assertj.core.api.AssertionInfo;
 import org.assertj.core.internal.ShortsBaseTest;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+
+@RunWith(DataProviderRunner.class)
 public class Shorts_assertIsCloseToPercentage_Test extends ShortsBaseTest {
 
   private static final Short ZERO = (short) 0;
   private static final Short ONE = (short) 1;
-  private static final Short TWO = (short) 2;
   private static final Short TEN = (short) 10;
 
   @Test
@@ -57,16 +61,32 @@ public void should_fail_if_percentage_is_greater_than_one_hundred() {
     shorts.assertIsCloseToPercentage(someInfo(), ONE, ZERO, withPercentage((short) 101));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_less_than_given_percentage() {
-    shorts.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage((short) 1));
-    shorts.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage((short) 100));
+  @DataProvider({
+    "1, 1, 1",
+    "1, 2, 100",
+    "-1, -1, 1",
+    "-1, -2, 100"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_less_than_given_percentage(Short actual, Short other, Short percentage) {
+    shorts.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
+  // @format:off
   @Test
-  public void should_pass_if_difference_is_equal_to_given_percentage() {
-    shorts.assertIsCloseToPercentage(someInfo(), ONE, ONE, withPercentage(ZERO));
-    shorts.assertIsCloseToPercentage(someInfo(), ONE, TWO, withPercentage((short) 50));
+  @DataProvider({
+    "1, 1, 0",
+    "2, 1, 100",
+    "1, 2, 50",
+    "-1, -1, 0",
+    "-2, -1, 100",
+    "-1, -2, 50"
+  })
+  // @format:on
+  public void should_pass_if_difference_is_equal_to_given_percentage(Short actual, Short other, Short percentage) {
+    shorts.assertIsCloseToPercentage(someInfo(), actual, other, withPercentage(percentage));
   }
 
   @Test