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