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