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