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