jdk/test/java/util/Arrays/CopyMethods.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Arrays/CopyMethods.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug     4655503
+ * @summary Test for array cloning and slicing methods.
+ * @author  John Rose
+ */
+
+import java.util.*;
+import java.lang.reflect.*;
+
+public class CopyMethods {
+    static int muzzle;  // if !=0, suppresses ("muzzles") messages
+
+    static int maxLen = 40;  // maximum length of test arrays
+    static int shortStepsNear = 4;  // interesting span near critical values
+    static int downShift = 3;
+
+    static int testCasesRun = 0;
+    static long consing = 0;
+
+    // very simple tests, mainly to test the framework itself
+    static void simpleTests() {
+        int[] a = (int[]) makeArray(3, int.class);
+        if (muzzle == 0)
+            System.out.println("int[] a = "+Arrays.toString(a));
+        check(a.length == 3);
+        check(a[0] == testValues[0]);
+        check(a[1] == testValues[1]);
+        check(a[2] == testValues[2]);
+        checkArray(a, int.class, 3, 0, 3);
+        // negative test of testing framework:
+        for (int bad = -2; bad < a.length; bad++) {
+            try {
+                int[] aa = a.clone();
+                if (bad < 0)  aa = new int[4];
+                else          aa[bad] = 0;
+                ++muzzle;
+                // the following check should fail!
+                if (bad == -2)
+                    checkArray(new String[3], int.class, 0, 0, a.length);
+                else
+                    checkArray(aa, int.class, 0, 0, a.length);
+                throw new Error("Should Not Reach Here");
+            } catch (RuntimeException ee) {
+                --muzzle;
+                if (muzzle == 0)
+                    System.out.println("Expected: "+ee);
+            }
+        }
+        checkArray(Arrays.copyOf(a, 0), int.class, 0, 0, 3);
+        checkArray(Arrays.copyOf(a, 1), int.class, 1, 0, 3);
+        checkArray(Arrays.copyOf(a, 2), int.class, 2, 0, 3);
+        checkArray(Arrays.copyOf(a, 3), int.class, 3, 0, 3);
+        checkArray(Arrays.copyOf(a, 4), int.class, 4, 0, 3);
+
+        // quick test of copyOfRange
+        int[] ar = Arrays.copyOfRange(a, 1, 3);
+        check(ar.length == 2);
+        check(ar[0] == a[1]);
+        check(ar[1] == a[2]);
+        checkArray(ar, int.class, 2, 1, 2);
+        ar = Arrays.copyOfRange(a, 2, 4);
+        check(ar.length == 2);
+        check(ar[0] == a[2]);
+        check(ar[1] == 0);
+        checkArray(ar, int.class, 2, 2, 1);
+        ar = Arrays.copyOfRange(a, 3, 5);
+        check(ar.length == 2);
+        check(ar[0] == 0);
+        check(ar[1] == 0);
+        checkArray(ar, int.class, 2, 3, 0);
+        byte[] ba = (byte[]) makeArray(3, byte.class);
+        if (muzzle == 0)
+            System.out.println("byte[] ba = "+Arrays.toString(ba));
+        for (int j = 0; j <= ba.length+2; j++) {
+            byte[] bb = Arrays.copyOf(ba, j);
+            if (muzzle == 0)
+                System.out.println("copyOf(ba,"+j+") = "+
+                                   Arrays.toString(bb));
+            checkArray(bb, byte.class, j, 0, ba.length);
+            byte[] bbr = Arrays.copyOfRange(ba, 0, j);
+            check(Arrays.equals(bb, bbr));
+        }
+        for (int i = 0; i <= a.length; i++) {
+            for (int j = i; j <= a.length+2; j++) {
+                byte[] br = Arrays.copyOfRange(ba, i, j);
+                if (muzzle == 0)
+                    System.out.println("copyOfRange(ba,"+i+","+j+") = "+
+                                       Arrays.toString(br));
+                checkArray(br, byte.class, j-i, i, ba.length-i);
+            }
+        }
+        String[] sa = (String[]) makeArray(3, String.class);
+        if (muzzle == 0)
+            System.out.println("String[] sa = "+Arrays.toString(sa));
+        check(sa[0].equals(Integer.toHexString(testValues[0])));
+        check(sa[1].equals(Integer.toHexString(testValues[1])));
+        check(sa[2].equals(Integer.toHexString(testValues[2])));
+        checkArray(sa, String.class, sa.length, 0, sa.length);
+        String[] sa4 = Arrays.copyOf(sa, sa.length+1);
+        check(sa4[0] == sa[0]);
+        check(sa4[1] == sa[1]);
+        check(sa4[2] == sa[2]);
+        check(sa4[sa.length] == null);
+        checkArray(sa4, String.class, sa4.length, 0, sa.length);
+        String[] sr4 = Arrays.copyOfRange(sa, 1, 5);
+        check(sr4[0] == sa[1]);
+        check(sr4[1] == sa[2]);
+        check(sr4[2] == null);
+        check(sr4[3] == null);
+        checkArray(sr4, String.class, 4, 1, sa.length-1);
+        if (muzzle == 0)
+            System.out.println("simpleTests done");
+    }
+
+    // the framework:  a fixed series of test values
+    static final int[] testValues;
+    static {
+        testValues = new int[1000];
+        Random r = new Random();
+        for (int i = 0; i < testValues.length; i++) {
+            testValues[i] = r.nextInt();
+        }
+    }
+    /** Return a canonical test value of a desired index and type.
+     *  The original test values are random ints.  Derive other test
+     *  values as follows:
+     *  <pre>
+     *  int tv = testValues[i]
+     *  (C)tv                    C is byte, short, char, long, float, double
+     *  (tv&1)!=0                C is boolean
+     *  (Integer)tv              C is Object and tv%16 != 0
+     *  null                     C is Object and tv%16 == 0
+     *  Integer.toHexString(tv)  C is String and tv != 0
+     *  null                     C is String and tv == 0
+     *  </pre>
+     *  are derived by ordinary Java coercions, except that boolean
+     *  samples the LSB of the int value, and String is the hex numeral.
+     *
+     *  (Also, the 0th String is null, and the 0th Object mod 16 is null,
+     *  regardless of the original int test value.)
+     */
+    static Object testValue(int i, Class<?> c) {
+        int tv = testValues[i % testValues.length];
+        if (i >= testValues.length)  tv ^= i;
+        // Turn the canonical int to a float, boolean, String, whatever:
+        return invoke(coercers.get(c), tv);
+    }
+    /** Build a test array of the given length,
+     *  packed with a subsequence of the test values.
+     *  The first element of the array is always testValue(0).
+     */
+    static Object makeArray(int len, Class<?> c) {
+        Object a = Array.newInstance(c, len);
+        for (int i = 0; i < len; i++) {
+            Array.set(a, i, testValue(i, c));
+        }
+        return a;
+    }
+    /** Check that the given array has the required length.
+     *  Check also that it is packed, up to firstNull, with
+     *  a particular subsequence of the canonical test values.
+     *  The subsequence must begin with a[0] == testValue(offset).
+     *  At a[firstNull] and beyond, the array must contain null values.
+     */
+    static void checkArray(Object a, Class<?> c, int requiredLen, int offset, int firstNull) {
+        check(c == a.getClass().getComponentType());
+        Object nullValue = nullValues.get(c);
+        // Note:  asserts in here are not part of the test program.
+        // They verify the integrity of the test method itself.
+        assert(nullValues.containsKey(c));
+
+        int misses = 0;
+        int firstMiss = -1;
+        // Check required length first.
+        int length = Array.getLength(a);
+        if (length != requiredLen && requiredLen != -1) {
+            if (muzzle == 0)
+                System.out.println("*** a.length = "+length+" != "+requiredLen);
+            ++misses;
+        }
+
+        for (int i = 0; i < length; i++) {
+            Object tv = (i >= firstNull) ? nullValue : testValue(i+offset, c);
+            Object ai = Array.get(a, i);
+            if (!eq(ai, tv)) {
+                if (muzzle == 0)
+                    System.out.println("*** a["+i+"] = "+ai+" != "+tv);
+                if (misses == 0)  firstMiss = i;
+                if (++misses > 10)  break;
+            }
+        }
+        if (misses != 0) {
+            Method toString = toStrings.get(c);
+            if (toString == null)  toString = toStrings.get(Object.class);
+            throw new RuntimeException("checkArray failed at "+firstMiss
+                                       +" "+c+"[]"
+                                       +" : "+invoke(toString, a));
+        }
+    }
+    // Typical comparison helper.  Why isn't this a method somewhere.
+    static boolean eq(Object x, Object y) {
+        return x == null? y == null: x.equals(y);
+    }
+    // Exception-ignoring invoke function.
+    static Object invoke(Method m, Object... args) {
+        Exception ex;
+        try {
+            return m.invoke(null, args);
+        } catch (InvocationTargetException ee) {
+            ex = ee;
+        } catch (IllegalAccessException ee) {
+            ex = ee;
+        } catch (IllegalArgumentException ee) {
+            ex = ee;
+        }
+        ArrayList<Object> call = new ArrayList<Object>();
+        call.add(m); Collections.addAll(call, args);
+        throw new RuntimeException(call+" : "+ex);
+    }
+    // version of assert() that runs unconditionally
+    static void check(boolean z) {
+        if (!z)  throw new RuntimeException("check failed");
+    }
+
+
+    /** Run about 10**5 distinct parameter combinations
+     *  on copyOf and copyOfRange.  Use all primitive types,
+     *  and String and Object.
+     *  Try to all critical values, looking for fencepost errors.
+     */
+    static void fullTests(int maxLen, Class<?> c) {
+        Method cloner      = cloners.get(c);
+        assert(cloner != null) : c;
+        Method cloneRanger = cloneRangers.get(c);
+        // Note:  asserts in here are not part of the test program.
+        // They verify the integrity of the test method itself.
+        assert(cloneRanger != null) : c;
+        for (int src = 0; src <= maxLen; src = inc(src, 0, maxLen)) {
+            Object a = makeArray(src, c);
+            for (int x : new ArrayList<Integer>()) {}
+            for (int j = 0; j <= maxLen; j = inc(j, src, maxLen)) {
+                // b = Arrays.copyOf(a, j);
+                Object b = invoke(cloner, a, j);
+                checkArray(b, c, j, 0, src);
+                testCasesRun++;
+                consing += j;
+
+                int maxI = Math.min(src, j);
+                for (int i = 0; i <= maxI; i = inc(i, src, maxI)) {
+                    // r = Arrays.copyOfRange(a, i, j);
+                    Object r = invoke(cloneRanger, a, i, j);
+                    checkArray(r, c, j-i, i, src-i);
+                    //System.out.println("case c="+c+" src="+src+" i="+i+" j="+j);
+                    testCasesRun++;
+                    consing += j-i;
+                }
+            }
+        }
+    }
+    // Increment x by at least one.  Increment by a little more unless
+    // it is near a critical value, either zero, crit1, or crit2.
+    static int inc(int x, int crit1, int crit2) {
+        int D = shortStepsNear;
+        if (crit1 > crit2) { int t = crit1; crit1 = crit2; crit2 = t; }
+        assert(crit1 <= crit2);
+        assert(x <= crit2);  // next1 or next2 must be the limit value
+        x += 1;
+        if (x > D) {
+            if (x < crit1-D) {
+                x += (x << 1) >> downShift;  // giant step toward crit1-D
+                if (x > crit1-D)  x = crit1-D;
+            } else if (x >= crit1+D && x < crit2-D) {
+                x += (x << 1) >> downShift;  // giant step toward crit2-D
+                if (x > crit2-D)  x = crit2-D;
+            }
+        }
+        return x;
+    }
+
+    public static void main(String[] av) {
+        boolean verbose = (av.length != 0);
+        muzzle = (verbose? 0: 1);
+        if (muzzle == 0)
+            System.out.println("test values: "+Arrays.toString(Arrays.copyOf(testValues, 5))+"...");
+
+        simpleTests();
+
+        muzzle = 0;  // turn on print statements (affects failures only)
+
+        fullTests();
+        if (verbose)
+            System.out.println("ran "+testCasesRun+" tests, avg len="
+                               +(float)consing/testCasesRun);
+
+        // test much larger arrays, more sparsely
+        maxLen = 500;
+        shortStepsNear = 2;
+        downShift = 0;
+        testCasesRun = 0;
+        consing = 0;
+        fullTests();
+        if (verbose)
+            System.out.println("ran "+testCasesRun+" tests, avg len="
+                               +(float)consing/testCasesRun);
+    }
+
+    static void fullTests() {
+        for (Class<?> c : allTypes) {
+            fullTests(maxLen, c);
+        }
+    }
+
+    // We must run all the our tests on each of 8 distinct primitive types,
+    // and two reference types (Object, String) for good measure.
+    // This would be a pain to write out by hand, statically typed.
+    // So, use reflection.  Following are the tables of methods we use.
+    // (The initial simple tests exercise enough of the static typing
+    // features of the API to ensure that they compile as advertised.)
+
+    static Object  coerceToObject(int x) { return (x & 0xF) == 0? null: new Integer(x); }
+    static String  coerceToString(int x) { return (x == 0)? null: Integer.toHexString(x); }
+    static Integer coerceToInteger(int x) { return (x == 0)? null: x; }
+    static byte    coerceToByte(int x) { return (byte)x; }
+    static short   coerceToShort(int x) { return (short)x; }
+    static int     coerceToInt(int x) { return x; }
+    static long    coerceToLong(int x) { return x; }
+    static char    coerceToChar(int x) { return (char)x; }
+    static float   coerceToFloat(int x) { return x; }
+    static double  coerceToDouble(int x) { return x; }
+    static boolean coerceToBoolean(int x) { return (x&1) != 0; }
+
+    static Integer[] copyOfIntegerArray(Object[] a, int len) {
+        // This guy exercises the API based on a type-token.
+        // Note the static typing.
+        return Arrays.copyOf(a, len, Integer[].class);
+    }
+    static Integer[] copyOfIntegerArrayRange(Object[] a, int m, int n) {
+        // This guy exercises the API based on a type-token.
+        // Note the static typing.
+        return Arrays.copyOfRange(a, m, n, Integer[].class);
+    }
+
+    static final List<Class<?>> allTypes
+        = Arrays.asList(new Class<?>[]
+                        {   Object.class, String.class, Integer.class,
+                            byte.class, short.class, int.class, long.class,
+                            char.class, float.class, double.class,
+                            boolean.class
+                        });
+    static final HashMap<Class<?>,Method> coercers;
+    static final HashMap<Class<?>,Method> cloners;
+    static final HashMap<Class<?>,Method> cloneRangers;
+    static final HashMap<Class<?>,Method> toStrings;
+    static final HashMap<Class<?>,Object> nullValues;
+    static {
+        coercers = new HashMap<Class<?>,Method>();
+        Method[] testMethods = CopyMethods.class.getDeclaredMethods();
+        Method cia = null, ciar = null;
+        for (int i = 0; i < testMethods.length; i++) {
+            Method m = testMethods[i];
+            if (!Modifier.isStatic(m.getModifiers()))  continue;
+            Class<?> rt = m.getReturnType();
+            if (m.getName().startsWith("coerceTo") && allTypes.contains(rt))
+                coercers.put(m.getReturnType(), m);
+            if (m.getName().equals("copyOfIntegerArray"))
+                cia = m;
+            if (m.getName().equals("copyOfIntegerArrayRange"))
+                ciar = m;
+        }
+        Method[] arrayMethods = Arrays.class.getDeclaredMethods();
+        cloners      = new HashMap<Class<?>,Method>();
+        cloneRangers = new HashMap<Class<?>,Method>();
+        toStrings    = new HashMap<Class<?>,Method>();
+        for (int i = 0; i < arrayMethods.length; i++) {
+            Method m = arrayMethods[i];
+            if (!Modifier.isStatic(m.getModifiers()))  continue;
+            Class<?> rt = m.getReturnType();
+            if (m.getName().equals("copyOf")
+                && m.getParameterTypes().length == 2)
+                cloners.put(rt.getComponentType(), m);
+            if (m.getName().equals("copyOfRange")
+                && m.getParameterTypes().length == 3)
+                cloneRangers.put(rt.getComponentType(), m);
+            if (m.getName().equals("toString")) {
+                Class<?> pt = m.getParameterTypes()[0];
+                toStrings.put(pt.getComponentType(), m);
+            }
+        }
+        cloners.put(String.class, cloners.get(Object.class));
+        cloneRangers.put(String.class, cloneRangers.get(Object.class));
+        assert(cia != null);
+        cloners.put(Integer.class, cia);
+        assert(ciar != null);
+        cloneRangers.put(Integer.class, ciar);
+        nullValues = new HashMap<Class<?>,Object>();
+        for (Class<?> c : allTypes) {
+            nullValues.put(c, invoke(coercers.get(c), 0));
+        }
+    }
+}