ClassSanityTester.java revision 0888a09821a98ac0680fad765217302858e70fa4
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
17package com.google.common.testing;
18
19import static com.google.common.base.Preconditions.checkArgument;
20import static com.google.common.base.Preconditions.checkNotNull;
21
22import com.google.common.annotations.Beta;
23import com.google.common.annotations.VisibleForTesting;
24import com.google.common.base.Joiner;
25import com.google.common.base.Objects;
26import com.google.common.base.Throwables;
27import com.google.common.collect.ArrayListMultimap;
28import com.google.common.collect.ImmutableList;
29import com.google.common.collect.ListMultimap;
30import com.google.common.collect.Lists;
31import com.google.common.collect.MutableClassToInstanceMap;
32import com.google.common.collect.Ordering;
33import com.google.common.collect.Sets;
34import com.google.common.primitives.Ints;
35import com.google.common.reflect.Invokable;
36import com.google.common.reflect.Parameter;
37import com.google.common.reflect.Reflection;
38import com.google.common.reflect.TypeToken;
39import com.google.common.testing.NullPointerTester.Visibility;
40import com.google.common.testing.RelationshipTester.Item;
41import com.google.common.testing.RelationshipTester.ItemReporter;
42
43import junit.framework.Assert;
44import junit.framework.AssertionFailedError;
45
46import java.io.Serializable;
47import java.lang.reflect.Constructor;
48import java.lang.reflect.InvocationTargetException;
49import java.lang.reflect.Method;
50import java.lang.reflect.Modifier;
51import java.util.Collection;
52import java.util.HashSet;
53import java.util.List;
54import java.util.Map;
55import java.util.Set;
56
57import 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
79public 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
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   */
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
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   */
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   */
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
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
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   */
296  public void testEquals(Class<?> cls) {
297    try {
298      doTestEquals(cls);
299    } catch (Exception e) {
300      throw Throwables.propagate(e);
301    }
302  }
303
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   */
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   */
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
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     */
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     */
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     */
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     */
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     */
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
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   */
565  @Nullable private <T> T instantiate(Invokable<?, ? extends T> factory)
566      throws ParameterNotInstantiableException, InvocationTargetException,
567      IllegalAccessException {
568    return invoke(factory, getDummyArguments(factory));
569  }
570
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   */
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
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"})
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
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
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.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
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
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
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
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 {
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 {
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 {
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
807    SerializableDummyProxy(ClassSanityTester tester) {
808      this.tester = tester;
809    }
810
811    @Override <R> R dummyReturnValue(TypeToken<R> returnType) {
812      return tester.getDummyValue(returnType);
813    }
814
815    @Override public boolean equals(Object obj) {
816      return obj instanceof SerializableDummyProxy;
817    }
818
819    @Override public int hashCode() {
820      return 0;
821    }
822  }
823}
824
825