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