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