blob: 46f56269984e1c84952db6321d6c532e28dc609b [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
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
31import java.util.*;
32import java.lang.reflect.*;
33
34public 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}