1 /*
2  * Copyright (c) 2020, 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  * @summary Objects.checkIndex/jdk.internal.util.Preconditions.checkIndex tests for long values
27  * @run testng CheckLongIndex
28  * @modules java.base/jdk.internal.util
29  */
30 package test.java.util.Objects;
31 
32 import jdk.internal.util.Preconditions;
33 import org.testng.annotations.DataProvider;
34 import org.testng.annotations.Test;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Objects;
39 import java.util.function.BiConsumer;
40 import java.util.function.BiFunction;
41 import java.util.function.LongSupplier;
42 
43 import static org.testng.Assert.*;
44 
45 public class CheckLongIndex {
46 
47     static class AssertingOutOfBoundsException extends RuntimeException {
AssertingOutOfBoundsException(String message)48         public AssertingOutOfBoundsException(String message) {
49             super(message);
50         }
51     }
52 
assertingOutOfBounds( String message, String expCheckKind, Long... expArgs)53     static BiFunction<String, List<Number>, AssertingOutOfBoundsException> assertingOutOfBounds(
54             String message, String expCheckKind, Long... expArgs) {
55         return (checkKind, args) -> {
56             assertEquals(checkKind, expCheckKind);
57             assertEquals(args, List.of(expArgs));
58             try {
59                 args.clear();
60                 fail("Out of bounds List<Long> argument should be unmodifiable");
61             } catch (Exception e)  {
62             }
63             return new AssertingOutOfBoundsException(message);
64         };
65     }
66 
assertingOutOfBoundsReturnNull( String expCheckKind, Long... expArgs)67     static BiFunction<String, List<Number>, AssertingOutOfBoundsException> assertingOutOfBoundsReturnNull(
68             String expCheckKind, Long... expArgs) {
69         return (checkKind, args) -> {
70             assertEquals(checkKind, expCheckKind);
71             assertEquals(args, List.of(expArgs));
72             return null;
73         };
74     }
75 
76     static final long[] VALUES = {0, 1, Long.MAX_VALUE - 1, Long.MAX_VALUE, -1, Long.MIN_VALUE + 1, Long.MIN_VALUE};
77 
78     @DataProvider
79     static Object[][] checkIndexProvider() {
80         List<Object[]> l = new ArrayList<>();
81         for (long index : VALUES) {
82             for (long length : VALUES) {
83                 boolean withinBounds = index >= 0 &&
84                                        length >= 0 &&
85                                        index < length;
86                 l.add(new Object[]{index, length, withinBounds});
87             }
88         }
89         return l.toArray(Object[][]::new);
90     }
91 
92     @Test(dataProvider = "checkIndexProvider")
93     public void testCheckIndex(long index, long length, boolean withinBounds) {
94         String expectedMessage = withinBounds
95                                  ? null
96                                  : Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new).
97                 apply("checkIndex", List.of(index, length)).getMessage();
98 
99         BiConsumer<Class<? extends RuntimeException>, LongSupplier> checker = (ec, s) -> {
100             try {
101                 long rIndex = s.getAsLong();
102                 if (!withinBounds)
103                     fail(String.format(
104                             "Index %d is out of bounds of [0, %d), but was reported to be within bounds", index, length));
105                 assertEquals(rIndex, index);
106             }
107             catch (RuntimeException e) {
108                 assertTrue(ec.isInstance(e));
109                 if (withinBounds)
110                     fail(String.format(
111                             "Index %d is within bounds of [0, %d), but was reported to be out of bounds", index, length));
112                 else
113                     assertEquals(e.getMessage(), expectedMessage);
114             }
115         };
116 
117         checker.accept(AssertingOutOfBoundsException.class,
118                      () -> Preconditions.checkIndex(index, length,
119                                                     assertingOutOfBounds(expectedMessage, "checkIndex", index, length)));
120         checker.accept(IndexOutOfBoundsException.class,
121                      () -> Preconditions.checkIndex(index, length,
122                                                     assertingOutOfBoundsReturnNull("checkIndex", index, length)));
123         checker.accept(IndexOutOfBoundsException.class,
124                      () -> Preconditions.checkIndex(index, length, null));
125         checker.accept(IndexOutOfBoundsException.class,
126                      () -> Objects.checkIndex(index, length));
127         checker.accept(ArrayIndexOutOfBoundsException.class,
128                      () -> Preconditions.checkIndex(index, length,
129                                                     Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new)));
130         checker.accept(StringIndexOutOfBoundsException.class,
131                      () -> Preconditions.checkIndex(index, length,
132                                                     Preconditions.outOfBoundsExceptionFormatter(StringIndexOutOfBoundsException::new)));
133     }
134 
135 
136     @DataProvider
137     static Object[][] checkFromToIndexProvider() {
138         List<Object[]> l = new ArrayList<>();
139         for (long fromIndex : VALUES) {
140             for (long toIndex : VALUES) {
141                 for (long length : VALUES) {
142                     boolean withinBounds = fromIndex >= 0 &&
143                                            toIndex >= 0 &&
144                                            length >= 0 &&
145                                            fromIndex <= toIndex &&
146                                            toIndex <= length;
147                     l.add(new Object[]{fromIndex, toIndex, length, withinBounds});
148                 }
149             }
150         }
151         return l.toArray(Object[][]::new);
152     }
153 
154     @Test(dataProvider = "checkFromToIndexProvider")
155     public void testCheckFromToIndex(long fromIndex, long toIndex, long length, boolean withinBounds) {
156         String expectedMessage = withinBounds
157                                  ? null
158                                  : Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new).
159                 apply("checkFromToIndex", List.of(fromIndex, toIndex, length)).getMessage();
160 
161         BiConsumer<Class<? extends RuntimeException>, LongSupplier> check = (ec, s) -> {
162             try {
163                 long rIndex = s.getAsLong();
164                 if (!withinBounds)
165                     fail(String.format(
166                             "Range [%d, %d) is out of bounds of [0, %d), but was reported to be withing bounds", fromIndex, toIndex, length));
167                 assertEquals(rIndex, fromIndex);
168             }
169             catch (RuntimeException e) {
170                 assertTrue(ec.isInstance(e));
171                 if (withinBounds)
172                     fail(String.format(
173                             "Range [%d, %d) is within bounds of [0, %d), but was reported to be out of bounds", fromIndex, toIndex, length));
174                 else
175                     assertEquals(e.getMessage(), expectedMessage);
176             }
177         };
178 
179         check.accept(AssertingOutOfBoundsException.class,
180                      () -> Preconditions.checkFromToIndex(fromIndex, toIndex, length,
181                                                           assertingOutOfBounds(expectedMessage, "checkFromToIndex", fromIndex, toIndex, length)));
182         check.accept(IndexOutOfBoundsException.class,
183                      () -> Preconditions.checkFromToIndex(fromIndex, toIndex, length,
184                                                           assertingOutOfBoundsReturnNull("checkFromToIndex", fromIndex, toIndex, length)));
185         check.accept(IndexOutOfBoundsException.class,
186                      () -> Preconditions.checkFromToIndex(fromIndex, toIndex, length, null));
187         check.accept(IndexOutOfBoundsException.class,
188                      () -> Objects.checkFromToIndex(fromIndex, toIndex, length));
189         check.accept(ArrayIndexOutOfBoundsException.class,
190                      () -> Preconditions.checkFromToIndex(fromIndex, toIndex, length,
191                                                           Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new)));
192         check.accept(StringIndexOutOfBoundsException.class,
193                      () -> Preconditions.checkFromToIndex(fromIndex, toIndex, length,
194                                                           Preconditions.outOfBoundsExceptionFormatter(StringIndexOutOfBoundsException::new)));
195     }
196 
197 
198     @DataProvider
199     static Object[][] checkFromIndexSizeProvider() {
200         List<Object[]> l = new ArrayList<>();
201         for (long fromIndex : VALUES) {
202             for (long size : VALUES) {
203                 for (long length : VALUES) {
204                     long toIndex = fromIndex + size;
205 
206                     boolean withinBounds = fromIndex >= 0L &&
207                                            size >= 0L &&
208                                            length >= 0L &&
209                                            fromIndex <= toIndex && // overflow
210                                            toIndex <= length;
211                     l.add(new Object[]{fromIndex, size, length, withinBounds});
212                 }
213             }
214         }
215         return l.toArray(Object[][]::new);
216     }
217 
218     @Test(dataProvider = "checkFromIndexSizeProvider")
219     public void testCheckFromIndexSize(long fromIndex, long size, long length, boolean withinBounds) {
220         String expectedMessage = withinBounds
221                                  ? null
222                                  : Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new).
223                 apply("checkFromIndexSize", List.of(fromIndex, size, length)).getMessage();
224 
225         BiConsumer<Class<? extends RuntimeException>, LongSupplier> check = (ec, s) -> {
226             try {
227                 long rIndex = s.getAsLong();
228                 if (!withinBounds)
229                     fail(String.format(
230                             "Range [%d, %d + %d) is out of bounds of [0, %d), but was reported to be withing bounds", fromIndex, fromIndex, size, length));
231                 assertEquals(rIndex, fromIndex);
232             }
233             catch (RuntimeException e) {
234                 assertTrue(ec.isInstance(e));
235                 if (withinBounds)
236                     fail(String.format(
237                             "Range [%d, %d + %d) is within bounds of [0, %d), but was reported to be out of bounds", fromIndex, fromIndex, size, length));
238                 else
239                     assertEquals(e.getMessage(), expectedMessage);
240             }
241         };
242 
243         check.accept(AssertingOutOfBoundsException.class,
244                      () -> Preconditions.checkFromIndexSize(fromIndex, size, length,
245                                                             assertingOutOfBounds(expectedMessage, "checkFromIndexSize", fromIndex, size, length)));
246         check.accept(IndexOutOfBoundsException.class,
247                      () -> Preconditions.checkFromIndexSize(fromIndex, size, length,
248                                                             assertingOutOfBoundsReturnNull("checkFromIndexSize", fromIndex, size, length)));
249         check.accept(IndexOutOfBoundsException.class,
250                      () -> Preconditions.checkFromIndexSize(fromIndex, size, length, null));
251         check.accept(IndexOutOfBoundsException.class,
252                      () -> Objects.checkFromIndexSize(fromIndex, size, length));
253         check.accept(ArrayIndexOutOfBoundsException.class,
254                      () -> Preconditions.checkFromIndexSize(fromIndex, size, length,
255                                                             Preconditions.outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new)));
256         check.accept(StringIndexOutOfBoundsException.class,
257                      () -> Preconditions.checkFromIndexSize(fromIndex, size, length,
258                                                             Preconditions.outOfBoundsExceptionFormatter(StringIndexOutOfBoundsException::new)));
259     }
260 
261     @Test
262     public void uniqueMessagesForCheckKinds() {
263         BiFunction<String, List<Number>, IndexOutOfBoundsException> f =
264                 Preconditions.outOfBoundsExceptionFormatter(IndexOutOfBoundsException::new);
265 
266         List<String> messages = new ArrayList<>();
267         // Exact arguments
268         messages.add(f.apply("checkIndex", List.of(-1L, 0L)).getMessage());
269         messages.add(f.apply("checkFromToIndex", List.of(-1L, 0L, 0L)).getMessage());
270         messages.add(f.apply("checkFromIndexSize", List.of(-1L, 0L, 0L)).getMessage());
271         // Unknown check kind
272         messages.add(f.apply("checkUnknown", List.of(-1L, 0L, 0L)).getMessage());
273         // Known check kind with more arguments
274         messages.add(f.apply("checkIndex", List.of(-1L, 0L, 0L)).getMessage());
275         messages.add(f.apply("checkFromToIndex", List.of(-1L, 0L, 0L, 0L)).getMessage());
276         messages.add(f.apply("checkFromIndexSize", List.of(-1L, 0L, 0L, 0L)).getMessage());
277         // Known check kind with fewer arguments
278         messages.add(f.apply("checkIndex", List.of(-1L)).getMessage());
279         messages.add(f.apply("checkFromToIndex", List.of(-1L, 0L)).getMessage());
280         messages.add(f.apply("checkFromIndexSize", List.of(-1L, 0L)).getMessage());
281         // Null arguments
282         messages.add(f.apply(null, null).getMessage());
283         messages.add(f.apply("checkNullArguments", null).getMessage());
284         messages.add(f.apply(null, List.of(-1L)).getMessage());
285 
286         assertEquals(messages.size(), messages.stream().distinct().count());
287     }
288 }
289