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