J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. |
| 8 | * |
| 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 12 | * version 2 for more details (a copy is included in the LICENSE file that |
| 13 | * accompanied this code). |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License version |
| 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | * |
| 19 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 20 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 21 | * have any questions. |
| 22 | */ |
| 23 | |
| 24 | /* |
| 25 | * @test |
| 26 | * @bug 4655503 |
| 27 | * @summary Test for array cloning and slicing methods. |
| 28 | * @author John Rose |
| 29 | */ |
| 30 | |
| 31 | import java.util.*; |
| 32 | import java.lang.reflect.*; |
| 33 | |
| 34 | public class CopyMethods { |
| 35 | static int muzzle; // if !=0, suppresses ("muzzles") messages |
| 36 | |
| 37 | static int maxLen = 40; // maximum length of test arrays |
| 38 | static int shortStepsNear = 4; // interesting span near critical values |
| 39 | static int downShift = 3; |
| 40 | |
| 41 | static int testCasesRun = 0; |
| 42 | static long consing = 0; |
| 43 | |
| 44 | // very simple tests, mainly to test the framework itself |
| 45 | static void simpleTests() { |
| 46 | int[] a = (int[]) makeArray(3, int.class); |
| 47 | if (muzzle == 0) |
| 48 | System.out.println("int[] a = "+Arrays.toString(a)); |
| 49 | check(a.length == 3); |
| 50 | check(a[0] == testValues[0]); |
| 51 | check(a[1] == testValues[1]); |
| 52 | check(a[2] == testValues[2]); |
| 53 | checkArray(a, int.class, 3, 0, 3); |
| 54 | // negative test of testing framework: |
| 55 | for (int bad = -2; bad < a.length; bad++) { |
| 56 | try { |
| 57 | int[] aa = a.clone(); |
| 58 | if (bad < 0) aa = new int[4]; |
| 59 | else aa[bad] = 0; |
| 60 | ++muzzle; |
| 61 | // the following check should fail! |
| 62 | if (bad == -2) |
| 63 | checkArray(new String[3], int.class, 0, 0, a.length); |
| 64 | else |
| 65 | checkArray(aa, int.class, 0, 0, a.length); |
| 66 | throw new Error("Should Not Reach Here"); |
| 67 | } catch (RuntimeException ee) { |
| 68 | --muzzle; |
| 69 | if (muzzle == 0) |
| 70 | System.out.println("Expected: "+ee); |
| 71 | } |
| 72 | } |
| 73 | checkArray(Arrays.copyOf(a, 0), int.class, 0, 0, 3); |
| 74 | checkArray(Arrays.copyOf(a, 1), int.class, 1, 0, 3); |
| 75 | checkArray(Arrays.copyOf(a, 2), int.class, 2, 0, 3); |
| 76 | checkArray(Arrays.copyOf(a, 3), int.class, 3, 0, 3); |
| 77 | checkArray(Arrays.copyOf(a, 4), int.class, 4, 0, 3); |
| 78 | |
| 79 | // quick test of copyOfRange |
| 80 | int[] ar = Arrays.copyOfRange(a, 1, 3); |
| 81 | check(ar.length == 2); |
| 82 | check(ar[0] == a[1]); |
| 83 | check(ar[1] == a[2]); |
| 84 | checkArray(ar, int.class, 2, 1, 2); |
| 85 | ar = Arrays.copyOfRange(a, 2, 4); |
| 86 | check(ar.length == 2); |
| 87 | check(ar[0] == a[2]); |
| 88 | check(ar[1] == 0); |
| 89 | checkArray(ar, int.class, 2, 2, 1); |
| 90 | ar = Arrays.copyOfRange(a, 3, 5); |
| 91 | check(ar.length == 2); |
| 92 | check(ar[0] == 0); |
| 93 | check(ar[1] == 0); |
| 94 | checkArray(ar, int.class, 2, 3, 0); |
| 95 | byte[] ba = (byte[]) makeArray(3, byte.class); |
| 96 | if (muzzle == 0) |
| 97 | System.out.println("byte[] ba = "+Arrays.toString(ba)); |
| 98 | for (int j = 0; j <= ba.length+2; j++) { |
| 99 | byte[] bb = Arrays.copyOf(ba, j); |
| 100 | if (muzzle == 0) |
| 101 | System.out.println("copyOf(ba,"+j+") = "+ |
| 102 | Arrays.toString(bb)); |
| 103 | checkArray(bb, byte.class, j, 0, ba.length); |
| 104 | byte[] bbr = Arrays.copyOfRange(ba, 0, j); |
| 105 | check(Arrays.equals(bb, bbr)); |
| 106 | } |
| 107 | for (int i = 0; i <= a.length; i++) { |
| 108 | for (int j = i; j <= a.length+2; j++) { |
| 109 | byte[] br = Arrays.copyOfRange(ba, i, j); |
| 110 | if (muzzle == 0) |
| 111 | System.out.println("copyOfRange(ba,"+i+","+j+") = "+ |
| 112 | Arrays.toString(br)); |
| 113 | checkArray(br, byte.class, j-i, i, ba.length-i); |
| 114 | } |
| 115 | } |
| 116 | String[] sa = (String[]) makeArray(3, String.class); |
| 117 | if (muzzle == 0) |
| 118 | System.out.println("String[] sa = "+Arrays.toString(sa)); |
| 119 | check(sa[0].equals(Integer.toHexString(testValues[0]))); |
| 120 | check(sa[1].equals(Integer.toHexString(testValues[1]))); |
| 121 | check(sa[2].equals(Integer.toHexString(testValues[2]))); |
| 122 | checkArray(sa, String.class, sa.length, 0, sa.length); |
| 123 | String[] sa4 = Arrays.copyOf(sa, sa.length+1); |
| 124 | check(sa4[0] == sa[0]); |
| 125 | check(sa4[1] == sa[1]); |
| 126 | check(sa4[2] == sa[2]); |
| 127 | check(sa4[sa.length] == null); |
| 128 | checkArray(sa4, String.class, sa4.length, 0, sa.length); |
| 129 | String[] sr4 = Arrays.copyOfRange(sa, 1, 5); |
| 130 | check(sr4[0] == sa[1]); |
| 131 | check(sr4[1] == sa[2]); |
| 132 | check(sr4[2] == null); |
| 133 | check(sr4[3] == null); |
| 134 | checkArray(sr4, String.class, 4, 1, sa.length-1); |
| 135 | if (muzzle == 0) |
| 136 | System.out.println("simpleTests done"); |
| 137 | } |
| 138 | |
| 139 | // the framework: a fixed series of test values |
| 140 | static final int[] testValues; |
| 141 | static { |
| 142 | testValues = new int[1000]; |
| 143 | Random r = new Random(); |
| 144 | for (int i = 0; i < testValues.length; i++) { |
| 145 | testValues[i] = r.nextInt(); |
| 146 | } |
| 147 | } |
| 148 | /** Return a canonical test value of a desired index and type. |
| 149 | * The original test values are random ints. Derive other test |
| 150 | * values as follows: |
| 151 | * <pre> |
| 152 | * int tv = testValues[i] |
| 153 | * (C)tv C is byte, short, char, long, float, double |
| 154 | * (tv&1)!=0 C is boolean |
| 155 | * (Integer)tv C is Object and tv%16 != 0 |
| 156 | * null C is Object and tv%16 == 0 |
| 157 | * Integer.toHexString(tv) C is String and tv != 0 |
| 158 | * null C is String and tv == 0 |
| 159 | * </pre> |
| 160 | * are derived by ordinary Java coercions, except that boolean |
| 161 | * samples the LSB of the int value, and String is the hex numeral. |
| 162 | * |
| 163 | * (Also, the 0th String is null, and the 0th Object mod 16 is null, |
| 164 | * regardless of the original int test value.) |
| 165 | */ |
| 166 | static Object testValue(int i, Class<?> c) { |
| 167 | int tv = testValues[i % testValues.length]; |
| 168 | if (i >= testValues.length) tv ^= i; |
| 169 | // Turn the canonical int to a float, boolean, String, whatever: |
| 170 | return invoke(coercers.get(c), tv); |
| 171 | } |
| 172 | /** Build a test array of the given length, |
| 173 | * packed with a subsequence of the test values. |
| 174 | * The first element of the array is always testValue(0). |
| 175 | */ |
| 176 | static Object makeArray(int len, Class<?> c) { |
| 177 | Object a = Array.newInstance(c, len); |
| 178 | for (int i = 0; i < len; i++) { |
| 179 | Array.set(a, i, testValue(i, c)); |
| 180 | } |
| 181 | return a; |
| 182 | } |
| 183 | /** Check that the given array has the required length. |
| 184 | * Check also that it is packed, up to firstNull, with |
| 185 | * a particular subsequence of the canonical test values. |
| 186 | * The subsequence must begin with a[0] == testValue(offset). |
| 187 | * At a[firstNull] and beyond, the array must contain null values. |
| 188 | */ |
| 189 | static void checkArray(Object a, Class<?> c, int requiredLen, int offset, int firstNull) { |
| 190 | check(c == a.getClass().getComponentType()); |
| 191 | Object nullValue = nullValues.get(c); |
| 192 | // Note: asserts in here are not part of the test program. |
| 193 | // They verify the integrity of the test method itself. |
| 194 | assert(nullValues.containsKey(c)); |
| 195 | |
| 196 | int misses = 0; |
| 197 | int firstMiss = -1; |
| 198 | // Check required length first. |
| 199 | int length = Array.getLength(a); |
| 200 | if (length != requiredLen && requiredLen != -1) { |
| 201 | if (muzzle == 0) |
| 202 | System.out.println("*** a.length = "+length+" != "+requiredLen); |
| 203 | ++misses; |
| 204 | } |
| 205 | |
| 206 | for (int i = 0; i < length; i++) { |
| 207 | Object tv = (i >= firstNull) ? nullValue : testValue(i+offset, c); |
| 208 | Object ai = Array.get(a, i); |
| 209 | if (!eq(ai, tv)) { |
| 210 | if (muzzle == 0) |
| 211 | System.out.println("*** a["+i+"] = "+ai+" != "+tv); |
| 212 | if (misses == 0) firstMiss = i; |
| 213 | if (++misses > 10) break; |
| 214 | } |
| 215 | } |
| 216 | if (misses != 0) { |
| 217 | Method toString = toStrings.get(c); |
| 218 | if (toString == null) toString = toStrings.get(Object.class); |
| 219 | throw new RuntimeException("checkArray failed at "+firstMiss |
| 220 | +" "+c+"[]" |
| 221 | +" : "+invoke(toString, a)); |
| 222 | } |
| 223 | } |
| 224 | // Typical comparison helper. Why isn't this a method somewhere. |
| 225 | static boolean eq(Object x, Object y) { |
| 226 | return x == null? y == null: x.equals(y); |
| 227 | } |
| 228 | // Exception-ignoring invoke function. |
| 229 | static Object invoke(Method m, Object... args) { |
| 230 | Exception ex; |
| 231 | try { |
| 232 | return m.invoke(null, args); |
| 233 | } catch (InvocationTargetException ee) { |
| 234 | ex = ee; |
| 235 | } catch (IllegalAccessException ee) { |
| 236 | ex = ee; |
| 237 | } catch (IllegalArgumentException ee) { |
| 238 | ex = ee; |
| 239 | } |
| 240 | ArrayList<Object> call = new ArrayList<Object>(); |
| 241 | call.add(m); Collections.addAll(call, args); |
| 242 | throw new RuntimeException(call+" : "+ex); |
| 243 | } |
| 244 | // version of assert() that runs unconditionally |
| 245 | static void check(boolean z) { |
| 246 | if (!z) throw new RuntimeException("check failed"); |
| 247 | } |
| 248 | |
| 249 | |
| 250 | /** Run about 10**5 distinct parameter combinations |
| 251 | * on copyOf and copyOfRange. Use all primitive types, |
| 252 | * and String and Object. |
| 253 | * Try to all critical values, looking for fencepost errors. |
| 254 | */ |
| 255 | static void fullTests(int maxLen, Class<?> c) { |
| 256 | Method cloner = cloners.get(c); |
| 257 | assert(cloner != null) : c; |
| 258 | Method cloneRanger = cloneRangers.get(c); |
| 259 | // Note: asserts in here are not part of the test program. |
| 260 | // They verify the integrity of the test method itself. |
| 261 | assert(cloneRanger != null) : c; |
| 262 | for (int src = 0; src <= maxLen; src = inc(src, 0, maxLen)) { |
| 263 | Object a = makeArray(src, c); |
| 264 | for (int x : new ArrayList<Integer>()) {} |
| 265 | for (int j = 0; j <= maxLen; j = inc(j, src, maxLen)) { |
| 266 | // b = Arrays.copyOf(a, j); |
| 267 | Object b = invoke(cloner, a, j); |
| 268 | checkArray(b, c, j, 0, src); |
| 269 | testCasesRun++; |
| 270 | consing += j; |
| 271 | |
| 272 | int maxI = Math.min(src, j); |
| 273 | for (int i = 0; i <= maxI; i = inc(i, src, maxI)) { |
| 274 | // r = Arrays.copyOfRange(a, i, j); |
| 275 | Object r = invoke(cloneRanger, a, i, j); |
| 276 | checkArray(r, c, j-i, i, src-i); |
| 277 | //System.out.println("case c="+c+" src="+src+" i="+i+" j="+j); |
| 278 | testCasesRun++; |
| 279 | consing += j-i; |
| 280 | } |
| 281 | } |
| 282 | } |
| 283 | } |
| 284 | // Increment x by at least one. Increment by a little more unless |
| 285 | // it is near a critical value, either zero, crit1, or crit2. |
| 286 | static int inc(int x, int crit1, int crit2) { |
| 287 | int D = shortStepsNear; |
| 288 | if (crit1 > crit2) { int t = crit1; crit1 = crit2; crit2 = t; } |
| 289 | assert(crit1 <= crit2); |
| 290 | assert(x <= crit2); // next1 or next2 must be the limit value |
| 291 | x += 1; |
| 292 | if (x > D) { |
| 293 | if (x < crit1-D) { |
| 294 | x += (x << 1) >> downShift; // giant step toward crit1-D |
| 295 | if (x > crit1-D) x = crit1-D; |
| 296 | } else if (x >= crit1+D && x < crit2-D) { |
| 297 | x += (x << 1) >> downShift; // giant step toward crit2-D |
| 298 | if (x > crit2-D) x = crit2-D; |
| 299 | } |
| 300 | } |
| 301 | return x; |
| 302 | } |
| 303 | |
| 304 | public static void main(String[] av) { |
| 305 | boolean verbose = (av.length != 0); |
| 306 | muzzle = (verbose? 0: 1); |
| 307 | if (muzzle == 0) |
| 308 | System.out.println("test values: "+Arrays.toString(Arrays.copyOf(testValues, 5))+"..."); |
| 309 | |
| 310 | simpleTests(); |
| 311 | |
| 312 | muzzle = 0; // turn on print statements (affects failures only) |
| 313 | |
| 314 | fullTests(); |
| 315 | if (verbose) |
| 316 | System.out.println("ran "+testCasesRun+" tests, avg len=" |
| 317 | +(float)consing/testCasesRun); |
| 318 | |
| 319 | // test much larger arrays, more sparsely |
| 320 | maxLen = 500; |
| 321 | shortStepsNear = 2; |
| 322 | downShift = 0; |
| 323 | testCasesRun = 0; |
| 324 | consing = 0; |
| 325 | fullTests(); |
| 326 | if (verbose) |
| 327 | System.out.println("ran "+testCasesRun+" tests, avg len=" |
| 328 | +(float)consing/testCasesRun); |
| 329 | } |
| 330 | |
| 331 | static void fullTests() { |
| 332 | for (Class<?> c : allTypes) { |
| 333 | fullTests(maxLen, c); |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | // We must run all the our tests on each of 8 distinct primitive types, |
| 338 | // and two reference types (Object, String) for good measure. |
| 339 | // This would be a pain to write out by hand, statically typed. |
| 340 | // So, use reflection. Following are the tables of methods we use. |
| 341 | // (The initial simple tests exercise enough of the static typing |
| 342 | // features of the API to ensure that they compile as advertised.) |
| 343 | |
| 344 | static Object coerceToObject(int x) { return (x & 0xF) == 0? null: new Integer(x); } |
| 345 | static String coerceToString(int x) { return (x == 0)? null: Integer.toHexString(x); } |
| 346 | static Integer coerceToInteger(int x) { return (x == 0)? null: x; } |
| 347 | static byte coerceToByte(int x) { return (byte)x; } |
| 348 | static short coerceToShort(int x) { return (short)x; } |
| 349 | static int coerceToInt(int x) { return x; } |
| 350 | static long coerceToLong(int x) { return x; } |
| 351 | static char coerceToChar(int x) { return (char)x; } |
| 352 | static float coerceToFloat(int x) { return x; } |
| 353 | static double coerceToDouble(int x) { return x; } |
| 354 | static boolean coerceToBoolean(int x) { return (x&1) != 0; } |
| 355 | |
| 356 | static Integer[] copyOfIntegerArray(Object[] a, int len) { |
| 357 | // This guy exercises the API based on a type-token. |
| 358 | // Note the static typing. |
| 359 | return Arrays.copyOf(a, len, Integer[].class); |
| 360 | } |
| 361 | static Integer[] copyOfIntegerArrayRange(Object[] a, int m, int n) { |
| 362 | // This guy exercises the API based on a type-token. |
| 363 | // Note the static typing. |
| 364 | return Arrays.copyOfRange(a, m, n, Integer[].class); |
| 365 | } |
| 366 | |
| 367 | static final List<Class<?>> allTypes |
| 368 | = Arrays.asList(new Class<?>[] |
| 369 | { Object.class, String.class, Integer.class, |
| 370 | byte.class, short.class, int.class, long.class, |
| 371 | char.class, float.class, double.class, |
| 372 | boolean.class |
| 373 | }); |
| 374 | static final HashMap<Class<?>,Method> coercers; |
| 375 | static final HashMap<Class<?>,Method> cloners; |
| 376 | static final HashMap<Class<?>,Method> cloneRangers; |
| 377 | static final HashMap<Class<?>,Method> toStrings; |
| 378 | static final HashMap<Class<?>,Object> nullValues; |
| 379 | static { |
| 380 | coercers = new HashMap<Class<?>,Method>(); |
| 381 | Method[] testMethods = CopyMethods.class.getDeclaredMethods(); |
| 382 | Method cia = null, ciar = null; |
| 383 | for (int i = 0; i < testMethods.length; i++) { |
| 384 | Method m = testMethods[i]; |
| 385 | if (!Modifier.isStatic(m.getModifiers())) continue; |
| 386 | Class<?> rt = m.getReturnType(); |
| 387 | if (m.getName().startsWith("coerceTo") && allTypes.contains(rt)) |
| 388 | coercers.put(m.getReturnType(), m); |
| 389 | if (m.getName().equals("copyOfIntegerArray")) |
| 390 | cia = m; |
| 391 | if (m.getName().equals("copyOfIntegerArrayRange")) |
| 392 | ciar = m; |
| 393 | } |
| 394 | Method[] arrayMethods = Arrays.class.getDeclaredMethods(); |
| 395 | cloners = new HashMap<Class<?>,Method>(); |
| 396 | cloneRangers = new HashMap<Class<?>,Method>(); |
| 397 | toStrings = new HashMap<Class<?>,Method>(); |
| 398 | for (int i = 0; i < arrayMethods.length; i++) { |
| 399 | Method m = arrayMethods[i]; |
| 400 | if (!Modifier.isStatic(m.getModifiers())) continue; |
| 401 | Class<?> rt = m.getReturnType(); |
| 402 | if (m.getName().equals("copyOf") |
| 403 | && m.getParameterTypes().length == 2) |
| 404 | cloners.put(rt.getComponentType(), m); |
| 405 | if (m.getName().equals("copyOfRange") |
| 406 | && m.getParameterTypes().length == 3) |
| 407 | cloneRangers.put(rt.getComponentType(), m); |
| 408 | if (m.getName().equals("toString")) { |
| 409 | Class<?> pt = m.getParameterTypes()[0]; |
| 410 | toStrings.put(pt.getComponentType(), m); |
| 411 | } |
| 412 | } |
| 413 | cloners.put(String.class, cloners.get(Object.class)); |
| 414 | cloneRangers.put(String.class, cloneRangers.get(Object.class)); |
| 415 | assert(cia != null); |
| 416 | cloners.put(Integer.class, cia); |
| 417 | assert(ciar != null); |
| 418 | cloneRangers.put(Integer.class, ciar); |
| 419 | nullValues = new HashMap<Class<?>,Object>(); |
| 420 | for (Class<?> c : allTypes) { |
| 421 | nullValues.put(c, invoke(coercers.get(c), 0)); |
| 422 | } |
| 423 | } |
| 424 | } |