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