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