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