1 /* 2 * Copyright (C) 2006 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.base; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkElementIndex; 21 import static com.google.common.base.Preconditions.checkNotNull; 22 import static com.google.common.base.Preconditions.checkPositionIndex; 23 import static com.google.common.base.Preconditions.checkPositionIndexes; 24 import static com.google.common.base.Preconditions.checkState; 25 import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; 26 import static com.google.common.truth.Truth.assertThat; 27 28 import com.google.common.annotations.GwtCompatible; 29 import com.google.common.annotations.GwtIncompatible; 30 import com.google.common.annotations.J2ktIncompatible; 31 import com.google.common.collect.ImmutableList; 32 import com.google.common.collect.ImmutableSet; 33 import com.google.common.collect.Lists; 34 import com.google.common.testing.ArbitraryInstances; 35 import java.lang.reflect.InvocationTargetException; 36 import java.lang.reflect.Method; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.List; 40 import junit.framework.AssertionFailedError; 41 import junit.framework.TestCase; 42 import org.checkerframework.checker.nullness.qual.Nullable; 43 44 /** 45 * Unit test for {@link Preconditions}. 46 * 47 * @author Kevin Bourrillion 48 * @author Jared Levy 49 */ 50 @ElementTypesAreNonnullByDefault 51 @SuppressWarnings("LenientFormatStringValidation") // Intentional for testing 52 @GwtCompatible(emulated = true) 53 public class PreconditionsTest extends TestCase { testCheckArgument_simple_success()54 public void testCheckArgument_simple_success() { 55 checkArgument(true); 56 } 57 testCheckArgument_simple_failure()58 public void testCheckArgument_simple_failure() { 59 assertThrows(IllegalArgumentException.class, () -> checkArgument(false)); 60 } 61 testCheckArgument_simpleMessage_success()62 public void testCheckArgument_simpleMessage_success() { 63 checkArgument(true, IGNORE_ME); 64 } 65 testCheckArgument_simpleMessage_failure()66 public void testCheckArgument_simpleMessage_failure() { 67 IllegalArgumentException expected = 68 assertThrows(IllegalArgumentException.class, () -> checkArgument(false, new Message())); 69 verifySimpleMessage(expected); 70 } 71 testCheckArgument_nullMessage_failure()72 public void testCheckArgument_nullMessage_failure() { 73 IllegalArgumentException expected = 74 assertThrows(IllegalArgumentException.class, () -> checkArgument(false, null)); 75 assertThat(expected).hasMessageThat().isEqualTo("null"); 76 } 77 testCheckArgument_nullMessageWithArgs_failure()78 public void testCheckArgument_nullMessageWithArgs_failure() { 79 IllegalArgumentException e = 80 assertThrows(IllegalArgumentException.class, () -> checkArgument(false, null, "b", "d")); 81 assertThat(e).hasMessageThat().isEqualTo("null [b, d]"); 82 } 83 testCheckArgument_nullArgs_failure()84 public void testCheckArgument_nullArgs_failure() { 85 IllegalArgumentException e = 86 assertThrows( 87 IllegalArgumentException.class, () -> checkArgument(false, "A %s C %s E", null, null)); 88 assertThat(e).hasMessageThat().isEqualTo("A null C null E"); 89 } 90 testCheckArgument_notEnoughArgs_failure()91 public void testCheckArgument_notEnoughArgs_failure() { 92 IllegalArgumentException e = 93 assertThrows( 94 IllegalArgumentException.class, () -> checkArgument(false, "A %s C %s E", "b")); 95 assertThat(e).hasMessageThat().isEqualTo("A b C %s E"); 96 } 97 testCheckArgument_tooManyArgs_failure()98 public void testCheckArgument_tooManyArgs_failure() { 99 IllegalArgumentException e = 100 assertThrows( 101 IllegalArgumentException.class, 102 () -> checkArgument(false, "A %s C %s E", "b", "d", "f")); 103 assertThat(e).hasMessageThat().isEqualTo("A b C d E [f]"); 104 } 105 testCheckArgument_singleNullArg_failure()106 public void testCheckArgument_singleNullArg_failure() { 107 IllegalArgumentException e = 108 assertThrows( 109 IllegalArgumentException.class, () -> checkArgument(false, "A %s C", (Object) null)); 110 assertThat(e).hasMessageThat().isEqualTo("A null C"); 111 } 112 113 @J2ktIncompatible // TODO(b/319404022): Allow passing null array as varargs testCheckArgument_singleNullArray_failure()114 public void testCheckArgument_singleNullArray_failure() { 115 IllegalArgumentException e = 116 assertThrows( 117 IllegalArgumentException.class, () -> checkArgument(false, "A %s C", (Object[]) null)); 118 assertThat(e).hasMessageThat().isEqualTo("A (Object[])null C"); 119 } 120 testCheckArgument_complexMessage_success()121 public void testCheckArgument_complexMessage_success() { 122 checkArgument(true, "%s", IGNORE_ME); 123 } 124 testCheckArgument_complexMessage_failure()125 public void testCheckArgument_complexMessage_failure() { 126 IllegalArgumentException expected = 127 assertThrows(IllegalArgumentException.class, () -> checkArgument(false, FORMAT, 5)); 128 verifyComplexMessage(expected); 129 } 130 testCheckState_simple_success()131 public void testCheckState_simple_success() { 132 checkState(true); 133 } 134 testCheckState_simple_failure()135 public void testCheckState_simple_failure() { 136 assertThrows(IllegalStateException.class, () -> checkState(false)); 137 } 138 testCheckState_simpleMessage_success()139 public void testCheckState_simpleMessage_success() { 140 checkState(true, IGNORE_ME); 141 } 142 testCheckState_simpleMessage_failure()143 public void testCheckState_simpleMessage_failure() { 144 IllegalStateException expected = 145 assertThrows(IllegalStateException.class, () -> checkState(false, new Message())); 146 verifySimpleMessage(expected); 147 } 148 testCheckState_nullMessage_failure()149 public void testCheckState_nullMessage_failure() { 150 IllegalStateException expected = 151 assertThrows(IllegalStateException.class, () -> checkState(false, null)); 152 assertThat(expected).hasMessageThat().isEqualTo("null"); 153 } 154 testCheckState_complexMessage_success()155 public void testCheckState_complexMessage_success() { 156 checkState(true, "%s", IGNORE_ME); 157 } 158 testCheckState_complexMessage_failure()159 public void testCheckState_complexMessage_failure() { 160 IllegalStateException expected = 161 assertThrows(IllegalStateException.class, () -> checkState(false, FORMAT, 5)); 162 verifyComplexMessage(expected); 163 } 164 165 private static final String NON_NULL_STRING = "foo"; 166 testCheckNotNull_simple_success()167 public void testCheckNotNull_simple_success() { 168 String result = checkNotNull(NON_NULL_STRING); 169 assertSame(NON_NULL_STRING, result); 170 } 171 testCheckNotNull_simple_failure()172 public void testCheckNotNull_simple_failure() { 173 assertThrows(NullPointerException.class, () -> checkNotNull(null)); 174 } 175 testCheckNotNull_simpleMessage_success()176 public void testCheckNotNull_simpleMessage_success() { 177 String result = checkNotNull(NON_NULL_STRING, IGNORE_ME); 178 assertSame(NON_NULL_STRING, result); 179 } 180 testCheckNotNull_simpleMessage_failure()181 public void testCheckNotNull_simpleMessage_failure() { 182 NullPointerException expected = 183 assertThrows(NullPointerException.class, () -> checkNotNull(null, new Message())); 184 verifySimpleMessage(expected); 185 } 186 testCheckNotNull_complexMessage_success()187 public void testCheckNotNull_complexMessage_success() { 188 String result = checkNotNull(NON_NULL_STRING, "%s", IGNORE_ME); 189 assertSame(NON_NULL_STRING, result); 190 } 191 testCheckNotNull_complexMessage_failure()192 public void testCheckNotNull_complexMessage_failure() { 193 NullPointerException expected = 194 assertThrows(NullPointerException.class, () -> checkNotNull(null, FORMAT, 5)); 195 verifyComplexMessage(expected); 196 } 197 testCheckElementIndex_ok()198 public void testCheckElementIndex_ok() { 199 assertEquals(0, checkElementIndex(0, 1)); 200 assertEquals(0, checkElementIndex(0, 2)); 201 assertEquals(1, checkElementIndex(1, 2)); 202 } 203 testCheckElementIndex_badSize()204 public void testCheckElementIndex_badSize() { 205 assertThrows(IllegalArgumentException.class, () -> checkElementIndex(1, -1)); 206 } 207 testCheckElementIndex_negative()208 public void testCheckElementIndex_negative() { 209 IndexOutOfBoundsException expected = 210 assertThrows(IndexOutOfBoundsException.class, () -> checkElementIndex(-1, 1)); 211 assertThat(expected).hasMessageThat().isEqualTo("index (-1) must not be negative"); 212 } 213 testCheckElementIndex_tooHigh()214 public void testCheckElementIndex_tooHigh() { 215 IndexOutOfBoundsException expected = 216 assertThrows(IndexOutOfBoundsException.class, () -> checkElementIndex(1, 1)); 217 assertThat(expected).hasMessageThat().isEqualTo("index (1) must be less than size (1)"); 218 } 219 testCheckElementIndex_withDesc_negative()220 public void testCheckElementIndex_withDesc_negative() { 221 IndexOutOfBoundsException expected = 222 assertThrows(IndexOutOfBoundsException.class, () -> checkElementIndex(-1, 1, "foo")); 223 assertThat(expected).hasMessageThat().isEqualTo("foo (-1) must not be negative"); 224 } 225 testCheckElementIndex_withDesc_tooHigh()226 public void testCheckElementIndex_withDesc_tooHigh() { 227 IndexOutOfBoundsException expected = 228 assertThrows(IndexOutOfBoundsException.class, () -> checkElementIndex(1, 1, "foo")); 229 assertThat(expected).hasMessageThat().isEqualTo("foo (1) must be less than size (1)"); 230 } 231 testCheckPositionIndex_ok()232 public void testCheckPositionIndex_ok() { 233 assertEquals(0, checkPositionIndex(0, 0)); 234 assertEquals(0, checkPositionIndex(0, 1)); 235 assertEquals(1, checkPositionIndex(1, 1)); 236 } 237 testCheckPositionIndex_badSize()238 public void testCheckPositionIndex_badSize() { 239 assertThrows(IllegalArgumentException.class, () -> checkPositionIndex(1, -1)); 240 } 241 testCheckPositionIndex_negative()242 public void testCheckPositionIndex_negative() { 243 IndexOutOfBoundsException expected = 244 assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndex(-1, 1)); 245 assertThat(expected).hasMessageThat().isEqualTo("index (-1) must not be negative"); 246 } 247 testCheckPositionIndex_tooHigh()248 public void testCheckPositionIndex_tooHigh() { 249 IndexOutOfBoundsException expected = 250 assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndex(2, 1)); 251 assertThat(expected).hasMessageThat().isEqualTo("index (2) must not be greater than size (1)"); 252 } 253 testCheckPositionIndex_withDesc_negative()254 public void testCheckPositionIndex_withDesc_negative() { 255 IndexOutOfBoundsException expected = 256 assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndex(-1, 1, "foo")); 257 assertThat(expected).hasMessageThat().isEqualTo("foo (-1) must not be negative"); 258 } 259 testCheckPositionIndex_withDesc_tooHigh()260 public void testCheckPositionIndex_withDesc_tooHigh() { 261 IndexOutOfBoundsException expected = 262 assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndex(2, 1, "foo")); 263 assertThat(expected).hasMessageThat().isEqualTo("foo (2) must not be greater than size (1)"); 264 } 265 testCheckPositionIndexes_ok()266 public void testCheckPositionIndexes_ok() { 267 checkPositionIndexes(0, 0, 0); 268 checkPositionIndexes(0, 0, 1); 269 checkPositionIndexes(0, 1, 1); 270 checkPositionIndexes(1, 1, 1); 271 } 272 testCheckPositionIndexes_badSize()273 public void testCheckPositionIndexes_badSize() { 274 assertThrows(IllegalArgumentException.class, () -> checkPositionIndexes(1, 1, -1)); 275 } 276 testCheckPositionIndex_startNegative()277 public void testCheckPositionIndex_startNegative() { 278 IndexOutOfBoundsException expected = 279 assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndexes(-1, 1, 1)); 280 assertThat(expected).hasMessageThat().isEqualTo("start index (-1) must not be negative"); 281 } 282 testCheckPositionIndexes_endTooHigh()283 public void testCheckPositionIndexes_endTooHigh() { 284 IndexOutOfBoundsException expected = 285 assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndexes(0, 2, 1)); 286 assertThat(expected) 287 .hasMessageThat() 288 .isEqualTo("end index (2) must not be greater than size (1)"); 289 } 290 testCheckPositionIndexes_reversed()291 public void testCheckPositionIndexes_reversed() { 292 IndexOutOfBoundsException expected = 293 assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndexes(1, 0, 1)); 294 assertThat(expected) 295 .hasMessageThat() 296 .isEqualTo("end index (0) must not be less than start index (1)"); 297 } 298 299 @GwtIncompatible("Reflection") 300 @J2ktIncompatible testAllOverloads_checkArgument()301 public void testAllOverloads_checkArgument() throws Exception { 302 for (ImmutableList<Class<?>> sig : allSignatures(boolean.class)) { 303 Method checkArgumentMethod = 304 Preconditions.class.getMethod("checkArgument", sig.toArray(new Class<?>[] {})); 305 checkArgumentMethod.invoke(null /* static method */, getParametersForSignature(true, sig)); 306 307 Object[] failingParams = getParametersForSignature(false, sig); 308 InvocationTargetException ite = 309 assertThrows( 310 InvocationTargetException.class, 311 () -> checkArgumentMethod.invoke(null /* static method */, failingParams)); 312 assertFailureCause(ite.getCause(), IllegalArgumentException.class, failingParams); 313 } 314 } 315 316 @GwtIncompatible("Reflection") 317 @J2ktIncompatible testAllOverloads_checkState()318 public void testAllOverloads_checkState() throws Exception { 319 for (ImmutableList<Class<?>> sig : allSignatures(boolean.class)) { 320 Method checkArgumentMethod = 321 Preconditions.class.getMethod("checkState", sig.toArray(new Class<?>[] {})); 322 checkArgumentMethod.invoke(null /* static method */, getParametersForSignature(true, sig)); 323 324 Object[] failingParams = getParametersForSignature(false, sig); 325 InvocationTargetException ite = 326 assertThrows( 327 InvocationTargetException.class, 328 () -> checkArgumentMethod.invoke(null /* static method */, failingParams)); 329 assertFailureCause(ite.getCause(), IllegalStateException.class, failingParams); 330 } 331 } 332 333 @GwtIncompatible("Reflection") 334 @J2ktIncompatible testAllOverloads_checkNotNull()335 public void testAllOverloads_checkNotNull() throws Exception { 336 for (ImmutableList<Class<?>> sig : allSignatures(Object.class)) { 337 Method checkArgumentMethod = 338 Preconditions.class.getMethod("checkNotNull", sig.toArray(new Class<?>[] {})); 339 checkArgumentMethod.invoke( 340 null /* static method */, getParametersForSignature(new Object(), sig)); 341 342 Object[] failingParams = getParametersForSignature(null, sig); 343 InvocationTargetException ite = 344 assertThrows( 345 InvocationTargetException.class, 346 () -> checkArgumentMethod.invoke(null /* static method */, failingParams)); 347 assertFailureCause(ite.getCause(), NullPointerException.class, failingParams); 348 } 349 } 350 351 /** 352 * Asserts that the given throwable has the given class and then asserts on the message as using 353 * the full set of method parameters. 354 */ assertFailureCause( Throwable throwable, Class<? extends Throwable> clazz, Object[] params)355 private void assertFailureCause( 356 Throwable throwable, Class<? extends Throwable> clazz, Object[] params) { 357 assertThat(throwable).isInstanceOf(clazz); 358 if (params.length == 1) { 359 assertThat(throwable).hasMessageThat().isNull(); 360 } else if (params.length == 2) { 361 assertThat(throwable).hasMessageThat().isEmpty(); 362 } else { 363 assertThat(throwable) 364 .hasMessageThat() 365 .isEqualTo(Strings.lenientFormat("", Arrays.copyOfRange(params, 2, params.length))); 366 } 367 } 368 369 /** 370 * Returns an array containing parameters for invoking a checkArgument, checkNotNull or checkState 371 * method reflectively 372 * 373 * @param firstParam The first parameter 374 * @param sig The method signature 375 */ 376 @GwtIncompatible("ArbitraryInstances") 377 @J2ktIncompatible getParametersForSignature( @ullable Object firstParam, ImmutableList<Class<?>> sig)378 private Object[] getParametersForSignature( 379 @Nullable Object firstParam, ImmutableList<Class<?>> sig) { 380 Object[] params = new Object[sig.size()]; 381 params[0] = firstParam; 382 if (params.length > 1) { 383 params[1] = ""; 384 if (params.length > 2) { 385 // fill in the rest of the array with arbitrary instances 386 for (int i = 2; i < params.length; i++) { 387 params[i] = ArbitraryInstances.get(sig.get(i)); 388 } 389 } 390 } 391 return params; 392 } 393 394 private static final ImmutableList<Class<?>> possibleParamTypes = 395 ImmutableList.of(char.class, int.class, long.class, Object.class); 396 397 /** 398 * Returns a list of parameters for invoking an overload of checkState, checkArgument or 399 * checkNotNull 400 * 401 * @param predicateType The first parameter to the method (boolean or Object) 402 */ allSignatures(Class<?> predicateType)403 private static ImmutableList<ImmutableList<Class<?>>> allSignatures(Class<?> predicateType) { 404 ImmutableSet.Builder<ImmutableList<Class<?>>> allOverloads = ImmutableSet.builder(); 405 // The first two are for the overloads that don't take formatting args, e.g. 406 // checkArgument(boolean) and checkArgument(boolean, Object) 407 allOverloads.add(ImmutableList.<Class<?>>of(predicateType)); 408 allOverloads.add(ImmutableList.<Class<?>>of(predicateType, Object.class)); 409 410 List<List<Class<?>>> typesLists = new ArrayList<>(); 411 for (int i = 0; i < 2; i++) { 412 typesLists.add(possibleParamTypes); 413 for (List<Class<?>> curr : Lists.cartesianProduct(typesLists)) { 414 allOverloads.add( 415 ImmutableList.<Class<?>>builder() 416 .add(predicateType) 417 .add(String.class) // the format string 418 .addAll(curr) 419 .build()); 420 } 421 } 422 return allOverloads.build().asList(); 423 } 424 425 // 'test' to demonstrate some potentially ambiguous overloads. This 'test' is kind of strange, 426 // but essentially each line will be a call to a Preconditions method that, but for a documented 427 // change would be a compiler error. 428 // See http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2 for the spec on 429 // how javac selects overloads 430 @SuppressWarnings("null") overloadSelection()431 public void overloadSelection() { 432 Boolean boxedBoolean = null; 433 boolean aBoolean = true; 434 Long boxedLong = null; 435 int anInt = 1; 436 // With a boxed predicate, no overloads can be selected in phase 1 437 // ambiguous without the call to .booleanValue to unbox the Boolean 438 checkState(boxedBoolean.booleanValue(), "", 1); 439 // ambiguous without the cast to Object because the boxed predicate prevents any overload from 440 // being selected in phase 1 441 checkState(boxedBoolean, "", (Object) boxedLong); 442 443 // ternaries introduce their own problems. because of the ternary (which requires a boxing 444 // operation) no overload can be selected in phase 1. and in phase 2 it is ambiguous since it 445 // matches with the second parameter being boxed and without it being boxed. The cast to Object 446 // avoids this. 447 checkState(aBoolean, "", aBoolean ? "" : anInt, (Object) anInt); 448 449 // ambiguous without the .booleanValue() call since the boxing forces us into phase 2 resolution 450 short s = 2; 451 checkState(boxedBoolean.booleanValue(), "", s); 452 } 453 454 @J2ktIncompatible 455 @GwtIncompatible // NullPointerTester testNullPointers()456 public void testNullPointers() { 457 /* 458 * Don't bother testing: Preconditions defines a bunch of methods that accept a template (or 459 * even entire message) that simultaneously: 460 * 461 * - _shouldn't_ be null, so we don't annotate it with @Nullable 462 * 463 * - _can_ be null without causing a runtime failure (because we don't want the interesting 464 * details of precondition failure to be hidden by an exception we throw about an unexpectedly 465 * null _failure message_) 466 * 467 * That combination upsets NullPointerTester, which wants any call that passes null for a 468 * non-@Nullable parameter to trigger a NullPointerException. 469 * 470 * (We still define this empty method to keep PackageSanityTests from generating its own 471 * automated nullness tests, which would fail.) 472 */ 473 } 474 475 private static final Object IGNORE_ME = 476 new Object() { 477 @Override 478 public String toString() { 479 throw new AssertionFailedError(); 480 } 481 }; 482 483 private static class Message { 484 boolean invoked; 485 486 @Override toString()487 public String toString() { 488 assertFalse(invoked); 489 invoked = true; 490 return "A message"; 491 } 492 } 493 494 private static final String FORMAT = "I ate %s pies."; 495 verifySimpleMessage(Exception e)496 private static void verifySimpleMessage(Exception e) { 497 assertThat(e).hasMessageThat().isEqualTo("A message"); 498 } 499 verifyComplexMessage(Exception e)500 private static void verifyComplexMessage(Exception e) { 501 assertThat(e).hasMessageThat().isEqualTo("I ate 5 pies."); 502 } 503 } 504