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