--- /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));
+ }
+ }
+}