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