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