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