001/* 002 * Copyright (C) 2012 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.testing; 018 019import static com.google.common.base.Preconditions.checkArgument; 020import static com.google.common.base.Preconditions.checkNotNull; 021import static com.google.common.base.Throwables.throwIfUnchecked; 022import static com.google.common.testing.NullPointerTester.isNullable; 023 024import com.google.common.annotations.GwtIncompatible; 025import com.google.common.annotations.J2ktIncompatible; 026import com.google.common.annotations.VisibleForTesting; 027import com.google.common.base.Joiner; 028import com.google.common.base.Objects; 029import com.google.common.collect.ArrayListMultimap; 030import com.google.common.collect.ImmutableList; 031import com.google.common.collect.ListMultimap; 032import com.google.common.collect.Lists; 033import com.google.common.collect.MutableClassToInstanceMap; 034import com.google.common.collect.Ordering; 035import com.google.common.collect.Sets; 036import com.google.common.primitives.Ints; 037import com.google.common.reflect.Invokable; 038import com.google.common.reflect.Parameter; 039import com.google.common.reflect.Reflection; 040import com.google.common.reflect.TypeToken; 041import com.google.common.testing.NullPointerTester.Visibility; 042import com.google.common.testing.RelationshipTester.Item; 043import com.google.common.testing.RelationshipTester.ItemReporter; 044import com.google.errorprone.annotations.CanIgnoreReturnValue; 045import java.io.Serializable; 046import java.lang.reflect.Constructor; 047import java.lang.reflect.InvocationTargetException; 048import java.lang.reflect.Method; 049import java.lang.reflect.Modifier; 050import java.util.Collection; 051import java.util.List; 052import java.util.Map.Entry; 053import java.util.Set; 054import junit.framework.Assert; 055import junit.framework.AssertionFailedError; 056import org.checkerframework.checker.nullness.qual.Nullable; 057 058/** 059 * Tester that runs automated sanity tests for any given class. A typical use case is to test static 060 * factory classes like: 061 * 062 * <pre> 063 * interface Book {...} 064 * public class Books { 065 * public static Book hardcover(String title) {...} 066 * public static Book paperback(String title) {...} 067 * } 068 * </pre> 069 * 070 * <p>And all the created {@code Book} instances can be tested with: 071 * 072 * <pre> 073 * new ClassSanityTester() 074 * .forAllPublicStaticMethods(Books.class) 075 * .thatReturn(Book.class) 076 * .testEquals(); // or testNulls(), testSerializable() etc. 077 * </pre> 078 * 079 * @author Ben Yu 080 * @since 14.0 081 */ 082@GwtIncompatible 083@J2ktIncompatible 084public final class ClassSanityTester { 085 086 private static final Ordering<Invokable<?, ?>> BY_METHOD_NAME = 087 new Ordering<Invokable<?, ?>>() { 088 @Override 089 public int compare(Invokable<?, ?> left, Invokable<?, ?> right) { 090 return left.getName().compareTo(right.getName()); 091 } 092 }; 093 094 private static final Ordering<Invokable<?, ?>> BY_PARAMETERS = 095 new Ordering<Invokable<?, ?>>() { 096 @Override 097 public int compare(Invokable<?, ?> left, Invokable<?, ?> right) { 098 return Ordering.usingToString().compare(left.getParameters(), right.getParameters()); 099 } 100 }; 101 102 private static final Ordering<Invokable<?, ?>> BY_NUMBER_OF_PARAMETERS = 103 new Ordering<Invokable<?, ?>>() { 104 @Override 105 public int compare(Invokable<?, ?> left, Invokable<?, ?> right) { 106 return Ints.compare(left.getParameters().size(), right.getParameters().size()); 107 } 108 }; 109 110 private final MutableClassToInstanceMap<Object> defaultValues = 111 MutableClassToInstanceMap.create(); 112 private final ListMultimap<Class<?>, Object> distinctValues = ArrayListMultimap.create(); 113 private final NullPointerTester nullPointerTester = new NullPointerTester(); 114 115 public ClassSanityTester() { 116 // TODO(benyu): bake these into ArbitraryInstances. 117 setDefault(byte.class, (byte) 1); 118 setDefault(Byte.class, (byte) 1); 119 setDefault(short.class, (short) 1); 120 setDefault(Short.class, (short) 1); 121 setDefault(int.class, 1); 122 setDefault(Integer.class, 1); 123 setDefault(long.class, 1L); 124 setDefault(Long.class, 1L); 125 setDefault(float.class, 1F); 126 setDefault(Float.class, 1F); 127 setDefault(double.class, 1D); 128 setDefault(Double.class, 1D); 129 setDefault(Class.class, Class.class); 130 } 131 132 /** 133 * Sets the default value for {@code type}. The default value isn't used in testing {@link 134 * Object#equals} because more than one sample instances are needed for testing inequality. To set 135 * distinct values for equality testing, use {@link #setDistinctValues} instead. 136 */ 137 @CanIgnoreReturnValue 138 public <T> ClassSanityTester setDefault(Class<T> type, T value) { 139 nullPointerTester.setDefault(type, value); 140 defaultValues.putInstance(type, value); 141 return this; 142 } 143 144 /** 145 * Sets distinct values for {@code type}, so that when a class {@code Foo} is tested for {@link 146 * Object#equals} and {@link Object#hashCode}, and its construction requires a parameter of {@code 147 * type}, the distinct values of {@code type} can be passed as parameters to create {@code Foo} 148 * instances that are unequal. 149 * 150 * <p>Calling {@code setDistinctValues(type, v1, v2)} also sets the default value for {@code type} 151 * that's used for {@link #testNulls}. 152 * 153 * <p>Only necessary for types where {@link ClassSanityTester} doesn't already know how to create 154 * distinct values. 155 * 156 * @return this tester instance 157 * @since 17.0 158 */ 159 @CanIgnoreReturnValue 160 public <T> ClassSanityTester setDistinctValues(Class<T> type, T value1, T value2) { 161 checkNotNull(type); 162 checkNotNull(value1); 163 checkNotNull(value2); 164 checkArgument(!Objects.equal(value1, value2), "Duplicate value provided."); 165 distinctValues.replaceValues(type, ImmutableList.of(value1, value2)); 166 setDefault(type, value1); 167 return this; 168 } 169 170 /** 171 * Tests that {@code cls} properly checks null on all constructor and method parameters that 172 * aren't annotated nullable (according to the rules of {@link NullPointerTester}). In details: 173 * 174 * <ul> 175 * <li>All non-private static methods are checked such that passing null for any parameter 176 * that's not annotated nullable should throw {@link NullPointerException}. 177 * <li>If there is any non-private constructor or non-private static factory method declared by 178 * {@code cls}, all non-private instance methods will be checked too using the instance 179 * created by invoking the constructor or static factory method. 180 * <li>If there is any non-private constructor or non-private static factory method declared by 181 * {@code cls}: 182 * <ul> 183 * <li>Test will fail if default value for a parameter cannot be determined. 184 * <li>Test will fail if the factory method returns null so testing instance methods is 185 * impossible. 186 * <li>Test will fail if the constructor or factory method throws exception. 187 * </ul> 188 * <li>If there is no non-private constructor or non-private static factory method declared by 189 * {@code cls}, instance methods are skipped for nulls test. 190 * <li>Nulls test is not performed on method return values unless the method is a non-private 191 * static factory method whose return type is {@code cls} or {@code cls}'s subtype. 192 * </ul> 193 */ 194 public void testNulls(Class<?> cls) { 195 try { 196 doTestNulls(cls, Visibility.PACKAGE); 197 } catch (Exception e) { 198 throwIfUnchecked(e); 199 throw new RuntimeException(e); 200 } 201 } 202 203 void doTestNulls(Class<?> cls, Visibility visibility) 204 throws ParameterNotInstantiableException, IllegalAccessException, InvocationTargetException, 205 FactoryMethodReturnsNullException { 206 if (!Modifier.isAbstract(cls.getModifiers())) { 207 nullPointerTester.testConstructors(cls, visibility); 208 } 209 nullPointerTester.testStaticMethods(cls, visibility); 210 if (hasInstanceMethodToTestNulls(cls, visibility)) { 211 Object instance = instantiate(cls); 212 if (instance != null) { 213 nullPointerTester.testInstanceMethods(instance, visibility); 214 } 215 } 216 } 217 218 private boolean hasInstanceMethodToTestNulls(Class<?> c, Visibility visibility) { 219 for (Method method : nullPointerTester.getInstanceMethodsToTest(c, visibility)) { 220 for (Parameter param : Invokable.from(method).getParameters()) { 221 if (!NullPointerTester.isPrimitiveOrNullable(param)) { 222 return true; 223 } 224 } 225 } 226 return false; 227 } 228 229 /** 230 * Tests the {@link Object#equals} and {@link Object#hashCode} of {@code cls}. In details: 231 * 232 * <ul> 233 * <li>The non-private constructor or non-private static factory method with the most parameters 234 * is used to construct the sample instances. In case of tie, the candidate constructors or 235 * factories are tried one after another until one can be used to construct sample 236 * instances. 237 * <li>For the constructor or static factory method used to construct instances, it's checked 238 * that when equal parameters are passed, the result instance should also be equal; and vice 239 * versa. 240 * <li>If a non-private constructor or non-private static factory method exists: 241 * <ul> 242 * <li>Test will fail if default value for a parameter cannot be determined. 243 * <li>Test will fail if the factory method returns null so testing instance methods is 244 * impossible. 245 * <li>Test will fail if the constructor or factory method throws exception. 246 * </ul> 247 * <li>If there is no non-private constructor or non-private static factory method declared by 248 * {@code cls}, no test is performed. 249 * <li>Equality test is not performed on method return values unless the method is a non-private 250 * static factory method whose return type is {@code cls} or {@code cls}'s subtype. 251 * <li>Inequality check is not performed against state mutation methods such as {@link 252 * List#add}, or functional update methods such as {@link 253 * com.google.common.base.Joiner#skipNulls}. 254 * </ul> 255 * 256 * <p>Note that constructors taking a builder object cannot be tested effectively because 257 * semantics of builder can be arbitrarily complex. Still, a factory class can be created in the 258 * test to facilitate equality testing. For example: 259 * 260 * <pre> 261 * public class FooTest { 262 * 263 * private static class FooFactoryForTest { 264 * public static Foo create(String a, String b, int c, boolean d) { 265 * return Foo.builder() 266 * .setA(a) 267 * .setB(b) 268 * .setC(c) 269 * .setD(d) 270 * .build(); 271 * } 272 * } 273 * 274 * public void testEquals() { 275 * new ClassSanityTester() 276 * .forAllPublicStaticMethods(FooFactoryForTest.class) 277 * .thatReturn(Foo.class) 278 * .testEquals(); 279 * } 280 * } 281 * </pre> 282 * 283 * <p>It will test that Foo objects created by the {@code create(a, b, c, d)} factory method with 284 * equal parameters are equal and vice versa, thus indirectly tests the builder equality. 285 */ 286 public void testEquals(Class<?> cls) { 287 try { 288 doTestEquals(cls); 289 } catch (Exception e) { 290 throwIfUnchecked(e); 291 throw new RuntimeException(e); 292 } 293 } 294 295 void doTestEquals(Class<?> cls) 296 throws ParameterNotInstantiableException, ParameterHasNoDistinctValueException, 297 IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException { 298 if (cls.isEnum()) { 299 return; 300 } 301 List<? extends Invokable<?, ?>> factories = Lists.reverse(getFactories(TypeToken.of(cls))); 302 if (factories.isEmpty()) { 303 return; 304 } 305 int numberOfParameters = factories.get(0).getParameters().size(); 306 List<ParameterNotInstantiableException> paramErrors = Lists.newArrayList(); 307 List<ParameterHasNoDistinctValueException> distinctValueErrors = Lists.newArrayList(); 308 List<InvocationTargetException> instantiationExceptions = Lists.newArrayList(); 309 List<FactoryMethodReturnsNullException> nullErrors = Lists.newArrayList(); 310 // Try factories with the greatest number of parameters. 311 for (Invokable<?, ?> factory : factories) { 312 if (factory.getParameters().size() == numberOfParameters) { 313 try { 314 testEqualsUsing(factory); 315 return; 316 } catch (ParameterNotInstantiableException e) { 317 paramErrors.add(e); 318 } catch (ParameterHasNoDistinctValueException e) { 319 distinctValueErrors.add(e); 320 } catch (InvocationTargetException e) { 321 instantiationExceptions.add(e); 322 } catch (FactoryMethodReturnsNullException e) { 323 nullErrors.add(e); 324 } 325 } 326 } 327 throwFirst(paramErrors); 328 throwFirst(distinctValueErrors); 329 throwFirst(instantiationExceptions); 330 throwFirst(nullErrors); 331 } 332 333 /** 334 * Instantiates {@code cls} by invoking one of its non-private constructors or non-private static 335 * factory methods with the parameters automatically provided using dummy values. 336 * 337 * @return The instantiated instance, or {@code null} if the class has no non-private constructor 338 * or factory method to be constructed. 339 */ 340 <T> @Nullable T instantiate(Class<T> cls) 341 throws ParameterNotInstantiableException, 342 IllegalAccessException, 343 InvocationTargetException, 344 FactoryMethodReturnsNullException { 345 if (cls.isEnum()) { 346 T[] constants = cls.getEnumConstants(); 347 if (constants != null && constants.length > 0) { 348 return constants[0]; 349 } else { 350 return null; 351 } 352 } 353 TypeToken<T> type = TypeToken.of(cls); 354 List<ParameterNotInstantiableException> paramErrors = Lists.newArrayList(); 355 List<InvocationTargetException> instantiationExceptions = Lists.newArrayList(); 356 List<FactoryMethodReturnsNullException> nullErrors = Lists.newArrayList(); 357 for (Invokable<?, ? extends T> factory : getFactories(type)) { 358 T instance; 359 try { 360 instance = instantiate(factory); 361 } catch (ParameterNotInstantiableException e) { 362 paramErrors.add(e); 363 continue; 364 } catch (InvocationTargetException e) { 365 instantiationExceptions.add(e); 366 continue; 367 } 368 if (instance == null) { 369 nullErrors.add(new FactoryMethodReturnsNullException(factory)); 370 } else { 371 return instance; 372 } 373 } 374 throwFirst(paramErrors); 375 throwFirst(instantiationExceptions); 376 throwFirst(nullErrors); 377 return null; 378 } 379 380 /** 381 * Instantiates using {@code factory}. If {@code factory} is annotated nullable and returns null, 382 * null will be returned. 383 * 384 * @throws ParameterNotInstantiableException if the static methods cannot be invoked because the 385 * default value of a parameter cannot be determined. 386 * @throws IllegalAccessException if the class isn't public or is nested inside a non-public 387 * class, preventing its methods from being accessible. 388 * @throws InvocationTargetException if a static method threw exception. 389 */ 390 private <T> @Nullable T instantiate(Invokable<?, ? extends T> factory) 391 throws ParameterNotInstantiableException, InvocationTargetException, IllegalAccessException { 392 return invoke(factory, getDummyArguments(factory)); 393 } 394 395 /** 396 * Returns an object responsible for performing sanity tests against the return values of all 397 * public static methods declared by {@code cls}, excluding superclasses. 398 */ 399 public FactoryMethodReturnValueTester forAllPublicStaticMethods(Class<?> cls) { 400 ImmutableList.Builder<Invokable<?, ?>> builder = ImmutableList.builder(); 401 for (Method method : cls.getDeclaredMethods()) { 402 Invokable<?, ?> invokable = Invokable.from(method); 403 invokable.setAccessible(true); 404 if (invokable.isPublic() && invokable.isStatic() && !invokable.isSynthetic()) { 405 builder.add(invokable); 406 } 407 } 408 return new FactoryMethodReturnValueTester(cls, builder.build(), "public static methods"); 409 } 410 411 /** Runs sanity tests against return values of static factory methods declared by a class. */ 412 public final class FactoryMethodReturnValueTester { 413 private final Set<String> packagesToTest = Sets.newHashSet(); 414 private final Class<?> declaringClass; 415 private final ImmutableList<Invokable<?, ?>> factories; 416 private final String factoryMethodsDescription; 417 private Class<?> returnTypeToTest = Object.class; 418 419 private FactoryMethodReturnValueTester( 420 Class<?> declaringClass, 421 ImmutableList<Invokable<?, ?>> factories, 422 String factoryMethodsDescription) { 423 this.declaringClass = declaringClass; 424 this.factories = factories; 425 this.factoryMethodsDescription = factoryMethodsDescription; 426 packagesToTest.add(Reflection.getPackageName(declaringClass)); 427 } 428 429 /** 430 * Specifies that only the methods that are declared to return {@code returnType} or its subtype 431 * are tested. 432 * 433 * @return this tester object 434 */ 435 @CanIgnoreReturnValue 436 public FactoryMethodReturnValueTester thatReturn(Class<?> returnType) { 437 this.returnTypeToTest = returnType; 438 return this; 439 } 440 441 /** 442 * Tests null checks against the instance methods of the return values, if any. 443 * 444 * <p>Test fails if default value cannot be determined for a constructor or factory method 445 * parameter, or if the constructor or factory method throws exception. 446 * 447 * @return this tester 448 */ 449 @CanIgnoreReturnValue 450 public FactoryMethodReturnValueTester testNulls() throws Exception { 451 for (Invokable<?, ?> factory : getFactoriesToTest()) { 452 Object instance = instantiate(factory); 453 if (instance != null 454 && packagesToTest.contains(Reflection.getPackageName(instance.getClass()))) { 455 try { 456 nullPointerTester.testAllPublicInstanceMethods(instance); 457 } catch (AssertionError e) { 458 AssertionError error = 459 new AssertionFailedError("Null check failed on return value of " + factory); 460 error.initCause(e); 461 throw error; 462 } 463 } 464 } 465 return this; 466 } 467 468 /** 469 * Tests {@link Object#equals} and {@link Object#hashCode} against the return values of the 470 * static methods, by asserting that when equal parameters are passed to the same static method, 471 * the return value should also be equal; and vice versa. 472 * 473 * <p>Test fails if default value cannot be determined for a constructor or factory method 474 * parameter, or if the constructor or factory method throws exception. 475 * 476 * @return this tester 477 */ 478 @CanIgnoreReturnValue 479 public FactoryMethodReturnValueTester testEquals() throws Exception { 480 for (Invokable<?, ?> factory : getFactoriesToTest()) { 481 try { 482 testEqualsUsing(factory); 483 } catch (FactoryMethodReturnsNullException e) { 484 // If the factory returns null, we just skip it. 485 } 486 } 487 return this; 488 } 489 490 /** 491 * Runs serialization test on the return values of the static methods. 492 * 493 * <p>Test fails if default value cannot be determined for a constructor or factory method 494 * parameter, or if the constructor or factory method throws exception. 495 * 496 * @return this tester 497 */ 498 @CanIgnoreReturnValue 499 @SuppressWarnings("CatchingUnchecked") // sneaky checked exception 500 public FactoryMethodReturnValueTester testSerializable() throws Exception { 501 for (Invokable<?, ?> factory : getFactoriesToTest()) { 502 Object instance = instantiate(factory); 503 if (instance != null) { 504 try { 505 SerializableTester.reserialize(instance); 506 } catch (Exception e) { // sneaky checked exception 507 AssertionError error = 508 new AssertionFailedError("Serialization failed on return value of " + factory); 509 error.initCause(e.getCause()); 510 throw error; 511 } 512 } 513 } 514 return this; 515 } 516 517 /** 518 * Runs equals and serialization test on the return values. 519 * 520 * <p>Test fails if default value cannot be determined for a constructor or factory method 521 * parameter, or if the constructor or factory method throws exception. 522 * 523 * @return this tester 524 */ 525 @CanIgnoreReturnValue 526 @SuppressWarnings("CatchingUnchecked") // sneaky checked exception 527 public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Exception { 528 for (Invokable<?, ?> factory : getFactoriesToTest()) { 529 try { 530 testEqualsUsing(factory); 531 } catch (FactoryMethodReturnsNullException e) { 532 // If the factory returns null, we just skip it. 533 } 534 Object instance = instantiate(factory); 535 if (instance != null) { 536 try { 537 SerializableTester.reserializeAndAssert(instance); 538 } catch (Exception e) { // sneaky checked exception 539 AssertionError error = 540 new AssertionFailedError("Serialization failed on return value of " + factory); 541 error.initCause(e.getCause()); 542 throw error; 543 } catch (AssertionFailedError e) { 544 AssertionError error = 545 new AssertionFailedError( 546 "Return value of " + factory + " reserialized to an unequal value"); 547 error.initCause(e); 548 throw error; 549 } 550 } 551 } 552 return this; 553 } 554 555 private ImmutableList<Invokable<?, ?>> getFactoriesToTest() { 556 ImmutableList.Builder<Invokable<?, ?>> builder = ImmutableList.builder(); 557 for (Invokable<?, ?> factory : factories) { 558 if (returnTypeToTest.isAssignableFrom(factory.getReturnType().getRawType())) { 559 builder.add(factory); 560 } 561 } 562 ImmutableList<Invokable<?, ?>> factoriesToTest = builder.build(); 563 Assert.assertFalse( 564 "No " 565 + factoryMethodsDescription 566 + " that return " 567 + returnTypeToTest.getName() 568 + " or subtype are found in " 569 + declaringClass 570 + ".", 571 factoriesToTest.isEmpty()); 572 return factoriesToTest; 573 } 574 } 575 576 private void testEqualsUsing(final Invokable<?, ?> factory) 577 throws ParameterNotInstantiableException, ParameterHasNoDistinctValueException, 578 IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException { 579 List<Parameter> params = factory.getParameters(); 580 List<FreshValueGenerator> argGenerators = Lists.newArrayListWithCapacity(params.size()); 581 List<@Nullable Object> args = Lists.newArrayListWithCapacity(params.size()); 582 for (Parameter param : params) { 583 FreshValueGenerator generator = newFreshValueGenerator(); 584 argGenerators.add(generator); 585 args.add(generateDummyArg(param, generator)); 586 } 587 Object instance = createInstance(factory, args); 588 List<Object> equalArgs = generateEqualFactoryArguments(factory, params, args); 589 // Each group is a List of items, each item has a list of factory args. 590 final List<List<List<Object>>> argGroups = Lists.newArrayList(); 591 argGroups.add(ImmutableList.of(args, equalArgs)); 592 EqualsTester tester = 593 new EqualsTester( 594 new ItemReporter() { 595 @Override 596 String reportItem(Item<?> item) { 597 List<Object> factoryArgs = argGroups.get(item.groupNumber).get(item.itemNumber); 598 return factory.getName() 599 + "(" 600 + Joiner.on(", ").useForNull("null").join(factoryArgs) 601 + ")"; 602 } 603 }); 604 tester.addEqualityGroup(instance, createInstance(factory, equalArgs)); 605 for (int i = 0; i < params.size(); i++) { 606 List<Object> newArgs = Lists.newArrayList(args); 607 Object newArg = argGenerators.get(i).generateFresh(params.get(i).getType()); 608 609 if (newArg == null || Objects.equal(args.get(i), newArg)) { 610 if (params.get(i).getType().getRawType().isEnum()) { 611 continue; // Nothing better we can do if it's single-value enum 612 } 613 throw new ParameterHasNoDistinctValueException(params.get(i)); 614 } 615 newArgs.set(i, newArg); 616 tester.addEqualityGroup(createInstance(factory, newArgs)); 617 argGroups.add(ImmutableList.of(newArgs)); 618 } 619 tester.testEquals(); 620 } 621 622 /** 623 * Returns dummy factory arguments that are equal to {@code args} but may be different instances, 624 * to be used to construct a second instance of the same equality group. 625 */ 626 private List<Object> generateEqualFactoryArguments( 627 Invokable<?, ?> factory, List<Parameter> params, List<Object> args) 628 throws ParameterNotInstantiableException, FactoryMethodReturnsNullException, 629 InvocationTargetException, IllegalAccessException { 630 List<Object> equalArgs = Lists.newArrayList(args); 631 for (int i = 0; i < args.size(); i++) { 632 Parameter param = params.get(i); 633 Object arg = args.get(i); 634 // Use new fresh value generator because 'args' were populated with new fresh generator each. 635 // Two newFreshValueGenerator() instances should normally generate equal value sequence. 636 Object shouldBeEqualArg = generateDummyArg(param, newFreshValueGenerator()); 637 if (arg != shouldBeEqualArg 638 && Objects.equal(arg, shouldBeEqualArg) 639 && hashCodeInsensitiveToArgReference(factory, args, i, checkNotNull(shouldBeEqualArg)) 640 && hashCodeInsensitiveToArgReference( 641 factory, args, i, generateDummyArg(param, newFreshValueGenerator()))) { 642 // If the implementation uses identityHashCode(), referential equality is 643 // probably intended. So no point in using an equal-but-different factory argument. 644 // We check twice to avoid confusion caused by accidental hash collision. 645 equalArgs.set(i, shouldBeEqualArg); 646 } 647 } 648 return equalArgs; 649 } 650 651 private static boolean hashCodeInsensitiveToArgReference( 652 Invokable<?, ?> factory, List<Object> args, int i, Object alternateArg) 653 throws FactoryMethodReturnsNullException, InvocationTargetException, IllegalAccessException { 654 List<Object> tentativeArgs = Lists.newArrayList(args); 655 tentativeArgs.set(i, alternateArg); 656 return createInstance(factory, tentativeArgs).hashCode() 657 == createInstance(factory, args).hashCode(); 658 } 659 660 // distinctValues is a type-safe class-values mapping, but we don't have a type-safe data 661 // structure to hold the mappings. 662 @SuppressWarnings({"unchecked", "rawtypes"}) 663 private FreshValueGenerator newFreshValueGenerator() { 664 FreshValueGenerator generator = 665 new FreshValueGenerator() { 666 @Override 667 Object interfaceMethodCalled(Class<?> interfaceType, Method method) { 668 return getDummyValue(TypeToken.of(interfaceType).method(method).getReturnType()); 669 } 670 }; 671 for (Entry<Class<?>, Collection<Object>> entry : distinctValues.asMap().entrySet()) { 672 generator.addSampleInstances((Class) entry.getKey(), entry.getValue()); 673 } 674 return generator; 675 } 676 677 private static @Nullable Object generateDummyArg(Parameter param, FreshValueGenerator generator) 678 throws ParameterNotInstantiableException { 679 if (isNullable(param)) { 680 return null; 681 } 682 Object arg = generator.generateFresh(param.getType()); 683 if (arg == null) { 684 throw new ParameterNotInstantiableException(param); 685 } 686 return arg; 687 } 688 689 private static <X extends Throwable> void throwFirst(List<X> exceptions) throws X { 690 if (!exceptions.isEmpty()) { 691 throw exceptions.get(0); 692 } 693 } 694 695 /** Factories with the least number of parameters are listed first. */ 696 private static <T> ImmutableList<Invokable<?, ? extends T>> getFactories(TypeToken<T> type) { 697 List<Invokable<?, ? extends T>> factories = Lists.newArrayList(); 698 for (Method method : type.getRawType().getDeclaredMethods()) { 699 Invokable<?, ?> invokable = type.method(method); 700 if (!invokable.isPrivate() 701 && !invokable.isSynthetic() 702 && invokable.isStatic() 703 && type.isSupertypeOf(invokable.getReturnType())) { 704 @SuppressWarnings("unchecked") // guarded by isAssignableFrom() 705 Invokable<?, ? extends T> factory = (Invokable<?, ? extends T>) invokable; 706 factories.add(factory); 707 } 708 } 709 if (!Modifier.isAbstract(type.getRawType().getModifiers())) { 710 for (Constructor<?> constructor : type.getRawType().getDeclaredConstructors()) { 711 Invokable<T, T> invokable = type.constructor(constructor); 712 if (!invokable.isPrivate() && !invokable.isSynthetic()) { 713 factories.add(invokable); 714 } 715 } 716 } 717 for (Invokable<?, ?> factory : factories) { 718 factory.setAccessible(true); 719 } 720 // Sorts methods/constructors with the least number of parameters first since it's likely easier 721 // to fill dummy parameter values for them. Ties are broken by name then by the string form of 722 // the parameter list. 723 return BY_NUMBER_OF_PARAMETERS 724 .compound(BY_METHOD_NAME) 725 .compound(BY_PARAMETERS) 726 .immutableSortedCopy(factories); 727 } 728 729 private List<Object> getDummyArguments(Invokable<?, ?> invokable) 730 throws ParameterNotInstantiableException { 731 List<Object> args = Lists.newArrayList(); 732 for (Parameter param : invokable.getParameters()) { 733 if (isNullable(param)) { 734 args.add(null); 735 continue; 736 } 737 Object defaultValue = getDummyValue(param.getType()); 738 if (defaultValue == null) { 739 throw new ParameterNotInstantiableException(param); 740 } 741 args.add(defaultValue); 742 } 743 return args; 744 } 745 746 private <T> T getDummyValue(TypeToken<T> type) { 747 Class<? super T> rawType = type.getRawType(); 748 @SuppressWarnings("unchecked") // Assume all default values are generics safe. 749 T defaultValue = (T) defaultValues.getInstance(rawType); 750 if (defaultValue != null) { 751 return defaultValue; 752 } 753 @SuppressWarnings("unchecked") // ArbitraryInstances always returns generics-safe dummies. 754 T value = (T) ArbitraryInstances.get(rawType); 755 if (value != null) { 756 return value; 757 } 758 if (rawType.isInterface()) { 759 return new SerializableDummyProxy(this).newProxy(type); 760 } 761 return null; 762 } 763 764 private static <T> T createInstance(Invokable<?, ? extends T> factory, List<?> args) 765 throws FactoryMethodReturnsNullException, InvocationTargetException, IllegalAccessException { 766 T instance = invoke(factory, args); 767 if (instance == null) { 768 throw new FactoryMethodReturnsNullException(factory); 769 } 770 return instance; 771 } 772 773 private static <T> @Nullable T invoke(Invokable<?, ? extends T> factory, List<?> args) 774 throws InvocationTargetException, IllegalAccessException { 775 T returnValue = factory.invoke(null, args.toArray()); 776 if (returnValue == null) { 777 Assert.assertTrue( 778 factory + " returns null but it's not annotated with @Nullable", isNullable(factory)); 779 } 780 return returnValue; 781 } 782 783 /** 784 * Thrown if the test tries to invoke a constructor or static factory method but failed because 785 * the dummy value of a constructor or method parameter is unknown. 786 */ 787 @VisibleForTesting 788 static class ParameterNotInstantiableException extends Exception { 789 public ParameterNotInstantiableException(Parameter parameter) { 790 super( 791 "Cannot determine value for parameter " 792 + parameter 793 + " of " 794 + parameter.getDeclaringInvokable()); 795 } 796 } 797 798 /** 799 * Thrown if the test fails to generate two distinct non-null values of a constructor or factory 800 * parameter in order to test {@link Object#equals} and {@link Object#hashCode} of the declaring 801 * class. 802 */ 803 @VisibleForTesting 804 static class ParameterHasNoDistinctValueException extends Exception { 805 ParameterHasNoDistinctValueException(Parameter parameter) { 806 super( 807 "Cannot generate distinct value for parameter " 808 + parameter 809 + " of " 810 + parameter.getDeclaringInvokable()); 811 } 812 } 813 814 /** 815 * Thrown if the test tries to invoke a static factory method to test instance methods but the 816 * factory returned null. 817 */ 818 @VisibleForTesting 819 static class FactoryMethodReturnsNullException extends Exception { 820 public FactoryMethodReturnsNullException(Invokable<?, ?> factory) { 821 super(factory + " returns null and cannot be used to test instance methods."); 822 } 823 } 824 825 private static final class SerializableDummyProxy extends DummyProxy implements Serializable { 826 827 private final transient ClassSanityTester tester; 828 829 SerializableDummyProxy(ClassSanityTester tester) { 830 this.tester = tester; 831 } 832 833 @Override 834 <R> R dummyReturnValue(TypeToken<R> returnType) { 835 return tester.getDummyValue(returnType); 836 } 837 838 @Override 839 public boolean equals(@Nullable Object obj) { 840 return obj instanceof SerializableDummyProxy; 841 } 842 843 @Override 844 public int hashCode() { 845 return 0; 846 } 847 } 848}