This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-numbers.git

commit 778f94b5a4c93d378d549ed0dfd0a8147264f042
Author: aherbert <[email protected]>
AuthorDate: Tue Dec 10 12:55:49 2019 +0000

    Improved CReferenceTest to allow processing of flagged reference data.
---
 .../commons/numbers/complex/CReferenceTest.java    | 122 +++++++++++++++------
 .../apache/commons/numbers/complex/TestUtils.java  |  83 ++++++++++----
 2 files changed, 150 insertions(+), 55 deletions(-)

diff --git 
a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java
 
b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java
index 57822b8..d122235 100644
--- 
a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java
+++ 
b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.commons.numbers.complex;
 
+import org.apache.commons.numbers.complex.TestUtils.TestDataFlagOption;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -37,14 +38,33 @@ import java.util.function.UnaryOperator;
  */
 public class CReferenceTest {
     /**
-     * The maximum units of least precision allowed between values.
+     * The maximum units of least precision (ULPs) allowed between values.
+     * This is a global setting used to override individual test settings for 
ULPs as follows:
      *
-     * <p>In the normal use case this is set to zero and ignored.
-     * It can be set to a non-zero value and it overrides the ulps values used 
in
-     * each test if greater in magnitude. This can be used to output a report
-     * of the ULPS between Complex and the reference data.
+     * <ul>
+     * <li>In the normal use case this is set to zero and ignored.
+     * <li>If the sign matches the setting of the test then the larger 
magnitude is used.
+     * <li>If the global setting is negative and the test setting is positive 
then it overrides
+     * the individual test setting for reporting purposes.
+     * <li>If the global setting is positive and the test setting is negative 
then the test
+     * setting takes precedence.
+     * </ul>
+     *
+     * <p>During testing if the difference between an expected and actual 
result is greater in
+     * magnitude than the current ULPS then this is considered an error. If 
the maximum ULPs is
+     * positive then an assertion error is raised. If negative then the error 
is printed to
+     * System out. This allows reporting of large deviations between the 
library and the
+     * reference data.
+     *
+     * <p>In a standard use-case all tests will have a configured positive 
maximum ULPs to
+     * pass the current test data. The global setting can be set to a negative 
value to allow
+     * reporting of errors larger in magnitude to the console.
+     *
+     * <p>Setting the global maximum ULPs to negative has the second effect of 
loading all
+     * data that has been flagged in data files. Otherwise this data is 
ignored by testing and
+     * printed to System out.
      */
-    private static final long MAX_ULPS = 0;
+    private static long globalMaxUlps = 0;
 
     /**
      * Assert the two numbers are equal within the provided units of least 
precision.
@@ -81,16 +101,17 @@ public class CReferenceTest {
      * {@code maxUlps - 1}.
      *
      * @param c Input number.
+     * @param name the operation name
      * @param operation the operation
      * @param expected Expected result.
      * @param maxUlps the maximum units of least precision between the two 
values
      */
     private static void assertComplex(Complex c,
-            UnaryOperator<Complex> operation,
+            String name, UnaryOperator<Complex> operation,
             Complex expected, long maxUlps) {
         final Complex z = operation.apply(c);
-        assertEquals(() -> c + ": real", expected.real(), z.real(), maxUlps);
-        assertEquals(() -> c + ": imaginary", expected.imag(), z.imag(), 
maxUlps);
+        assertEquals(() -> c + "." + name + "(): real", expected.real(), 
z.real(), maxUlps);
+        assertEquals(() -> c + "." + name + "(): imaginary", expected.imag(), 
z.imag(), maxUlps);
     }
 
     /**
@@ -102,51 +123,68 @@ public class CReferenceTest {
      *
      * @param c1 First number.
      * @param c2 Second number.
+     * @param name the operation name
      * @param operation the operation
      * @param expected Expected real part.
      * @param maxUlps the maximum units of least precision between the two 
values
      */
     private static void assertComplex(Complex c1, Complex c2,
-            BiFunction<Complex, Complex, Complex> operation,
+            String name, BiFunction<Complex, Complex, Complex> operation,
             Complex expected, long maxUlps) {
         final Complex z = operation.apply(c1, c2);
-        assertEquals(() -> c1 + " op " + c2 + ": real", expected.real(), 
z.real(), maxUlps);
-        assertEquals(() -> c1 + " op " + c2 + ": imaginary", expected.imag(), 
z.imag(), maxUlps);
+        assertEquals(() -> c1 + "." + name + c2 + ": real", expected.real(), 
z.real(), maxUlps);
+        assertEquals(() -> c1 + "." + name + c2 + ": imaginary", 
expected.imag(), z.imag(), maxUlps);
     }
 
     /**
      * Assert the operation using the data loaded from test resources.
      *
-     * @param testData Test data resource name.
+     * @param name the operation name
      * @param operation the operation
      * @param maxUlps the maximum units of least precision between the two 
values
      */
-    private static void assertOperation(String testData,
+    private static void assertOperation(String name,
             UnaryOperator<Complex> operation, long maxUlps) {
-        final List<Complex[]> data = TestUtils.loadTestData(testData);
+        final List<Complex[]> data = loadTestData(name);
         final long ulps = getTestUlps(maxUlps);
         for (final Complex[] pair : data) {
-            assertComplex(pair[0], operation, pair[1], ulps);
+            assertComplex(pair[0], name, operation, pair[1], ulps);
         }
     }
 
     /**
      * Assert the operation using the data loaded from test resources.
      *
-     * @param testData Test data resource name.
+     * @param name the operation name
      * @param operation the operation
      * @param maxUlps the maximum units of least precision between the two 
values
      */
-    private static void assertBiOperation(String testData,
+    private static void assertBiOperation(String name,
             BiFunction<Complex, Complex, Complex> operation, long maxUlps) {
-        final List<Complex[]> data = TestUtils.loadTestData(testData);
+        final List<Complex[]> data = loadTestData(name);
         final long ulps = getTestUlps(maxUlps);
         for (final Complex[] triple : data) {
-            assertComplex(triple[0], triple[1], operation, triple[2], ulps);
+            assertComplex(triple[0], triple[1], name, operation, triple[2], 
ulps);
         }
     }
 
     /**
+     * Assert the operation using the data loaded from test resources.
+     *
+     * @param testData Test data resource name.
+     * @return the list
+     */
+    private static List<Complex[]> loadTestData(String name) {
+        final String testData = "data/" + name + ".txt";
+        final TestDataFlagOption option = globalMaxUlps < 1 ?
+            TestDataFlagOption.LOAD : TestDataFlagOption.IGNORE;
+        return TestUtils.loadTestData(testData, option,
+            // CHECKSTYLE: stop Regex
+            s -> System.out.println(name + " IGNORED: " + s));
+            // CHECKSTYLE: resume Regex
+    }
+
+    /**
      * Gets the test ulps. This uses the input value of the global setting if 
that is greater
      * in magnitude.
      *
@@ -154,78 +192,90 @@ public class CReferenceTest {
      * @return the test ulps
      */
     private static long getTestUlps(long ulps) {
-        final long max = Math.max(Math.abs(ulps), Math.abs(MAX_ULPS));
-        // If either are negative then choose negative for debugging output
-        return (ulps | MAX_ULPS) < 0 ? -max : max;
+        // If sign matches use the larger magnitude.
+        // xor the sign bytes will be negative if the sign does not match
+        if ((globalMaxUlps ^ ulps) >= 0) {
+            final long max = Math.max(Math.abs(globalMaxUlps), Math.abs(ulps));
+            // restore sign
+            return ulps < 0 ? -max : max;
+        }
+        // If the global setting is negative and the test setting is positive 
then it overrides
+        // the individual test setting for reporting purposes.
+        if (globalMaxUlps < 0) {
+            return globalMaxUlps;
+        }
+        // If the global setting is positive and the test setting is negative 
then the test
+        // setting takes precedence.
+        return ulps;
     }
 
     @Test
     public void testAcos() {
-        assertOperation("data/acos.txt", Complex::acos, 36);
+        assertOperation("acos", Complex::acos, 36);
     }
 
     @Test
     public void testAcosh() {
-        assertOperation("data/acosh.txt", Complex::acosh, 36);
+        assertOperation("acosh", Complex::acosh, 36);
     }
 
     @Test
     public void testAsinh() {
         // Odd function: negative real cases defined by positive real cases
-        assertOperation("data/asinh.txt", Complex::asinh, 2);
+        assertOperation("asinh", Complex::asinh, 2);
     }
 
     @Test
     public void testAtanh() {
         // Odd function: negative real cases defined by positive real cases
-        assertOperation("data/atanh.txt", Complex::atanh, 26);
+        assertOperation("atanh", Complex::atanh, 26);
     }
 
     @Test
     public void testCosh() {
         // Even function: negative real cases defined by positive real cases
-        assertOperation("data/cosh.txt", Complex::cosh, 2);
+        assertOperation("cosh", Complex::cosh, 2);
     }
 
     @Test
     public void testSinh() {
         // Odd function: negative real cases defined by positive real cases
-        assertOperation("data/sinh.txt", Complex::sinh, 2);
+        assertOperation("sinh", Complex::sinh, 2);
     }
 
     @Test
     public void testTanh() {
         // Odd function: negative real cases defined by positive real cases
-        assertOperation("data/tanh.txt", Complex::tanh, 34);
+        assertOperation("tanh", Complex::tanh, 34);
     }
 
     @Test
     public void testExp() {
-        assertOperation("data/exp.txt", Complex::exp, 2);
+        assertOperation("exp", Complex::exp, 2);
     }
 
     @Test
     public void testLog() {
-        assertOperation("data/log.txt", Complex::log, 3);
+        assertOperation("log", Complex::log, 3);
     }
 
     @Test
     public void testSqrt() {
-        assertOperation("data/sqrt.txt", Complex::sqrt, 1);
+        assertOperation("sqrt", Complex::sqrt, 1);
     }
 
     @Test
     public void testMultiply() {
-        assertBiOperation("data/multiply.txt", Complex::multiply, 0);
+        assertBiOperation("multiply", Complex::multiply, 0);
     }
 
     @Test
     public void testDivide() {
-        assertBiOperation("data/divide.txt", Complex::divide, 0);
+        assertBiOperation("divide", Complex::divide, 0);
     }
 
     @Test
     public void testPowComplex() {
-        assertBiOperation("data/pow.txt", Complex::pow, 17);
+        assertBiOperation("pow", Complex::pow, 17);
     }
 }
diff --git 
a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java
 
b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java
index c1cc4a6..eb85e68 100644
--- 
a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java
+++ 
b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java
@@ -26,6 +26,7 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 
 import org.apache.commons.numbers.core.Precision;
 
@@ -35,6 +36,18 @@ import org.junit.jupiter.api.Assertions;
  * Test utilities. TODO: Cleanup (remove unused and obsolete methods).
  */
 public final class TestUtils {
+
+    /**
+     * The option for how to process test data lines flagged (prefixed)
+     * with the {@code ;} character
+     */
+    public enum TestDataFlagOption {
+        /** Ignore the line. */
+        IGNORE,
+        /** Load the data by stripping the {@code ;} character. */
+        LOAD
+    }
+
     /**
      * Collection of static methods used in math unit tests.
      */
@@ -288,42 +301,40 @@ public final class TestUtils {
     /**
      * Load test data from resources.
      *
-     * <p>This method can be used to load input complex numbers and the 
expected
-     * result after applying a function.
+     * <p>This method can be used to load input complex numbers and the 
expected result
+     * after applying a function.
      *
-     * <p>Data is assumed to be a resource available to the class loader. The 
data
-     * should be space delimited doubles. Each pair of doubles on a line is
-     * converted to a Complex. For example the following represents the 
numbers (0.5
-     * - 0 i) and (1.5 + 2 i):
+     * <p>Data is assumed to be a resource available to the class loader. The 
data should
+     * be space delimited doubles. Each pair of doubles on a line is converted 
to a
+     * Complex. For example the following represents the numbers (0.5 - 0 i) 
and (1.5 + 2
+     * i):
      *
      * <pre>
      * 0.5 -0.0 1.5 2
      * </pre>
      *
-     * <p>An unmatched double not part of a pair on a line will raise an
-     * AssertionError.
+     * <p>An unmatched double not part of a pair on a line will raise an 
AssertionError.
      *
      * <p>Lines starting with the {@code #} character are ignored.
      *
-     * <p>Lines starting with the {@code ;} character are ignored but printed. 
This can be used
-     * to disable tests in the data file.
+     * <p>Lines starting with the {@code ;} character are processed using the 
provided
+     * flag option. This character can be used to disable tests in the data 
file.
+     *
+     * <p>The flagged data will be passed to the consumer.
      *
      * @param name the resource name
+     * @param option the option controlling processing of flagged data
+     * @param flaggedDataConsumer the flagged data consumer (can be null)
      * @return the list
      */
-    public static List<Complex[]> loadTestData(String name) {
+    public static List<Complex[]> loadTestData(String name, TestDataFlagOption 
option,
+            Consumer<String> flaggedDataConsumer) {
         final List<Complex[]> data = new ArrayList<>();
         try (BufferedReader input = new BufferedReader(
                 new 
InputStreamReader(Thread.currentThread().getContextClassLoader().getResourceAsStream(name))))
 {
             for  (String line = input.readLine(); line != null; line = 
input.readLine()) {
-                // Skip comments and empty lines
-                if (line.length() == 0 || line.charAt(0) == '#') {
-                    continue;
-                }
-                if (line.charAt(0) == ';') {
-                    // CHECKSTYLE: stop all
-                    System.out.println(line);
-                    // CHECKSTYLE: resume
+                line = preprocessTestData(line, option, flaggedDataConsumer);
+                if (line == null) {
                     continue;
                 }
                 final String[] parts = line.split(" ");
@@ -343,4 +354,38 @@ public final class TestUtils {
         }
         return data;
     }
+
+    /**
+     * Pre-process the next line of data from the input.
+     * Returns null when the line should be ignored.
+     *
+     * @param input the input
+     * @param option the option controlling processing of flagged data
+     * @param flaggedDataConsumer the flagged data consumer (can be null)
+     * @return the line of data (or null)
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    private static String preprocessTestData(String line, TestDataFlagOption 
option,
+            Consumer<String> flaggedDataConsumer) {
+        // Skip comments and empty lines
+        if (line.length() == 0 || line.charAt(0) == '#') {
+            return null;
+        }
+        if (line.charAt(0) == ';') {
+            switch (option) {
+            case LOAD:
+                // Strip the leading character
+                line = line.substring(1);
+                break;
+            case IGNORE:
+            default:
+                if (flaggedDataConsumer != null) {
+                    flaggedDataConsumer.accept(line.substring(1));
+                }
+                // Ignore the line
+                line = null;
+            }
+        }
+        return line;
+    }
 }

Reply via email to