• 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.primitives.Ints;
37 import com.google.common.reflect.Invokable;
38 import com.google.common.reflect.Parameter;
39 import com.google.common.reflect.Reflection;
40 import com.google.common.reflect.TypeToken;
41 import com.google.common.testing.NullPointerTester.Visibility;
42 import com.google.common.testing.RelationshipTester.Item;
43 import com.google.common.testing.RelationshipTester.ItemReporter;
44 import com.google.errorprone.annotations.CanIgnoreReturnValue;
45 import java.io.Serializable;
46 import java.lang.reflect.Constructor;
47 import java.lang.reflect.InvocationTargetException;
48 import java.lang.reflect.Method;
49 import java.lang.reflect.Modifier;
50 import java.util.Collection;
51 import java.util.List;
52 import java.util.Map.Entry;
53 import java.util.Set;
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 Ints.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             AssertionError error =
459                 new AssertionFailedError("Null check failed on return value of " + factory);
460             error.initCause(e);
461             throw error;
462           }
463         }
464       }
465       return this;
466     }
467 
468     /**
469      * Tests {@link Object#equals} and {@link Object#hashCode} against the return values of the
470      * static methods, by asserting that when equal parameters are passed to the same static method,
471      * the return value should also be equal; and vice versa.
472      *
473      * <p>Test fails if default value cannot be determined for a constructor or factory method
474      * parameter, or if the constructor or factory method throws exception.
475      *
476      * @return this tester
477      */
478     @CanIgnoreReturnValue
testEquals()479     public FactoryMethodReturnValueTester testEquals() throws Exception {
480       for (Invokable<?, ?> factory : getFactoriesToTest()) {
481         try {
482           testEqualsUsing(factory);
483         } catch (FactoryMethodReturnsNullException e) {
484           // If the factory returns null, we just skip it.
485         }
486       }
487       return this;
488     }
489 
490     /**
491      * Runs serialization test on the return values of the static methods.
492      *
493      * <p>Test fails if default value cannot be determined for a constructor or factory method
494      * parameter, or if the constructor or factory method throws exception.
495      *
496      * @return this tester
497      */
498     @CanIgnoreReturnValue
499     @SuppressWarnings("CatchingUnchecked") // sneaky checked exception
testSerializable()500     public FactoryMethodReturnValueTester testSerializable() throws Exception {
501       for (Invokable<?, ?> factory : getFactoriesToTest()) {
502         Object instance = instantiate(factory);
503         if (instance != null) {
504           try {
505             SerializableTester.reserialize(instance);
506           } catch (Exception e) { // sneaky checked exception
507             AssertionError error =
508                 new AssertionFailedError("Serialization failed on return value of " + factory);
509             error.initCause(e.getCause());
510             throw error;
511           }
512         }
513       }
514       return this;
515     }
516 
517     /**
518      * Runs equals and serialization test on the return values.
519      *
520      * <p>Test fails if default value cannot be determined for a constructor or factory method
521      * parameter, or if the constructor or factory method throws exception.
522      *
523      * @return this tester
524      */
525     @CanIgnoreReturnValue
526     @SuppressWarnings("CatchingUnchecked") // sneaky checked exception
testEqualsAndSerializable()527     public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Exception {
528       for (Invokable<?, ?> factory : getFactoriesToTest()) {
529         try {
530           testEqualsUsing(factory);
531         } catch (FactoryMethodReturnsNullException e) {
532           // If the factory returns null, we just skip it.
533         }
534         Object instance = instantiate(factory);
535         if (instance != null) {
536           try {
537             SerializableTester.reserializeAndAssert(instance);
538           } catch (Exception e) { // sneaky checked exception
539             AssertionError error =
540                 new AssertionFailedError("Serialization failed on return value of " + factory);
541             error.initCause(e.getCause());
542             throw error;
543           } catch (AssertionFailedError e) {
544             AssertionError error =
545                 new AssertionFailedError(
546                     "Return value of " + factory + " reserialized to an unequal value");
547             error.initCause(e);
548             throw error;
549           }
550         }
551       }
552       return this;
553     }
554 
getFactoriesToTest()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 
testEqualsUsing(final Invokable<?, ?> factory)576   private void testEqualsUsing(final Invokable<?, ?> factory)
577       throws ParameterNotInstantiableException, ParameterHasNoDistinctValueException,
578           IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException {
579     List<Parameter> params = factory.getParameters();
580     List<FreshValueGenerator> argGenerators = Lists.newArrayListWithCapacity(params.size());
581     List<@Nullable Object> args = Lists.newArrayListWithCapacity(params.size());
582     for (Parameter param : params) {
583       FreshValueGenerator generator = newFreshValueGenerator();
584       argGenerators.add(generator);
585       args.add(generateDummyArg(param, generator));
586     }
587     Object instance = createInstance(factory, args);
588     List<Object> equalArgs = generateEqualFactoryArguments(factory, params, args);
589     // Each group is a List of items, each item has a list of factory args.
590     final List<List<List<Object>>> argGroups = Lists.newArrayList();
591     argGroups.add(ImmutableList.of(args, equalArgs));
592     EqualsTester tester =
593         new EqualsTester(
594             new ItemReporter() {
595               @Override
596               String reportItem(Item<?> item) {
597                 List<Object> factoryArgs = argGroups.get(item.groupNumber).get(item.itemNumber);
598                 return factory.getName()
599                     + "("
600                     + Joiner.on(", ").useForNull("null").join(factoryArgs)
601                     + ")";
602               }
603             });
604     tester.addEqualityGroup(instance, createInstance(factory, equalArgs));
605     for (int i = 0; i < params.size(); i++) {
606       List<Object> newArgs = Lists.newArrayList(args);
607       Object newArg = argGenerators.get(i).generateFresh(params.get(i).getType());
608 
609       if (newArg == null || Objects.equal(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    */
generateEqualFactoryArguments( Invokable<?, ?> factory, List<Parameter> params, List<Object> args)626   private List<Object> generateEqualFactoryArguments(
627       Invokable<?, ?> factory, List<Parameter> params, List<Object> args)
628       throws ParameterNotInstantiableException, FactoryMethodReturnsNullException,
629           InvocationTargetException, IllegalAccessException {
630     List<Object> equalArgs = Lists.newArrayList(args);
631     for (int i = 0; i < args.size(); i++) {
632       Parameter param = params.get(i);
633       Object arg = args.get(i);
634       // Use new fresh value generator because 'args' were populated with new fresh generator each.
635       // Two newFreshValueGenerator() instances should normally generate equal value sequence.
636       Object shouldBeEqualArg = generateDummyArg(param, newFreshValueGenerator());
637       if (arg != shouldBeEqualArg
638           && Objects.equal(arg, shouldBeEqualArg)
639           && hashCodeInsensitiveToArgReference(factory, args, i, shouldBeEqualArg)
640           && hashCodeInsensitiveToArgReference(
641               factory, args, i, generateDummyArg(param, newFreshValueGenerator()))) {
642         // If the implementation uses identityHashCode(), referential equality is
643         // probably intended. So no point in using an equal-but-different factory argument.
644         // We check twice to avoid confusion caused by accidental hash collision.
645         equalArgs.set(i, shouldBeEqualArg);
646       }
647     }
648     return equalArgs;
649   }
650 
hashCodeInsensitiveToArgReference( Invokable<?, ?> factory, List<Object> args, int i, Object alternateArg)651   private static boolean hashCodeInsensitiveToArgReference(
652       Invokable<?, ?> factory, List<Object> args, int i, Object alternateArg)
653       throws FactoryMethodReturnsNullException, InvocationTargetException, IllegalAccessException {
654     List<Object> tentativeArgs = Lists.newArrayList(args);
655     tentativeArgs.set(i, alternateArg);
656     return createInstance(factory, tentativeArgs).hashCode()
657         == createInstance(factory, args).hashCode();
658   }
659 
660   // distinctValues is a type-safe class-values mapping, but we don't have a type-safe data
661   // structure to hold the mappings.
662   @SuppressWarnings({"unchecked", "rawtypes"})
newFreshValueGenerator()663   private FreshValueGenerator newFreshValueGenerator() {
664     FreshValueGenerator generator =
665         new FreshValueGenerator() {
666           @Override
667           Object interfaceMethodCalled(Class<?> interfaceType, Method method) {
668             return getDummyValue(TypeToken.of(interfaceType).method(method).getReturnType());
669           }
670         };
671     for (Entry<Class<?>, Collection<Object>> entry : distinctValues.asMap().entrySet()) {
672       generator.addSampleInstances((Class) entry.getKey(), entry.getValue());
673     }
674     return generator;
675   }
676 
generateDummyArg(Parameter param, FreshValueGenerator generator)677   private static @Nullable Object generateDummyArg(Parameter param, FreshValueGenerator generator)
678       throws ParameterNotInstantiableException {
679     if (isNullable(param)) {
680       return null;
681     }
682     Object arg = generator.generateFresh(param.getType());
683     if (arg == null) {
684       throw new ParameterNotInstantiableException(param);
685     }
686     return arg;
687   }
688 
throwFirst(List<X> exceptions)689   private static <X extends Throwable> void throwFirst(List<X> exceptions) throws X {
690     if (!exceptions.isEmpty()) {
691       throw exceptions.get(0);
692     }
693   }
694 
695   /** Factories with the least number of parameters are listed first. */
getFactories(TypeToken<T> type)696   private static <T> ImmutableList<Invokable<?, ? extends T>> getFactories(TypeToken<T> type) {
697     List<Invokable<?, ? extends T>> factories = Lists.newArrayList();
698     for (Method method : type.getRawType().getDeclaredMethods()) {
699       Invokable<?, ?> invokable = type.method(method);
700       if (!invokable.isPrivate()
701           && !invokable.isSynthetic()
702           && invokable.isStatic()
703           && type.isSupertypeOf(invokable.getReturnType())) {
704         @SuppressWarnings("unchecked") // guarded by isAssignableFrom()
705         Invokable<?, ? extends T> factory = (Invokable<?, ? extends T>) invokable;
706         factories.add(factory);
707       }
708     }
709     if (!Modifier.isAbstract(type.getRawType().getModifiers())) {
710       for (Constructor<?> constructor : type.getRawType().getDeclaredConstructors()) {
711         Invokable<T, T> invokable = type.constructor(constructor);
712         if (!invokable.isPrivate() && !invokable.isSynthetic()) {
713           factories.add(invokable);
714         }
715       }
716     }
717     for (Invokable<?, ?> factory : factories) {
718       factory.setAccessible(true);
719     }
720     // Sorts methods/constructors with the least number of parameters first since it's likely easier
721     // to fill dummy parameter values for them. Ties are broken by name then by the string form of
722     // the parameter list.
723     return BY_NUMBER_OF_PARAMETERS
724         .compound(BY_METHOD_NAME)
725         .compound(BY_PARAMETERS)
726         .immutableSortedCopy(factories);
727   }
728 
getDummyArguments(Invokable<?, ?> invokable)729   private List<Object> getDummyArguments(Invokable<?, ?> invokable)
730       throws ParameterNotInstantiableException {
731     List<Object> args = Lists.newArrayList();
732     for (Parameter param : invokable.getParameters()) {
733       if (isNullable(param)) {
734         args.add(null);
735         continue;
736       }
737       Object defaultValue = getDummyValue(param.getType());
738       if (defaultValue == null) {
739         throw new ParameterNotInstantiableException(param);
740       }
741       args.add(defaultValue);
742     }
743     return args;
744   }
745 
getDummyValue(TypeToken<T> type)746   private <T> T getDummyValue(TypeToken<T> type) {
747     Class<? super T> rawType = type.getRawType();
748     @SuppressWarnings("unchecked") // Assume all default values are generics safe.
749     T defaultValue = (T) defaultValues.getInstance(rawType);
750     if (defaultValue != null) {
751       return defaultValue;
752     }
753     @SuppressWarnings("unchecked") // ArbitraryInstances always returns generics-safe dummies.
754     T value = (T) ArbitraryInstances.get(rawType);
755     if (value != null) {
756       return value;
757     }
758     if (rawType.isInterface()) {
759       return new SerializableDummyProxy(this).newProxy(type);
760     }
761     return null;
762   }
763 
createInstance(Invokable<?, ? extends T> factory, List<?> args)764   private static <T> T createInstance(Invokable<?, ? extends T> factory, List<?> args)
765       throws FactoryMethodReturnsNullException, InvocationTargetException, IllegalAccessException {
766     T instance = invoke(factory, args);
767     if (instance == null) {
768       throw new FactoryMethodReturnsNullException(factory);
769     }
770     return instance;
771   }
772 
invoke(Invokable<?, ? extends T> factory, List<?> args)773   private static <T> @Nullable T invoke(Invokable<?, ? extends T> factory, List<?> args)
774       throws InvocationTargetException, IllegalAccessException {
775     T returnValue = factory.invoke(null, args.toArray());
776     if (returnValue == null) {
777       Assert.assertTrue(
778           factory + " returns null but it's not annotated with @Nullable", isNullable(factory));
779     }
780     return returnValue;
781   }
782 
783   /**
784    * Thrown if the test tries to invoke a constructor or static factory method but failed because
785    * the dummy value of a constructor or method parameter is unknown.
786    */
787   @VisibleForTesting
788   static class ParameterNotInstantiableException extends Exception {
ParameterNotInstantiableException(Parameter parameter)789     public ParameterNotInstantiableException(Parameter parameter) {
790       super(
791           "Cannot determine value for parameter "
792               + parameter
793               + " of "
794               + parameter.getDeclaringInvokable());
795     }
796   }
797 
798   /**
799    * Thrown if the test fails to generate two distinct non-null values of a constructor or factory
800    * parameter in order to test {@link Object#equals} and {@link Object#hashCode} of the declaring
801    * class.
802    */
803   @VisibleForTesting
804   static class ParameterHasNoDistinctValueException extends Exception {
ParameterHasNoDistinctValueException(Parameter parameter)805     ParameterHasNoDistinctValueException(Parameter parameter) {
806       super(
807           "Cannot generate distinct value for parameter "
808               + parameter
809               + " of "
810               + parameter.getDeclaringInvokable());
811     }
812   }
813 
814   /**
815    * Thrown if the test tries to invoke a static factory method to test instance methods but the
816    * factory returned null.
817    */
818   @VisibleForTesting
819   static class FactoryMethodReturnsNullException extends Exception {
FactoryMethodReturnsNullException(Invokable<?, ?> factory)820     public FactoryMethodReturnsNullException(Invokable<?, ?> factory) {
821       super(factory + " returns null and cannot be used to test instance methods.");
822     }
823   }
824 
825   private static final class SerializableDummyProxy extends DummyProxy implements Serializable {
826 
827     private final transient ClassSanityTester tester;
828 
SerializableDummyProxy(ClassSanityTester tester)829     SerializableDummyProxy(ClassSanityTester tester) {
830       this.tester = tester;
831     }
832 
833     @Override
dummyReturnValue(TypeToken<R> returnType)834     <R> R dummyReturnValue(TypeToken<R> returnType) {
835       return tester.getDummyValue(returnType);
836     }
837 
838     @Override
equals(@ullable Object obj)839     public boolean equals(@Nullable Object obj) {
840       return obj instanceof SerializableDummyProxy;
841     }
842 
843     @Override
hashCode()844     public int hashCode() {
845       return 0;
846     }
847   }
848 }
849