Coverage Summary for Class: ClassSanityTester (com.google.common.testing)

Class Method, % Line, %
ClassSanityTester 100% (24/24) 98.7% (227/230)
ClassSanityTester$1 100% (2/2) 100% (2/2)
ClassSanityTester$2 100% (2/2) 100% (2/2)
ClassSanityTester$3 100% (2/2) 100% (2/2)
ClassSanityTester$4 100% (2/2) 100% (4/4)
ClassSanityTester$5 50% (1/2) 50% (1/2)
ClassSanityTester$FactoryMethodReturnsNullException 100% (1/1) 100% (2/2)
ClassSanityTester$FactoryMethodReturnValueTester 100% (8/8) 100% (68/68)
ClassSanityTester$ParameterHasNoDistinctValueException 100% (1/1) 100% (3/3)
ClassSanityTester$ParameterNotInstantiableException 100% (1/1) 100% (3/3)
ClassSanityTester$SerializableDummyProxy 75% (3/4) 83.3% (5/6)
Total 95.9% (47/49) 98.5% (319/324)


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