org.eclipse.triquetrum.scisoft.analysis.rpc.flattening.FlatteningTestAbstract.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.triquetrum.scisoft.analysis.rpc.flattening.FlatteningTestAbstract.java

Source

/*******************************************************************************
 * Copyright (c) 2012-2016 Diamond Light Source Ltd.,
 *                         Kichwa Coders & iSencia Belgium NV.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    DLS, Kichwa Coders - initial API and implementation and/or initial documentation
 *    Erwin De Ley - extraction from DAWN to ease reuse in other contexts
 *******************************************************************************/

package org.eclipse.triquetrum.scisoft.analysis.rpc.flattening;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;

import org.apache.commons.lang.ArrayUtils;
import org.eclipse.triquetrum.scisoft.analysis.rpc.AnalysisRpcRemoteException;
import org.eclipse.triquetrum.scisoft.analysis.rpc.internal.AnalysisRpcDoubleParser;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;

abstract public class FlatteningTestAbstract {
    @Rule
    public Timeout globalTimeout = Timeout.seconds(2);

    /**
     * Waiting period for server to start up (in milliseconds)
     */
    public static final long SERVER_WAIT_TIME = 200;

    protected static IRootFlattener flattener;

    @BeforeClass
    public static void setUp() {
        flattener = new RootFlattener();
    }

    /**
     * Special version of Assert.assertEquals that, for some types, uses reflection or other knowledge instead of equals() to test for equality. The reason for
     * this is to work around insufficient or unsuitable equals implementations in those classes. Arrays, Lists and Maps are compared deeply.
     *
     * @param expected
     *          first object to test
     * @param actual
     *          second object to test
     */
    protected void assertFlattenEquals(Object expected, Object actual) {
        if (expected == null) {
            Assert.assertNull(actual);
        } else if (expected instanceof Exception) {
            Exception expException = (Exception) expected;
            Exception actException = (Exception) actual;
            // The message gets supplemented with extra type information, make sure the original expected
            // message is present in the supplemented info
            if (!actException.getMessage().contains(expException.getMessage())) {
                Assert.fail("Expected message: " + expException.getMessage() + " actual message: "
                        + actException.getMessage());
            }
            StackTraceElement[] expStackTrace = expException.getStackTrace();
            StackTraceElement[] actStackTrace = actException.getStackTrace();
            Assert.assertArrayEquals(expStackTrace, actStackTrace);
        } else if (expected instanceof List) {
            List<?> expectedlist = (List<?>) expected;
            List<?> actuallist = (List<?>) actual;
            Assert.assertEquals(expectedlist.size(), actuallist.size());
            for (int i = 0; i < actuallist.size(); i++) {
                assertFlattenEquals(expectedlist.get(i), actuallist.get(i));
            }
        } else if (expected instanceof byte[]) {
            byte[] expectedarray = (byte[]) expected;
            byte[] actualarray = (byte[]) actual;
            Assert.assertArrayEquals(expectedarray, actualarray);
        } else if (expected instanceof int[]) {
            int[] expectedarray = (int[]) expected;
            int[] actualarray = (int[]) actual;
            Assert.assertArrayEquals(expectedarray, actualarray);
        } else if (expected instanceof double[]) {
            double[] expectedarray = (double[]) expected;
            double[] actualarray = (double[]) actual;
            Assert.assertArrayEquals(expectedarray, actualarray, 0.0);
        } else if (expected instanceof boolean[]) {
            boolean[] expectedarray = (boolean[]) expected;
            boolean[] actualarray = (boolean[]) actual;
            Assert.assertTrue(Arrays.equals(expectedarray, actualarray));
        } else if (expected instanceof Object[]) {
            Object[] expectedarray = (Object[]) expected;
            Object[] actualarray = (Object[]) actual;
            Assert.assertEquals(expectedarray.length, actualarray.length);
            for (int i = 0; i < actualarray.length; i++) {
                assertFlattenEquals(expectedarray[i], actualarray[i]);
            }
        } else if (expected instanceof Map) {
            Map<?, ?> expectedMap = (Map<?, ?>) expected;
            Map<?, ?> actualMap = (Map<?, ?>) actual;
            Assert.assertEquals(expectedMap.size(), actualMap.size());
            Set<?> entrySet = expectedMap.entrySet();
            for (Object object : entrySet) {
                Entry<?, ?> entry = (Entry<?, ?>) object;
                Assert.assertTrue(actualMap.containsKey(entry.getKey()));
                assertFlattenEquals(entry.getValue(), actualMap.get(entry.getKey()));
            }
        } else {
            Assert.assertEquals(expected, actual);
        }
    }

    protected Object flattenAndUnflatten(Object inObj) {
        return flattenAndUnflatten(inObj, inObj);
    }

    private Object flattenAndUnflatten(Object inObj, Object expectedObj) {
        return flattenAndUnflatten(inObj, expectedObj, expectedObj.getClass());
    }

    protected abstract Object doActualFlattenAndUnflatten(Object inObj);

    private Object flattenAndUnflatten(Object inObj, Object expectedObj, Class<?> expectedType) {
        final Object out = doActualFlattenAndUnflatten(inObj);

        assertFlattenEquals(expectedObj, out);
        if (expectedObj != null && expectedObj.getClass().equals(expectedType)) {
            Assert.assertEquals(expectedType, out.getClass());
        } else if (expectedType != null) {
            Assert.assertTrue(expectedType.isAssignableFrom(out.getClass()));
        }

        // finally, take a unflattened item and make sure it has made something
        // that is fully flattenable still
        assertFlattenEquals(out, doActualFlattenAndUnflatten(out));

        return out;
    }

    @Test
    public void testInteger() {
        flattenAndUnflatten(18);
        flattenAndUnflatten(-7);
        flattenAndUnflatten(0);
        flattenAndUnflatten(Integer.MIN_VALUE);
        flattenAndUnflatten(Integer.MAX_VALUE);
    }

    @Test
    public void testBoolean() {
        flattenAndUnflatten(true);
        flattenAndUnflatten(false);
    }

    @Test
    public void testString() {
        flattenAndUnflatten("");
        flattenAndUnflatten("bananas");
        flattenAndUnflatten("\nhello\tgoodbye");
    }

    @Test
    public void testUnicodeString() {
        flattenAndUnflatten("\u00b0");
    }

    @Test
    public void testDouble() {
        flattenAndUnflatten(0);
        flattenAndUnflatten(Math.PI);
        flattenAndUnflatten(Double.MIN_VALUE);
        flattenAndUnflatten(Double.MAX_VALUE);
    }

    /**
     * Note NaN, +/-Inf only work if AnalysisRpc is in the loop or Java is talking to Java. Therefore this method overridden by classes that don't support it.
     * Open hierarchy to see where this test really exists.
     * <p>
     * Note the subclasses that do not support it don't matter because they were written simply to identify these types of issues.
     *
     * @See {@link AnalysisRpcDoubleParser}
     */
    @Test
    public void testDoubleSpecialValues() {
        flattenAndUnflatten(Double.NaN);
        flattenAndUnflatten(Double.NEGATIVE_INFINITY);
        flattenAndUnflatten(Double.POSITIVE_INFINITY);
    }

    @Test
    public void testByeArray() {
        flattenAndUnflatten(new byte[] { 1, 5, -7 });
        flattenAndUnflatten(new byte[0]);
        flattenAndUnflatten(new byte[1000]);
    }

    @Test
    public void testMap() {
        HashMap<String, Double> hashMap = new HashMap<String, Double>();
        hashMap.put("pi", Math.PI);
        hashMap.put("One", new Double(1));
        flattenAndUnflatten(hashMap, hashMap, Map.class);

        TreeMap<String, Double> treeMap = new TreeMap<String, Double>(hashMap);
        flattenAndUnflatten(treeMap, treeMap, Map.class);

        HashMap<String, Object> hashMap2 = new HashMap<String, Object>(hashMap);
        hashMap2.put("Integer", Integer.valueOf(0));
        hashMap2.put("Integer", Integer.valueOf(100));
        flattenAndUnflatten(hashMap2, hashMap2, Map.class);

        HashMap<Object, Object> nonStringKeys = new HashMap<Object, Object>(hashMap2);
        nonStringKeys.put(Integer.valueOf(5), Integer.valueOf(0));
        nonStringKeys.put(Double.valueOf(10.0), "Filename");
        flattenAndUnflatten(nonStringKeys, nonStringKeys, Map.class);
    }

    @Test
    public void testObjectArrays() {
        flattenAndUnflatten(new Object[] { new Double(1.2), new Integer(2) });

        // arrays of things which look like arrays of integers, double or booleans come out as such
        flattenAndUnflatten(new Object[] { 0, 1, 2, 3 }, new int[] { 0, 1, 2, 3 });
        flattenAndUnflatten(
                new Object[] { new Object[] { 0, 1, 2, 3 }, new Object[] { 4, 5, 6, 7 },
                        new Object[] { 8, 9, 10, 11 }, new Object[] { 12, 13, 14, 15 } },
                new int[][] { new int[] { 0, 1, 2, 3 }, new int[] { 4, 5, 6, 7 }, new int[] { 8, 9, 10, 11 },
                        new int[] { 12, 13, 14, 15 } });
        flattenAndUnflatten(new Object[][] { { 0, 1 }, { 2, 3 } },
                new int[][] { new int[] { 0, 1 }, new int[] { 2, 3 } });

        // Empty arrays come out as array of Object[]
        flattenAndUnflatten(new Object[0], new Object[0]);
        flattenAndUnflatten(new int[0], new Object[0]);
        flattenAndUnflatten(new String[0], new Object[0]);

        // arrays of other types come out as arrays if the class of each element of the array is the same
        flattenAndUnflatten(new String[] { "one", "two" }, new String[] { "one", "two" }); // String[] --> String[]
        flattenAndUnflatten(new Object[] { "one", "two" }, new String[] { "one", "two" }); // Object[] --> String[]
        flattenAndUnflatten(new Object[] { new Integer(0), "one" }, new Object[] { new Integer(0), "one" }); // Object[] --> Object[]

        // arrays with some (but not all) nulls follow the rule above
        flattenAndUnflatten(new String[] { null, "two" }, new String[] { null, "two" }); // String[] --> String[]
        flattenAndUnflatten(new String[] { "one", null }, new String[] { "one", null }); // String[] --> String[]
        flattenAndUnflatten(new Object[] { null, "two" }, new String[] { null, "two" }); // Object[] --> String[]

        // arrays with all nulls in them come out as arrays of Object[]
        flattenAndUnflatten(new String[] { null, null }, new Object[] { null, null }); // String[] --> Object[]
        flattenAndUnflatten(new Object[] { null, null }, new Object[] { null, null }); // Object[] --> Object[]
    }

    @Test
    public void testLists() {
        ArrayList<String> strings = new ArrayList<String>();
        strings.add("one");
        strings.add("two");
        strings.add("three");

        // Lists are to the same type as every element in the array if they are all the same class...
        flattenAndUnflatten(strings, strings.toArray(new String[0]));

        ArrayList<Number> nums = new ArrayList<Number>();
        nums.add(new Double(0.0));
        nums.add(new Integer(1));

        // ...otherwise they unflatten to an array of Object[]
        flattenAndUnflatten(nums, nums.toArray(new Object[0]));
    }

    @Test
    public void testNull() {
        flattenAndUnflatten(null, null, null);

        // test null within other data structures
        Map<Object, Object> map = new HashMap<Object, Object>();
        // map, null key
        map.put(null, "null");
        flattenAndUnflatten(map, map, Map.class);
        // map, null value
        map.put("null", null);
        flattenAndUnflatten(map, map, Map.class);
        // map, null key and value
        map.put(null, null);
        flattenAndUnflatten(map, map, Map.class);

        String[] array = new String[] { "null", null };
        // array, null entry
        flattenAndUnflatten(array);
        // list, null entry
        flattenAndUnflatten(Arrays.asList(array), array);
    }

    @Test
    public void testTypedNull() {
        // A typed null should come out equal
        TypedNone nullDouble = new TypedNone(Double.class);
        flattenAndUnflatten(nullDouble, nullDouble);
    }

    @Test
    public void testException() {
        // Exceptions are always unflattened as AnalysisRpcRemoteException type,
        // with original type information pre-pended to the message
        Exception[] npe_in = new Exception[] { new NullPointerException("Exceptional null happened") };
        AnalysisRpcRemoteException[] npe_out = new AnalysisRpcRemoteException[] {
                new AnalysisRpcRemoteException("Exceptional null happened") };
        // Make sure the stack traces are the same
        npe_out[0].setStackTrace(npe_in[0].getStackTrace());
        flattenAndUnflatten(npe_in, npe_out);
    }

    @Test
    public void testUUID() {
        flattenAndUnflatten(UUID.fromString("93dfd804-85ba-4074-afce-d621f7f2aac6"));
        flattenAndUnflatten(UUID.fromString("dd09fd5c-bb75-4c8b-854b-7f3bb2c9c399"));
        flattenAndUnflatten(UUID.fromString("00000000-0000-0000-0000-000000000000"));
        flattenAndUnflatten(UUID.fromString("ffffffff-ffff-ffff-ffff-ffffffffffff"));
        flattenAndUnflatten(UUID.fromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"));
        for (int i = 0; i < 100; i++) {
            // There is a reproducibility danger in using random UUIDs in this
            // test in that if one fails to match it cannot simply be re-run to
            // reproduce. The failing UUID should be extracted from the error message
            // stack trace and added to the list above of explicit values.
            // If it fails, the message will look something like:
            // junit.framework.AssertionFailedError: expected:<93dfd804-85ba-4074-afce-d621f7f2aac6> but
            // was:<dd09fd5c-bb75-4c8b-854b-7f3bb2c9c399>
            // ...
            flattenAndUnflatten(UUID.randomUUID());
        }
    }

    // Primitive arrays and arrays of boxed primitives unflatten as primitive arrays
    @Test
    public void testPrimitiveArrays() {
        int[] ints = { 1, 5, -7 };
        flattenAndUnflatten(ints);
        flattenAndUnflatten(ArrayUtils.toObject(ints), ints);

        double[] doubles = { 1.4, 12.6, 0 };
        flattenAndUnflatten(doubles);
        flattenAndUnflatten(ArrayUtils.toObject(doubles), doubles);

        boolean[] booleans = { true, false, false, true };
        flattenAndUnflatten(booleans);
        flattenAndUnflatten(ArrayUtils.toObject(booleans), booleans);

        double[][] doubles2d = { { 1, 5, -7 }, { 1.4, 12.6, 0 } };
        flattenAndUnflatten(doubles2d, new double[][] { doubles2d[0], doubles2d[1] });
        flattenAndUnflatten(new double[][] { doubles2d[0], doubles2d[1] });
    }

}