1 /*
2 * Copyright (c) 2015, 2026, 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 8033148 8141409
27 * @summary tests for array equals and compare
28 * @library /test/lib
29 * @run junit ArraysEqCmpTest
30 */
31
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import java.lang.reflect.Array;
36 import java.util.Arrays;
37 import java.util.Comparator;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Objects;
42 import java.util.function.BiFunction;
43 import java.util.function.LongFunction;
44 import java.util.stream.IntStream;
45
46 import jdk.test.lib.valueclass.AsValueClass;
47
48 import org.junit.jupiter.api.Assertions;
49 import org.junit.jupiter.api.Test;
50 import org.junit.jupiter.api.TestInstance;
51 import org.junit.jupiter.params.ParameterizedTest;
52 import org.junit.jupiter.params.provider.MethodSource;
53
54 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
55 public class ArraysEqCmpTest {
56
57 // Maximum width in bits
58 static final int MAX_WIDTH = 512;
59
60 static final Map<Class, Integer> typeToWidth;
61
62 @AsValueClass
63 record Point(int x, int y) implements Comparable<Point> {
64 @Override
65 public int compareTo(Point o) {
66 int r = Integer.compare(this.x, o.x);
67 return (r != 0) ? r : Integer.compare(this.y, o.y);
68 }
69 }
70
71 static {
72 typeToWidth = new HashMap<>();
73 typeToWidth.put(boolean.class, Byte.SIZE);
74 typeToWidth.put(byte.class, Byte.SIZE);
75 typeToWidth.put(short.class, Short.SIZE);
76 typeToWidth.put(char.class, Character.SIZE);
77 typeToWidth.put(int.class, Integer.SIZE);
78 typeToWidth.put(long.class, Long.SIZE);
79 typeToWidth.put(float.class, Float.SIZE);
80 typeToWidth.put(double.class, Double.SIZE);
81 typeToWidth.put(Object.class, Integer.SIZE); // @@@ 32 or 64?
82 }
83
84 static int arraySizeFor(Class<?> type) {
85 type = type.isPrimitive() ? type : Object.class;
86 return 4 * MAX_WIDTH / typeToWidth.get(type);
87 }
88
89 static abstract class ArrayType<T> {
90 final Class<?> arrayType;
91 final Class<?> componentType;
92 final boolean unsigned;
93
94 final MethodHandle cpy;
95
96 final MethodHandle eq;
97 final MethodHandle eqr;
98 final MethodHandle cmp;
99 final MethodHandle cmpr;
100 final MethodHandle mm;
101 final MethodHandle mmr;
102
103 final MethodHandle getter;
104
105 final MethodHandle toString;
106
107 public ArrayType(Class<T> arrayType) {
108 this(arrayType, false);
109 }
110
111 public ArrayType(Class<T> arrayType, boolean unsigned) {
112 this.arrayType = arrayType;
113 this.componentType = arrayType.getComponentType();
114 this.unsigned = unsigned;
115
116 try {
117 MethodHandles.Lookup l = MethodHandles.lookup();
118
119 getter = MethodHandles.arrayElementGetter(arrayType);
120
121 if (componentType.isPrimitive()) {
122 cpy = l.findStatic(Arrays.class, "copyOfRange",
123 MethodType.methodType(arrayType, arrayType, int.class, int.class));
124
125 MethodType eqt = MethodType.methodType(
126 boolean.class, arrayType, arrayType);
127 MethodType eqrt = MethodType.methodType(
128 boolean.class, arrayType, int.class, int.class, arrayType, int.class, int.class);
129
130 eq = l.findStatic(Arrays.class, "equals", eqt);
131 eqr = l.findStatic(Arrays.class, "equals", eqrt);
132
133 String compareName = unsigned ? "compareUnsigned" : "compare";
134 cmp = l.findStatic(Arrays.class, compareName,
135 eqt.changeReturnType(int.class));
136 cmpr = l.findStatic(Arrays.class, compareName,
137 eqrt.changeReturnType(int.class));
138
139 mm = l.findStatic(Arrays.class, "mismatch",
140 eqt.changeReturnType(int.class));
141 mmr = l.findStatic(Arrays.class, "mismatch",
142 eqrt.changeReturnType(int.class));
143
144 toString = l.findStatic(Arrays.class, "toString",
145 MethodType.methodType(String.class, arrayType));
146 }
147 else {
148 cpy = l.findStatic(Arrays.class, "copyOfRange",
149 MethodType.methodType(Object[].class, Object[].class, int.class, int.class));
150
151 MethodType eqt = MethodType.methodType(
152 boolean.class, Object[].class, Object[].class);
153 MethodType eqrt = MethodType.methodType(
154 boolean.class, Object[].class, int.class, int.class, Object[].class, int.class, int.class);
155
156 eq = l.findStatic(Arrays.class, "equals", eqt);
157 eqr = l.findStatic(Arrays.class, "equals", eqrt);
158
159 MethodType cmpt = MethodType.methodType(
160 int.class, Comparable[].class, Comparable[].class);
161 MethodType cmprt = MethodType.methodType(
162 int.class, Comparable[].class, int.class, int.class, Comparable[].class, int.class, int.class);
163
164 cmp = l.findStatic(Arrays.class, "compare", cmpt);
165 cmpr = l.findStatic(Arrays.class, "compare", cmprt);
166
167 mm = l.findStatic(Arrays.class, "mismatch",
168 eqt.changeReturnType(int.class));
169 mmr = l.findStatic(Arrays.class, "mismatch",
170 eqrt.changeReturnType(int.class));
171
172 toString = l.findStatic(Arrays.class, "toString",
173 MethodType.methodType(String.class, Object[].class));
174 }
175
176 }
177 catch (Exception e) {
178 throw new Error(e);
179 }
180 }
181
182 @Override
183 public String toString() {
184 String s = arrayType.getCanonicalName();
185 return unsigned ? "unsigned " + s : s;
186 }
187
188 Object construct(int length) {
189 return Array.newInstance(componentType, length);
190 }
191
192 Object copyOf(Object a) {
193 return copyOf(a, 0, Array.getLength(a));
194 }
195
196 Object copyOf(Object a, int from, int to) {
197 try {
198 return (Object) cpy.invoke(a, from, to);
199 }
200 catch (RuntimeException | Error e) {
201 throw e;
202 }
203 catch (Throwable t) {
204 throw new Error(t);
205 }
206 }
207
208 Object get(Object a, int i) {
209 try {
210 return (Object) getter.invoke(a, i);
211 }
212 catch (RuntimeException | Error e) {
213 throw e;
214 }
215 catch (Throwable t) {
216 throw new Error(t);
217 }
218 }
219
220 abstract void set(Object a, int i, Object v);
221
222 boolean equals(Object a, Object b) {
223 try {
224 return (boolean) eq.invoke(a, b);
225 }
226 catch (RuntimeException | Error e) {
227 throw e;
228 }
229 catch (Throwable t) {
230 throw new Error(t);
231 }
232 }
233
234 boolean equals(Object a, int aFromIndex, int aToIndex,
235 Object b, int bFromIndex, int bToIndex) {
236 try {
237 return (boolean) eqr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
238 }
239 catch (RuntimeException | Error e) {
240 throw e;
241 }
242 catch (Throwable t) {
243 throw new Error(t);
244 }
245 }
246
247 int compare(Object a, Object b) {
248 try {
249 return (int) cmp.invoke(a, b);
250 }
251 catch (RuntimeException | Error e) {
252 throw e;
253 }
254 catch (Throwable t) {
255 throw new Error(t);
256 }
257 }
258
259 int compare(Object a, int aFromIndex, int aToIndex,
260 Object b, int bFromIndex, int bToIndex) {
261 try {
262 return (int) cmpr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
263 }
264 catch (RuntimeException | Error e) {
265 throw e;
266 }
267 catch (Throwable t) {
268 throw new Error(t);
269 }
270 }
271
272 int mismatch(Object a, Object b) {
273 try {
274 return (int) mm.invoke(a, b);
275 }
276 catch (RuntimeException | Error e) {
277 throw e;
278 }
279 catch (Throwable t) {
280 throw new Error(t);
281 }
282 }
283
284 int mismatch(Object a, int aFromIndex, int aToIndex,
285 Object b, int bFromIndex, int bToIndex) {
286 try {
287 return (int) mmr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
288 }
289 catch (RuntimeException | Error e) {
290 throw e;
291 }
292 catch (Throwable t) {
293 throw new Error(t);
294 }
295 }
296
297 String toString(Object a) {
298 try {
299 return (String) toString.invoke(a);
300 }
301 catch (RuntimeException | Error e) {
302 throw e;
303 }
304 catch (Throwable t) {
305 throw new Error(t);
306 }
307 }
308
309 static class BoxedIntegers extends ArrayType<Integer[]> {
310 public BoxedIntegers() {
311 super(Integer[].class);
312 }
313
314 @Override
315 void set(Object a, int i, Object v) {
316 // Ensure unique reference
317 ((Integer[]) a)[i] = v != null ? new Integer((Integer) v) : null;
318 }
319 }
320
321 static class BoxedIntegersWithReverseComparator extends BoxedIntegers {
322 final Comparator<Integer> c = (a, b) -> {
323 // Nulls sort after non-nulls
324 if (a == null || b == null)
325 return a == null ? b == null ? 0 : 1 : -1;
326
327 return Integer.compare(b, a);
328 };
329
330 final MethodHandle eqc;
331 final MethodHandle eqcr;
332 final MethodHandle cmpc;
333 final MethodHandle cmpcr;
334 final MethodHandle mismatchc;
335 final MethodHandle mismatchcr;
336
337 public BoxedIntegersWithReverseComparator() {
338 try {
339 MethodHandles.Lookup l = MethodHandles.lookup();
340
341 MethodType cmpt = MethodType.methodType(
342 int.class, Object[].class, Object[].class, Comparator.class);
343 MethodType cmprt = MethodType.methodType(
344 int.class, Object[].class, int.class, int.class,
345 Object[].class, int.class, int.class, Comparator.class);
346
347 eqc = l.findStatic(Arrays.class, "equals", cmpt.changeReturnType(boolean.class));
348 eqcr = l.findStatic(Arrays.class, "equals", cmprt.changeReturnType(boolean.class));
349 cmpc = l.findStatic(Arrays.class, "compare", cmpt);
350 cmpcr = l.findStatic(Arrays.class, "compare", cmprt);
351 mismatchc = l.findStatic(Arrays.class, "mismatch", cmpt);
352 mismatchcr = l.findStatic(Arrays.class, "mismatch", cmprt);
353 }
354 catch (Exception e) {
355 throw new Error(e);
356 }
357 }
358
359 @Override
360 boolean equals(Object a, Object b) {
361 try {
362 return (boolean) eqc.invoke(a, b, c);
363 }
364 catch (RuntimeException | Error e) {
365 throw e;
366 }
367 catch (Throwable t) {
368 throw new Error(t);
369 }
370 }
371
372 @Override
373 boolean equals(Object a, int aFromIndex, int aToIndex,
374 Object b, int bFromIndex, int bToIndex) {
375 try {
376 return (boolean) eqcr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex, c);
377 }
378 catch (RuntimeException | Error e) {
379 throw e;
380 }
381 catch (Throwable t) {
382 throw new Error(t);
383 }
384 }
385
386 @Override
387 int compare(Object a, Object b) {
388 try {
389 return (int) cmpc.invoke(a, b, c);
390 }
391 catch (RuntimeException | Error e) {
392 throw e;
393 }
394 catch (Throwable t) {
395 throw new Error(t);
396 }
397 }
398
399 @Override
400 int compare(Object a, int aFromIndex, int aToIndex,
401 Object b, int bFromIndex, int bToIndex) {
402 try {
403 return (int) cmpcr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex, c);
404 }
405 catch (RuntimeException | Error e) {
406 throw e;
407 }
408 catch (Throwable t) {
409 throw new Error(t);
410 }
411 }
412
413 @Override
414 int mismatch(Object a, Object b) {
415 try {
416 return (int) mismatchc.invoke(a, b, c);
417 }
418 catch (RuntimeException | Error e) {
419 throw e;
420 }
421 catch (Throwable t) {
422 throw new Error(t);
423 }
424 }
425
426 @Override
427 int mismatch(Object a, int aFromIndex, int aToIndex,
428 Object b, int bFromIndex, int bToIndex) {
429 try {
430 return (int) mismatchcr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex, c);
431 }
432 catch (RuntimeException | Error e) {
433 throw e;
434 }
435 catch (Throwable t) {
436 throw new Error(t);
437 }
438 }
439
440 @Override
441 public String toString() {
442 return arrayType.getCanonicalName() + " with Comparator";
443 }
444 }
445
446 static class Booleans extends ArrayType<boolean[]> {
447 public Booleans() {
448 super(boolean[].class);
449 }
450
451 @Override
452 void set(Object a, int i, Object v) {
453 boolean pv;
454 if (v instanceof Boolean) {
455 pv = (Boolean) v;
456 }
457 else if (v instanceof Integer) {
458 pv = ((Integer) v) >= 0;
459 }
460 else throw new IllegalStateException();
461
462 ((boolean[]) a)[i] = pv;
463 }
464 }
465
466 static class Bytes extends ArrayType<byte[]> {
467 public Bytes(boolean unsigned) {
468 super(byte[].class, unsigned);
469 }
470
471 @Override
472 void set(Object a, int i, Object v) {
473 byte pv;
474 if (v instanceof Byte) {
475 pv = (Byte) v;
476 }
477 else if (v instanceof Integer) {
478 pv = ((Integer) v).byteValue();
479 }
480 else throw new IllegalStateException();
481
482 ((byte[]) a)[i] = pv;
483 }
484 }
485
486 static class Characters extends ArrayType<char[]> {
487 public Characters() {
488 super(char[].class);
489 }
490
491 @Override
492 void set(Object a, int i, Object v) {
493 char pv;
494 if (v instanceof Character) {
495 pv = (Character) v;
496 }
497 else if (v instanceof Integer) {
498 pv = (char) ((Integer) v).intValue();
499 }
500 else throw new IllegalStateException();
501
502 ((char[]) a)[i] = pv;
503 }
504 }
505
506 static class Shorts extends ArrayType<short[]> {
507 public Shorts(boolean unsigned) {
508 super(short[].class, unsigned);
509 }
510
511 @Override
512 void set(Object a, int i, Object v) {
513 short pv;
514 if (v instanceof Short) {
515 pv = (Short) v;
516 }
517 else if (v instanceof Integer) {
518 pv = ((Integer) v).shortValue();
519 }
520 else throw new IllegalStateException();
521
522 ((short[]) a)[i] = pv;
523 }
524 }
525
526 static class Integers extends ArrayType<int[]> {
527 public Integers(boolean unsigned) {
528 super(int[].class, unsigned);
529 }
530
531 @Override
532 void set(Object a, int i, Object v) {
533 int pv;
534 if (v instanceof Integer) {
535 pv = ((Integer) v).shortValue();
536 }
537 else throw new IllegalStateException();
538
539 ((int[]) a)[i] = pv;
540 }
541 }
542
543 static class Longs extends ArrayType<long[]> {
544 public Longs(boolean unsigned) {
545 super(long[].class, unsigned);
546 }
547
548 @Override
549 void set(Object a, int i, Object v) {
550 long pv;
551 if (v instanceof Long) {
552 pv = (Long) v;
553 }
554 else if (v instanceof Integer) {
555 pv = ((Integer) v).longValue();
556 }
557 else throw new IllegalStateException();
558
559 ((long[]) a)[i] = pv;
560 }
561 }
562
563 static class Floats extends ArrayType<float[]> {
564 public Floats() {
565 super(float[].class);
566 }
567
568 @Override
569 void set(Object a, int i, Object v) {
570 float pv;
571 if (v instanceof Float) {
572 pv = (Float) v;
573 }
574 else if (v instanceof Integer) {
575 pv = ((Integer) v).floatValue();
576 }
577 else throw new IllegalStateException();
578
579 ((float[]) a)[i] = pv;
580 }
581 }
582
583 static class Doubles extends ArrayType<double[]> {
584 public Doubles() {
585 super(double[].class);
586 }
587
588 @Override
589 void set(Object a, int i, Object v) {
590 double pv;
591 if (v instanceof Double) {
592 pv = (Double) v;
593 }
594 else if (v instanceof Integer) {
595 pv = ((Integer) v).doubleValue();
596 }
597 else throw new IllegalStateException();
598
599 ((double[]) a)[i] = pv;
600 }
601 }
602
603 static class ValuePoints extends ArrayType<Point[]> {
604 public ValuePoints() {
605 super(Point[].class);
606 }
607
608 @Override
609 void set(Object a, int i, Object v) {
610 if (v == null) {
611 ((Point[]) a)[i] = null;
612 } else if (v instanceof Point) {
613 ((Point[]) a)[i] = (Point) v;
614 } else if (v instanceof Integer) {
615 int val = (Integer) v;
616 ((Point[]) a)[i] = new Point(val, val);
617 } else throw new IllegalStateException();
618 }
619 }
620 }
621
622 static Object[][] arrayTypes;
623
624 public static Object[][] arrayTypesProvider() {
625 if (arrayTypes == null) {
626 arrayTypes = new Object[][]{
627 new Object[]{new ArrayType.BoxedIntegers()},
628 new Object[]{new ArrayType.BoxedIntegersWithReverseComparator()},
629 new Object[]{new ArrayType.Booleans()},
630 new Object[]{new ArrayType.Bytes(false)},
631 new Object[]{new ArrayType.Bytes(true)},
632 new Object[]{new ArrayType.Characters()},
633 new Object[]{new ArrayType.Shorts(false)},
634 new Object[]{new ArrayType.Shorts(true)},
635 new Object[]{new ArrayType.Integers(false)},
636 new Object[]{new ArrayType.Integers(true)},
637 new Object[]{new ArrayType.Longs(false)},
638 new Object[]{new ArrayType.Longs(true)},
639 new Object[]{new ArrayType.Floats()},
640 new Object[]{new ArrayType.Doubles()},
641 new Object[]{new ArrayType.ValuePoints()}
642 };
643 }
644 return arrayTypes;
645 }
646
647 static Object[][] floatArrayTypes;
648
649 public static Object[][] floatArrayTypesProvider() {
650 if (floatArrayTypes == null) {
651 LongFunction<Object> bTof = rb -> Float.intBitsToFloat((int) rb);
652 LongFunction<Object> bToD = Double::longBitsToDouble;
653
654 floatArrayTypes = new Object[][]{
655 new Object[]{new ArrayType.Floats(), 0x7fc00000L, 0x7f800001L, bTof},
656 new Object[]{new ArrayType.Doubles(), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD},
657 };
658 }
659 return floatArrayTypes;
660 }
661
662 static Object[][] objectArrayTypes;
663
664 public static Object[][] objectArrayTypesProvider() {
665 if (objectArrayTypes == null) {
666 LongFunction<Object> bTof = rb -> Float.intBitsToFloat((int) rb);
667 LongFunction<Object> bToD = Double::longBitsToDouble;
668
669 objectArrayTypes = new Object[][]{
670 new Object[]{new ArrayType.BoxedIntegers()},
671 new Object[]{new ArrayType.BoxedIntegersWithReverseComparator()},
672 new Object[]{new ArrayType.ValuePoints()},
673 };
674 }
675 return objectArrayTypes;
676 }
677
678
679 static Object[][] signedUnsignedArrayTypes;
680
681 public static Object[][] signedUnsignedArrayTypes() {
682 if (signedUnsignedArrayTypes == null) {
683 signedUnsignedArrayTypes = new Object[][]{
684 new Object[]{new ArrayType.Bytes(false), new ArrayType.Bytes(true)},
685 new Object[]{new ArrayType.Shorts(false), new ArrayType.Shorts(true)},
686 new Object[]{new ArrayType.Integers(false), new ArrayType.Integers(true)},
687 new Object[]{new ArrayType.Longs(false), new ArrayType.Longs(true)},
688 };
689 }
690 return signedUnsignedArrayTypes;
691 }
692
693 // Equality and comparison tests
694
695 @ParameterizedTest
696 @MethodSource("arrayTypesProvider")
697 public void testArray(ArrayType<?> arrayType) {
698 BiFunction<ArrayType<?>, Integer, Object> constructor = (at, s) -> {
699 Object a = at.construct(s);
700 for (int x = 0; x < s; x++) {
701 at.set(a, x, x % 8);
702 }
703 return a;
704 };
705
706 BiFunction<ArrayType<?>, Object, Object> cloner = (at, a) ->
707 constructor.apply(at, Array.getLength(a));
708
709 testArrayType(arrayType, constructor, cloner);
710 }
711
712 @ParameterizedTest
713 @MethodSource("floatArrayTypesProvider")
714 public void testPrimitiveFloatArray(
715 ArrayType<?> arrayType,
716 long canonicalNanRawBits, long nonCanonicalNanRawBits,
717 LongFunction<Object> bitsToFloat) {
718 Object canonicalNan = bitsToFloat.apply(canonicalNanRawBits);
719 // If conversion is a signalling NaN it may be subject to conversion to a
720 // quiet NaN on some processors, even if a copy is performed
721 // The tests assume that if conversion occurs it does not convert to the
722 // canonical NaN
723 Object nonCanonicalNan = bitsToFloat.apply(nonCanonicalNanRawBits);
724
725 BiFunction<ArrayType<?>, Integer, Object> canonicalNaNs = (at, s) -> {
726 Object a = at.construct(s);
727 for (int x = 0; x < s; x++) {
728 at.set(a, x, canonicalNan);
729 }
730 return a;
731 };
732
733 BiFunction<ArrayType<?>, Object, Object> nonCanonicalNaNs = (at, a) -> {
734 int s = Array.getLength(a);
735 Object ac = at.construct(s);
736 for (int x = 0; x < s; x++) {
737 at.set(ac, x, nonCanonicalNan);
738 }
739 return ac;
740 };
741
742 BiFunction<ArrayType<?>, Object, Object> halfNonCanonicalNaNs = (at, a) -> {
743 int s = Array.getLength(a);
744 Object ac = at.construct(s);
745 for (int x = 0; x < s / 2; x++) {
746 at.set(ac, x, nonCanonicalNan);
747 }
748 for (int x = s / 2; x < s; x++) {
749 at.set(ac, x, 1);
750 }
751 return ac;
752 };
753
754 testArrayType(arrayType, canonicalNaNs, nonCanonicalNaNs);
755 testArrayType(arrayType, canonicalNaNs, halfNonCanonicalNaNs);
756 }
757
758 @ParameterizedTest
759 @MethodSource("objectArrayTypesProvider")
760 public void testNullElementsInObjectArray(ArrayType<?> arrayType) {
761 BiFunction<ArrayType<?>, Object, Object> cloner = ArrayType::copyOf;
762
763 // All nulls
764 testArrayType(arrayType,
765 (at, s) -> {
766 Object a = at.construct(s);
767 for (int x = 0; x < s; x++) {
768 at.set(a, x, null);
769 }
770 return a;
771 },
772 cloner);
773
774
775 // Some nulls
776 testArrayType(arrayType,
777 (at, s) -> {
778 Object a = at.construct(s);
779 for (int x = 0; x < s; x++) {
780 int v = x % 8;
781 at.set(a, x, v == 0 ? null : v);
782 }
783 return a;
784 },
785 cloner);
786
787 Integer[] a = new Integer[]{null, 0};
788 Integer[] b = new Integer[]{0, 0};
789 Assertions.assertTrue(Arrays.compare(a, b) < 0);
790 Assertions.assertTrue(Arrays.compare(b, a) > 0);
791 }
792
793 @ParameterizedTest
794 @MethodSource("objectArrayTypesProvider")
795 public void testSameRefElementsInObjectArray(ArrayType<?> arrayType) {
796 BiFunction<ArrayType<?>, Object, Object> cloner = ArrayType::copyOf;
797
798 // One ref
799 Integer one = 1;
800 testArrayType(arrayType,
801 (at, s) -> {
802 Object a = at.construct(s);
803 for (int x = 0; x < s; x++) {
804 at.set(a, x, one);
805 }
806 return a;
807 },
808 cloner);
809
810 // All ref
811 testArrayType(arrayType,
812 (at, s) -> {
813 Object a = at.construct(s);
814 for (int x = 0; x < s; x++) {
815 at.set(a, x, s);
816 }
817 return a;
818 },
819 cloner);
820
821 // Some same ref
822 testArrayType(arrayType,
823 (at, s) -> {
824 Object a = at.construct(s);
825 for (int x = 0; x < s; x++) {
826 int v = x % 8;
827 at.set(a, x, v == 1 ? one : v);
828 }
829 return a;
830 },
831 cloner);
832 }
833
834 @ParameterizedTest
835 @MethodSource("signedUnsignedArrayTypes")
836 public void testSignedUnsignedArray(ArrayType<?> sat, ArrayType<?> uat) {
837 BiFunction<ArrayType<?>, Integer, Object> constructor = (at, s) -> {
838 Object a = at.construct(s);
839 for (int x = 0; x < s; x++) {
840 at.set(a, x, 1);
841 }
842 return a;
843 };
844
845 int n = arraySizeFor(sat.componentType);
846
847 for (int s : ranges(0, n)) {
848 Object a = constructor.apply(sat, s);
849
850 for (int aFrom : ranges(0, s)) {
851 for (int aTo : ranges(aFrom, s)) {
852 int aLength = aTo - aFrom;
853
854 if (aLength > 0) {
855 for (int i = aFrom; i < aTo; i++) {
856 Object ac = sat.copyOf(a);
857 // Create common prefix with a length of i - aFrom
858 sat.set(ac, i, -1);
859
860 int sc = sat.compare(ac, aFrom, aTo, a, aFrom, aTo);
861 int uc = uat.compare(ac, aFrom, aTo, a, aFrom, aTo);
862
863 Assertions.assertTrue(sc < 0);
864 Assertions.assertTrue(uc > 0);
865 }
866 }
867 }
868 }
869 }
870 }
871
872 void testArrayType(ArrayType<?> at,
873 BiFunction<ArrayType<?>, Integer, Object> constructor,
874 BiFunction<ArrayType<?>, Object, Object> cloner) {
875 int n = arraySizeFor(at.componentType);
876
877 for (int s : ranges(0, n)) {
878 Object a = constructor.apply(at, s);
879 Object b = cloner.apply(at, a);
880
881 for (int aFrom : ranges(0, s)) {
882 for (int aTo : ranges(aFrom, s)) {
883 int aLength = aTo - aFrom;
884
885 for (int bFrom : ranges(0, s)) {
886 for (int bTo : ranges(bFrom, s)) {
887 int bLength = bTo - bFrom;
888
889 Object anr = at.copyOf(a, aFrom, aTo);
890 Object bnr = at.copyOf(b, bFrom, bTo);
891
892 boolean eq = isEqual(at, a, aFrom, aTo, b, bFrom, bTo);
893 Assertions.assertEquals(eq, at.equals(a, aFrom, aTo, b, bFrom, bTo));
894 Assertions.assertEquals(eq, at.equals(b, bFrom, bTo, a, aFrom, aTo));
895 Assertions.assertEquals(eq, at.equals(anr, bnr));
896 Assertions.assertEquals(eq, at.equals(bnr, anr));
897 if (eq) {
898 Assertions.assertEquals(0, at.compare(a, aFrom, aTo, b, bFrom, bTo));
899 Assertions.assertEquals(0, at.compare(b, bFrom, bTo, a, aFrom, aTo));
900 Assertions.assertEquals(0, at.compare(anr, bnr));
901 Assertions.assertEquals(0, at.compare(bnr, anr));
902
903 Assertions.assertEquals(-1, at.mismatch(a, aFrom, aTo, b, bFrom, bTo));
904 Assertions.assertEquals(-1, at.mismatch(b, bFrom, bTo, a, aFrom, aTo));
905 Assertions.assertEquals(-1, at.mismatch(anr, bnr));
906 Assertions.assertEquals(-1, at.mismatch(bnr, anr));
907 }
908 else {
909 int aCb = at.compare(a, aFrom, aTo, b, bFrom, bTo);
910 int bCa = at.compare(b, bFrom, bTo, a, aFrom, aTo);
911 int v = Integer.signum(aCb) * Integer.signum(bCa);
912 Assertions.assertTrue(v == -1);
913
914 int anrCbnr = at.compare(anr, bnr);
915 int bnrCanr = at.compare(bnr, anr);
916 Assertions.assertEquals(aCb, anrCbnr);
917 Assertions.assertEquals(bCa, bnrCanr);
918
919
920 int aMb = at.mismatch(a, aFrom, aTo, b, bFrom, bTo);
921 int bMa = at.mismatch(b, bFrom, bTo, a, aFrom, aTo);
922 int anrMbnr = at.mismatch(anr, bnr);
923 int bnrManr = at.mismatch(bnr, anr);
924
925 Assertions.assertNotEquals(-1, aMb);
926 Assertions.assertEquals(bMa, aMb);
927 Assertions.assertNotEquals(-1, anrMbnr);
928 Assertions.assertEquals(bnrManr, anrMbnr);
929 Assertions.assertEquals(anrMbnr, aMb);
930 Assertions.assertEquals(bnrManr, bMa);
931
932 // Common or proper prefix
933 Assertions.assertTrue(at.equals(a, aFrom, aFrom + aMb, b, bFrom, bFrom + aMb));
934 if (aMb < Math.min(aLength, bLength)) {
935 // Common prefix
936 Assertions.assertFalse(isEqual(at, a, aFrom + aMb, b, bFrom + aMb));
937 }
938 }
939 }
940 }
941
942 if (aLength > 0) {
943 for (int i = aFrom; i < aTo; i++) {
944 Object ac = at.copyOf(a);
945 // Create common prefix with a length of i - aFrom
946 at.set(ac, i, -1);
947
948 Object acnr = at.copyOf(ac, aFrom, aTo);
949 Object anr = at.copyOf(a, aFrom, aTo);
950
951 Assertions.assertFalse(at.equals(ac, aFrom, aTo, a, aFrom, aTo));
952 Assertions.assertFalse(at.equals(acnr, anr));
953
954 int acCa = at.compare(ac, aFrom, aTo, a, aFrom, aTo);
955 int aCac = at.compare(a, aFrom, aTo, ac, aFrom, aTo);
956 int v = Integer.signum(acCa) * Integer.signum(aCac);
957 Assertions.assertTrue(v == -1);
958
959 int acnrCanr = at.compare(acnr, anr);
960 int anrCacnr = at.compare(anr, acnr);
961 Assertions.assertEquals(acCa, acnrCanr);
962 Assertions.assertEquals(aCac, anrCacnr);
963
964
965 int acMa = at.mismatch(ac, aFrom, aTo, a, aFrom, aTo);
966 int aMac = at.mismatch(a, aFrom, aTo, ac, aFrom, aTo);
967 Assertions.assertEquals(aMac, acMa);
968 Assertions.assertEquals(i - aFrom, acMa);
969
970 int acnrManr = at.mismatch(acnr, anr);
971 int anrMacnr = at.mismatch(anr, acnr);
972 Assertions.assertEquals(anrMacnr, acnrManr);
973 Assertions.assertEquals(i - aFrom, acnrManr);
974 }
975 }
976 }
977 }
978 }
979 }
980
981 static boolean isEqual(ArrayType<?> at, Object a, int aFromIndex, int aToIndex,
982 Object b, int bFromIndex, int bToIndex) {
983 int aLength = aToIndex - aFromIndex;
984 int bLength = bToIndex - bFromIndex;
985 if (aLength != bLength)
986 return false;
987
988 for (int i = 0; i < aLength; i++) {
989 Object av = at.get(a, aFromIndex++);
990 Object bv = at.get(b, bFromIndex++);
991 if (!Objects.equals(av, bv)) return false;
992 }
993
994 return true;
995 }
996
997 static boolean isEqual(ArrayType<?> at, Object a, int aFrom, Object b, int bFrom) {
998 Object av = at.get(a, aFrom);
999 Object bv = at.get(b, bFrom);
1000
1001 return Objects.equals(av, bv);
1002 }
1003
1004 static int[] ranges(int from, int to) {
1005 int width = to - from;
1006 switch (width) {
1007 case 0:
1008 return new int[]{};
1009 case 1:
1010 return new int[]{from, to};
1011 case 2:
1012 return new int[]{from, from + 1, to};
1013 case 3:
1014 return new int[]{from, from + 1, from + 2, to};
1015 default:
1016 return IntStream.of(from, from + 1, from + 2, to / 2 - 1, to / 2, to / 2 + 1, to - 2, to - 1, to)
1017 .filter(i -> i >= from && i <= to)
1018 .distinct().toArray();
1019 }
1020 }
1021
1022
1023 // Null array reference tests
1024
1025 @ParameterizedTest
1026 @MethodSource("arrayTypesProvider")
1027 public void testNullArrayRefs(ArrayType<?> arrayType) {
1028 Object n = null;
1029 Object a = arrayType.construct(0);
1030
1031 Assertions.assertTrue(arrayType.equals(n, n));
1032 Assertions.assertFalse(arrayType.equals(n, a));
1033 Assertions.assertFalse(arrayType.equals(a, n));
1034
1035 Assertions.assertEquals(0, arrayType.compare(n, n));
1036 Assertions.assertTrue(arrayType.compare(n, a) < 0);
1037 Assertions.assertTrue(arrayType.compare(a, n) > 0);
1038 }
1039
1040
1041 // Exception throwing tests
1042
1043 @ParameterizedTest
1044 @MethodSource("arrayTypesProvider")
1045 public void testNPEs(ArrayType<?> arrayType) {
1046 Object[] values = new Object[]{null, arrayType.construct(0)};
1047
1048 for (Object o1 : values) {
1049 for (Object o2 : values) {
1050 if (o1 != null && o2 != null)
1051 continue;
1052
1053 testNPE(() -> arrayType.equals(o1, 0, 0, o2, 0, 0));
1054 testNPE(() -> arrayType.compare(o1, 0, 0, o2, 0, 0));
1055 testNPE(() -> arrayType.mismatch(o1, o2));
1056 testNPE(() -> arrayType.mismatch(o1, 0, 0, o2, 0, 0));
1057 }
1058 }
1059 }
1060
1061 @Test
1062 public void testObjectNPEs() {
1063 String[][] values = new String[][]{null, new String[0]};
1064 Comparator<String> c = String::compareTo;
1065 Comparator[] cs = new Comparator[]{null, c};
1066
1067 for (String[] o1 : values) {
1068 for (String[] o2 : values) {
1069 for (Comparator o3 : cs) {
1070 if (o1 != null && o2 != null && o3 != null)
1071 continue;
1072
1073 if (o3 == null) {
1074 testNPE(() -> Arrays.equals(o1, o2, o3));
1075 testNPE(() -> Arrays.compare(o1, o2, o3));
1076 testNPE(() -> Arrays.mismatch(o1, o2, o3));
1077 }
1078
1079 testNPE(() -> Arrays.equals(o1, 0, 0, o2, 0, 0, o3));
1080 testNPE(() -> Arrays.compare(o1, 0, 0, o2, 0, 0, o3));
1081 testNPE(() -> Arrays.mismatch(o1, 0, 0, o2, 0, 0, o3));
1082 }
1083 }
1084 }
1085 }
1086
1087 @ParameterizedTest
1088 @MethodSource("arrayTypesProvider")
1089 public void testIAEs(ArrayType<?> arrayType) {
1090 List<Integer> values = Arrays.asList(0, 1);
1091
1092 for (int s : values) {
1093 Object a = arrayType.construct(s);
1094
1095 for (int o1 : values) {
1096 for (int o2 : values) {
1097 if (o1 <= o2) continue;
1098
1099 testIAE(() -> arrayType.equals(a, o1, 0, a, o2, 0));
1100 testIAE(() -> arrayType.compare(a, o1, 0, a, o2, 0));
1101 testIAE(() -> arrayType.mismatch(a, o1, 0, a, o2, 0));
1102 }
1103 }
1104 }
1105 }
1106
1107 @ParameterizedTest
1108 @MethodSource("arrayTypesProvider")
1109 public void testAIOBEs(ArrayType<?> arrayType) {
1110 List<Integer> froms = Arrays.asList(-1, 0);
1111
1112 for (int s : Arrays.asList(0, 1)) {
1113 List<Integer> tos = Arrays.asList(s, s + 1);
1114 Object a = arrayType.construct(s);
1115
1116 for (int aFrom : froms) {
1117 for (int aTo : tos) {
1118 for (int bFrom : froms) {
1119 for (int bTo : tos) {
1120 if (aFrom >= 0 && aTo <= s &&
1121 bFrom >= 0 && bTo <= s) continue;
1122
1123 testAIOBE(() -> arrayType.equals(a, aFrom, aTo, a, bFrom, bTo));
1124 testAIOBE(() -> arrayType.compare(a, aFrom, aTo, a, bFrom, bTo));
1125 testAIOBE(() -> arrayType.mismatch(a, aFrom, aTo, a, bFrom, bTo));
1126 }
1127 }
1128 }
1129 }
1130 }
1131 }
1132
1133 static void testNPE(Runnable r) {
1134 testThrowable(r, NullPointerException.class);
1135 }
1136
1137 static void testIAE(Runnable r) {
1138 testThrowable(r, IllegalArgumentException.class);
1139 }
1140
1141 static void testAIOBE(Runnable r) {
1142 testThrowable(r, ArrayIndexOutOfBoundsException.class);
1143 }
1144
1145 static void testThrowable(Runnable r, Class<? extends Throwable> expected) {
1146 Throwable caught = null;
1147 try {
1148 r.run();
1149 }
1150 catch (Throwable t) {
1151 caught = t;
1152 }
1153 Assertions.assertNotNull(caught);
1154 Assertions.assertTrue(expected.isInstance(caught));
1155 }
1156 }