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