|
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 } |