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