2
* Copyright (C) 2012 The Guava Authors
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
8
* http://www.apache.org/licenses/LICENSE-2.0
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.
17
package com.google.common.testing;
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;
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;
52
import java.util.Map.Entry;
54
import javax.annotation.CheckForNull;
55
import junit.framework.Assert;
56
import junit.framework.AssertionFailedError;
57
import org.checkerframework.checker.nullness.qual.Nullable;
60
* Tester that runs automated sanity tests for any given class. A typical use case is to test static
61
* factory classes like:
64
* interface Book {...}
65
* public class Books {
66
* public static Book hardcover(String title) {...}
67
* public static Book paperback(String title) {...}
71
* <p>And all the created {@code Book} instances can be tested with:
74
* new ClassSanityTester()
75
* .forAllPublicStaticMethods(Books.class)
76
* .thatReturn(Book.class)
77
* .testEquals(); // or testNulls(), testSerializable() etc.
85
public final class ClassSanityTester {
87
private static final Ordering<Invokable<?, ?>> BY_METHOD_NAME =
88
new Ordering<Invokable<?, ?>>() {
90
public int compare(Invokable<?, ?> left, Invokable<?, ?> right) {
91
return left.getName().compareTo(right.getName());
95
private static final Ordering<Invokable<?, ?>> BY_PARAMETERS =
96
new Ordering<Invokable<?, ?>>() {
98
public int compare(Invokable<?, ?> left, Invokable<?, ?> right) {
99
return Ordering.usingToString().compare(left.getParameters(), right.getParameters());
103
private static final Ordering<Invokable<?, ?>> BY_NUMBER_OF_PARAMETERS =
104
new Ordering<Invokable<?, ?>>() {
106
public int compare(Invokable<?, ?> left, Invokable<?, ?> right) {
107
return Ints.compare(left.getParameters().size(), right.getParameters().size());
111
private final MutableClassToInstanceMap<Object> defaultValues =
112
MutableClassToInstanceMap.create();
113
private final ListMultimap<Class<?>, Object> distinctValues = ArrayListMultimap.create();
114
private final NullPointerTester nullPointerTester = new NullPointerTester();
116
public ClassSanityTester() {
117
// TODO(benyu): bake these into ArbitraryInstances.
118
setDefault(byte.class, (byte) 1);
119
setDefault(Byte.class, (byte) 1);
120
setDefault(short.class, (short) 1);
121
setDefault(Short.class, (short) 1);
122
setDefault(int.class, 1);
123
setDefault(Integer.class, 1);
124
setDefault(long.class, 1L);
125
setDefault(Long.class, 1L);
126
setDefault(float.class, 1F);
127
setDefault(Float.class, 1F);
128
setDefault(double.class, 1D);
129
setDefault(Double.class, 1D);
130
setDefault(Class.class, Class.class);
134
* Sets the default value for {@code type}. The default value isn't used in testing {@link
135
* Object#equals} because more than one sample instances are needed for testing inequality. To set
136
* distinct values for equality testing, use {@link #setDistinctValues} instead.
138
@CanIgnoreReturnValue
139
public <T> ClassSanityTester setDefault(Class<T> type, T value) {
140
nullPointerTester.setDefault(type, value);
141
defaultValues.putInstance(type, value);
146
* Sets distinct values for {@code type}, so that when a class {@code Foo} is tested for {@link
147
* Object#equals} and {@link Object#hashCode}, and its construction requires a parameter of {@code
148
* type}, the distinct values of {@code type} can be passed as parameters to create {@code Foo}
149
* instances that are unequal.
151
* <p>Calling {@code setDistinctValues(type, v1, v2)} also sets the default value for {@code type}
152
* that's used for {@link #testNulls}.
154
* <p>Only necessary for types where {@link ClassSanityTester} doesn't already know how to create
157
* @return this tester instance
160
@CanIgnoreReturnValue
161
public <T> ClassSanityTester setDistinctValues(Class<T> type, T value1, T value2) {
163
checkNotNull(value1);
164
checkNotNull(value2);
165
checkArgument(!Objects.equal(value1, value2), "Duplicate value provided.");
166
distinctValues.replaceValues(type, ImmutableList.of(value1, value2));
167
setDefault(type, value1);
172
* Tests that {@code cls} properly checks null on all constructor and method parameters that
173
* aren't annotated nullable (according to the rules of {@link NullPointerTester}). In details:
176
* <li>All non-private static methods are checked such that passing null for any parameter
177
* that's not annotated nullable should throw {@link NullPointerException}.
178
* <li>If there is any non-private constructor or non-private static factory method declared by
179
* {@code cls}, all non-private instance methods will be checked too using the instance
180
* created by invoking the constructor or static factory method.
181
* <li>If there is any non-private constructor or non-private static factory method declared by
184
* <li>Test will fail if default value for a parameter cannot be determined.
185
* <li>Test will fail if the factory method returns null so testing instance methods is
187
* <li>Test will fail if the constructor or factory method throws exception.
189
* <li>If there is no non-private constructor or non-private static factory method declared by
190
* {@code cls}, instance methods are skipped for nulls test.
191
* <li>Nulls test is not performed on method return values unless the method is a non-private
192
* static factory method whose return type is {@code cls} or {@code cls}'s subtype.
195
public void testNulls(Class<?> cls) {
197
doTestNulls(cls, Visibility.PACKAGE);
198
} catch (Exception e) {
200
throw new RuntimeException(e);
204
void doTestNulls(Class<?> cls, Visibility visibility)
205
throws ParameterNotInstantiableException, IllegalAccessException, InvocationTargetException,
206
FactoryMethodReturnsNullException {
207
if (!Modifier.isAbstract(cls.getModifiers())) {
208
nullPointerTester.testConstructors(cls, visibility);
210
nullPointerTester.testStaticMethods(cls, visibility);
211
if (hasInstanceMethodToTestNulls(cls, visibility)) {
212
Object instance = instantiate(cls);
213
if (instance != null) {
214
nullPointerTester.testInstanceMethods(instance, visibility);
219
private boolean hasInstanceMethodToTestNulls(Class<?> c, Visibility visibility) {
220
for (Method method : nullPointerTester.getInstanceMethodsToTest(c, visibility)) {
221
for (Parameter param : Invokable.from(method).getParameters()) {
222
if (!NullPointerTester.isPrimitiveOrNullable(param)) {
231
* Tests the {@link Object#equals} and {@link Object#hashCode} of {@code cls}. In details:
234
* <li>The non-private constructor or non-private static factory method with the most parameters
235
* is used to construct the sample instances. In case of tie, the candidate constructors or
236
* factories are tried one after another until one can be used to construct sample
238
* <li>For the constructor or static factory method used to construct instances, it's checked
239
* that when equal parameters are passed, the result instance should also be equal; and vice
241
* <li>If a non-private constructor or non-private static factory method exists:
243
* <li>Test will fail if default value for a parameter cannot be determined.
244
* <li>Test will fail if the factory method returns null so testing instance methods is
246
* <li>Test will fail if the constructor or factory method throws exception.
248
* <li>If there is no non-private constructor or non-private static factory method declared by
249
* {@code cls}, no test is performed.
250
* <li>Equality test is not performed on method return values unless the method is a non-private
251
* static factory method whose return type is {@code cls} or {@code cls}'s subtype.
252
* <li>Inequality check is not performed against state mutation methods such as {@link
253
* List#add}, or functional update methods such as {@link
254
* com.google.common.base.Joiner#skipNulls}.
257
* <p>Note that constructors taking a builder object cannot be tested effectively because
258
* semantics of builder can be arbitrarily complex. Still, a factory class can be created in the
259
* test to facilitate equality testing. For example:
262
* public class FooTest {
264
* private static class FooFactoryForTest {
265
* public static Foo create(String a, String b, int c, boolean d) {
266
* return Foo.builder()
275
* public void testEquals() {
276
* new ClassSanityTester()
277
* .forAllPublicStaticMethods(FooFactoryForTest.class)
278
* .thatReturn(Foo.class)
284
* <p>It will test that Foo objects created by the {@code create(a, b, c, d)} factory method with
285
* equal parameters are equal and vice versa, thus indirectly tests the builder equality.
287
public void testEquals(Class<?> cls) {
290
} catch (Exception e) {
292
throw new RuntimeException(e);
296
void doTestEquals(Class<?> cls)
297
throws ParameterNotInstantiableException, ParameterHasNoDistinctValueException,
298
IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException {
302
List<? extends Invokable<?, ?>> factories = Lists.reverse(getFactories(TypeToken.of(cls)));
303
if (factories.isEmpty()) {
306
int numberOfParameters = factories.get(0).getParameters().size();
307
List<ParameterNotInstantiableException> paramErrors = Lists.newArrayList();
308
List<ParameterHasNoDistinctValueException> distinctValueErrors = Lists.newArrayList();
309
List<InvocationTargetException> instantiationExceptions = Lists.newArrayList();
310
List<FactoryMethodReturnsNullException> nullErrors = Lists.newArrayList();
311
// Try factories with the greatest number of parameters.
312
for (Invokable<?, ?> factory : factories) {
313
if (factory.getParameters().size() == numberOfParameters) {
315
testEqualsUsing(factory);
317
} catch (ParameterNotInstantiableException e) {
319
} catch (ParameterHasNoDistinctValueException e) {
320
distinctValueErrors.add(e);
321
} catch (InvocationTargetException e) {
322
instantiationExceptions.add(e);
323
} catch (FactoryMethodReturnsNullException e) {
328
throwFirst(paramErrors);
329
throwFirst(distinctValueErrors);
330
throwFirst(instantiationExceptions);
331
throwFirst(nullErrors);
335
* Instantiates {@code cls} by invoking one of its non-private constructors or non-private static
336
* factory methods with the parameters automatically provided using dummy values.
338
* @return The instantiated instance, or {@code null} if the class has no non-private constructor
339
* or factory method to be constructed.
341
<T> @Nullable T instantiate(Class<T> cls)
342
throws ParameterNotInstantiableException,
343
IllegalAccessException,
344
InvocationTargetException,
345
FactoryMethodReturnsNullException {
347
T[] constants = cls.getEnumConstants();
348
if (constants != null && constants.length > 0) {
354
TypeToken<T> type = TypeToken.of(cls);
355
List<ParameterNotInstantiableException> paramErrors = Lists.newArrayList();
356
List<InvocationTargetException> instantiationExceptions = Lists.newArrayList();
357
List<FactoryMethodReturnsNullException> nullErrors = Lists.newArrayList();
358
for (Invokable<?, ? extends T> factory : getFactories(type)) {
361
instance = instantiate(factory);
362
} catch (ParameterNotInstantiableException e) {
365
} catch (InvocationTargetException e) {
366
instantiationExceptions.add(e);
369
if (instance == null) {
370
nullErrors.add(new FactoryMethodReturnsNullException(factory));
375
throwFirst(paramErrors);
376
throwFirst(instantiationExceptions);
377
throwFirst(nullErrors);
382
* Instantiates using {@code factory}. If {@code factory} is annotated nullable and returns null,
383
* null will be returned.
385
* @throws ParameterNotInstantiableException if the static methods cannot be invoked because the
386
* default value of a parameter cannot be determined.
387
* @throws IllegalAccessException if the class isn't public or is nested inside a non-public
388
* class, preventing its methods from being accessible.
389
* @throws InvocationTargetException if a static method threw exception.
391
private <T> @Nullable T instantiate(Invokable<?, ? extends T> factory)
392
throws ParameterNotInstantiableException, InvocationTargetException, IllegalAccessException {
393
return invoke(factory, getDummyArguments(factory));
397
* Returns an object responsible for performing sanity tests against the return values of all
398
* public static methods declared by {@code cls}, excluding superclasses.
400
public FactoryMethodReturnValueTester forAllPublicStaticMethods(Class<?> cls) {
401
ImmutableList.Builder<Invokable<?, ?>> builder = ImmutableList.builder();
402
for (Method method : cls.getDeclaredMethods()) {
403
Invokable<?, ?> invokable = Invokable.from(method);
404
invokable.setAccessible(true);
405
if (invokable.isPublic() && invokable.isStatic() && !invokable.isSynthetic()) {
406
builder.add(invokable);
409
return new FactoryMethodReturnValueTester(cls, builder.build(), "public static methods");
412
/** Runs sanity tests against return values of static factory methods declared by a class. */
413
public final class FactoryMethodReturnValueTester {
414
private final Set<String> packagesToTest = Sets.newHashSet();
415
private final Class<?> declaringClass;
416
private final ImmutableList<Invokable<?, ?>> factories;
417
private final String factoryMethodsDescription;
418
private Class<?> returnTypeToTest = Object.class;
420
private FactoryMethodReturnValueTester(
421
Class<?> declaringClass,
422
ImmutableList<Invokable<?, ?>> factories,
423
String factoryMethodsDescription) {
424
this.declaringClass = declaringClass;
425
this.factories = factories;
426
this.factoryMethodsDescription = factoryMethodsDescription;
427
packagesToTest.add(Reflection.getPackageName(declaringClass));
431
* Specifies that only the methods that are declared to return {@code returnType} or its subtype
434
* @return this tester object
436
@CanIgnoreReturnValue
437
public FactoryMethodReturnValueTester thatReturn(Class<?> returnType) {
438
this.returnTypeToTest = returnType;
443
* Tests null checks against the instance methods of the return values, if any.
445
* <p>Test fails if default value cannot be determined for a constructor or factory method
446
* parameter, or if the constructor or factory method throws exception.
448
* @return this tester
450
@CanIgnoreReturnValue
451
public FactoryMethodReturnValueTester testNulls() throws Exception {
452
for (Invokable<?, ?> factory : getFactoriesToTest()) {
453
Object instance = instantiate(factory);
455
&& packagesToTest.contains(Reflection.getPackageName(instance.getClass()))) {
457
nullPointerTester.testAllPublicInstanceMethods(instance);
458
} catch (AssertionError e) {
459
throw new AssertionError("Null check failed on return value of " + factory, e);
467
* Tests {@link Object#equals} and {@link Object#hashCode} against the return values of the
468
* static methods, by asserting that when equal parameters are passed to the same static method,
469
* the return value should also be equal; and vice versa.
471
* <p>Test fails if default value cannot be determined for a constructor or factory method
472
* parameter, or if the constructor or factory method throws exception.
474
* @return this tester
476
@CanIgnoreReturnValue
477
public FactoryMethodReturnValueTester testEquals() throws Exception {
478
for (Invokable<?, ?> factory : getFactoriesToTest()) {
480
testEqualsUsing(factory);
481
} catch (FactoryMethodReturnsNullException e) {
482
// If the factory returns null, we just skip it.
489
* Runs serialization test on the return values of the static methods.
491
* <p>Test fails if default value cannot be determined for a constructor or factory method
492
* parameter, or if the constructor or factory method throws exception.
494
* @return this tester
496
@CanIgnoreReturnValue
497
@SuppressWarnings("CatchingUnchecked") // sneaky checked exception
498
public FactoryMethodReturnValueTester testSerializable() throws Exception {
499
for (Invokable<?, ?> factory : getFactoriesToTest()) {
500
Object instance = instantiate(factory);
501
if (instance != null) {
503
SerializableTester.reserialize(instance);
504
} catch (Exception e) { // sneaky checked exception
505
throw new AssertionError(
506
"Serialization failed on return value of " + factory, e.getCause());
514
* Runs equals and serialization test on the return values.
516
* <p>Test fails if default value cannot be determined for a constructor or factory method
517
* parameter, or if the constructor or factory method throws exception.
519
* @return this tester
521
@CanIgnoreReturnValue
522
@SuppressWarnings("CatchingUnchecked") // sneaky checked exception
523
public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Exception {
524
for (Invokable<?, ?> factory : getFactoriesToTest()) {
526
testEqualsUsing(factory);
527
} catch (FactoryMethodReturnsNullException e) {
528
// If the factory returns null, we just skip it.
530
Object instance = instantiate(factory);
531
if (instance != null) {
533
SerializableTester.reserializeAndAssert(instance);
534
} catch (Exception e) { // sneaky checked exception
535
throw new AssertionError(
536
"Serialization failed on return value of " + factory, e.getCause());
537
} catch (AssertionFailedError e) {
538
throw new AssertionError(
539
"Return value of " + factory + " reserialized to an unequal value", e);
546
private ImmutableList<Invokable<?, ?>> getFactoriesToTest() {
547
ImmutableList.Builder<Invokable<?, ?>> builder = ImmutableList.builder();
548
for (Invokable<?, ?> factory : factories) {
549
if (returnTypeToTest.isAssignableFrom(factory.getReturnType().getRawType())) {
550
builder.add(factory);
553
ImmutableList<Invokable<?, ?>> factoriesToTest = builder.build();
556
+ factoryMethodsDescription
558
+ returnTypeToTest.getName()
559
+ " or subtype are found in "
562
factoriesToTest.isEmpty());
563
return factoriesToTest;
567
private void testEqualsUsing(final Invokable<?, ?> factory)
568
throws ParameterNotInstantiableException, ParameterHasNoDistinctValueException,
569
IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException {
570
List<Parameter> params = factory.getParameters();
571
List<FreshValueGenerator> argGenerators = Lists.newArrayListWithCapacity(params.size());
572
List<@Nullable Object> args = Lists.newArrayListWithCapacity(params.size());
573
for (Parameter param : params) {
574
FreshValueGenerator generator = newFreshValueGenerator();
575
argGenerators.add(generator);
576
args.add(generateDummyArg(param, generator));
578
Object instance = createInstance(factory, args);
579
List<Object> equalArgs = generateEqualFactoryArguments(factory, params, args);
580
// Each group is a List of items, each item has a list of factory args.
581
final List<List<List<Object>>> argGroups = Lists.newArrayList();
582
argGroups.add(ImmutableList.of(args, equalArgs));
583
EqualsTester tester =
587
String reportItem(Item<?> item) {
588
List<Object> factoryArgs = argGroups.get(item.groupNumber).get(item.itemNumber);
589
return factory.getName()
591
+ Joiner.on(", ").useForNull("null").join(factoryArgs)
595
tester.addEqualityGroup(instance, createInstance(factory, equalArgs));
596
for (int i = 0; i < params.size(); i++) {
597
List<Object> newArgs = Lists.newArrayList(args);
598
Object newArg = argGenerators.get(i).generateFresh(params.get(i).getType());
600
if (newArg == null || Objects.equal(args.get(i), newArg)) {
601
if (params.get(i).getType().getRawType().isEnum()) {
602
continue; // Nothing better we can do if it's single-value enum
604
throw new ParameterHasNoDistinctValueException(params.get(i));
606
newArgs.set(i, newArg);
607
tester.addEqualityGroup(createInstance(factory, newArgs));
608
argGroups.add(ImmutableList.of(newArgs));
614
* Returns dummy factory arguments that are equal to {@code args} but may be different instances,
615
* to be used to construct a second instance of the same equality group.
617
private List<Object> generateEqualFactoryArguments(
618
Invokable<?, ?> factory, List<Parameter> params, List<Object> args)
619
throws ParameterNotInstantiableException, FactoryMethodReturnsNullException,
620
InvocationTargetException, IllegalAccessException {
621
List<Object> equalArgs = Lists.newArrayList(args);
622
for (int i = 0; i < args.size(); i++) {
623
Parameter param = params.get(i);
624
Object arg = args.get(i);
625
// Use new fresh value generator because 'args' were populated with new fresh generator each.
626
// Two newFreshValueGenerator() instances should normally generate equal value sequence.
627
Object shouldBeEqualArg = generateDummyArg(param, newFreshValueGenerator());
628
if (arg != shouldBeEqualArg
629
&& Objects.equal(arg, shouldBeEqualArg)
630
&& hashCodeInsensitiveToArgReference(factory, args, i, shouldBeEqualArg)
631
&& hashCodeInsensitiveToArgReference(
632
factory, args, i, generateDummyArg(param, newFreshValueGenerator()))) {
633
// If the implementation uses identityHashCode(), referential equality is
634
// probably intended. So no point in using an equal-but-different factory argument.
635
// We check twice to avoid confusion caused by accidental hash collision.
636
equalArgs.set(i, shouldBeEqualArg);
642
private static boolean hashCodeInsensitiveToArgReference(
643
Invokable<?, ?> factory, List<Object> args, int i, Object alternateArg)
644
throws FactoryMethodReturnsNullException, InvocationTargetException, IllegalAccessException {
645
List<Object> tentativeArgs = Lists.newArrayList(args);
646
tentativeArgs.set(i, alternateArg);
647
return createInstance(factory, tentativeArgs).hashCode()
648
== createInstance(factory, args).hashCode();
651
// distinctValues is a type-safe class-values mapping, but we don't have a type-safe data
652
// structure to hold the mappings.
653
@SuppressWarnings({"unchecked", "rawtypes"})
654
private FreshValueGenerator newFreshValueGenerator() {
655
FreshValueGenerator generator =
656
new FreshValueGenerator() {
659
Object interfaceMethodCalled(Class<?> interfaceType, Method method) {
660
return getDummyValue(TypeToken.of(interfaceType).method(method).getReturnType());
663
for (Entry<Class<?>, Collection<Object>> entry : distinctValues.asMap().entrySet()) {
664
generator.addSampleInstances((Class) entry.getKey(), entry.getValue());
669
private static @Nullable Object generateDummyArg(Parameter param, FreshValueGenerator generator)
670
throws ParameterNotInstantiableException {
671
if (isNullable(param)) {
674
Object arg = generator.generateFresh(param.getType());
676
throw new ParameterNotInstantiableException(param);
681
private static <X extends Throwable> void throwFirst(List<X> exceptions) throws X {
682
if (!exceptions.isEmpty()) {
683
throw exceptions.get(0);
687
/** Factories with the least number of parameters are listed first. */
688
private static <T> ImmutableList<Invokable<?, ? extends T>> getFactories(TypeToken<T> type) {
689
List<Invokable<?, ? extends T>> factories = Lists.newArrayList();
690
for (Method method : type.getRawType().getDeclaredMethods()) {
691
Invokable<?, ?> invokable = type.method(method);
692
if (!invokable.isPrivate()
693
&& !invokable.isSynthetic()
694
&& invokable.isStatic()
695
&& type.isSupertypeOf(invokable.getReturnType())) {
696
@SuppressWarnings("unchecked") // guarded by isAssignableFrom()
697
Invokable<?, ? extends T> factory = (Invokable<?, ? extends T>) invokable;
698
factories.add(factory);
701
if (!Modifier.isAbstract(type.getRawType().getModifiers())) {
702
for (Constructor<?> constructor : type.getRawType().getDeclaredConstructors()) {
703
Invokable<T, T> invokable = type.constructor(constructor);
704
if (!invokable.isPrivate() && !invokable.isSynthetic()) {
705
factories.add(invokable);
709
for (Invokable<?, ?> factory : factories) {
710
factory.setAccessible(true);
712
// Sorts methods/constructors with the least number of parameters first since it's likely easier
713
// to fill dummy parameter values for them. Ties are broken by name then by the string form of
714
// the parameter list.
715
return BY_NUMBER_OF_PARAMETERS
716
.compound(BY_METHOD_NAME)
717
.compound(BY_PARAMETERS)
718
.immutableSortedCopy(factories);
721
private List<Object> getDummyArguments(Invokable<?, ?> invokable)
722
throws ParameterNotInstantiableException {
723
List<Object> args = Lists.newArrayList();
724
for (Parameter param : invokable.getParameters()) {
725
if (isNullable(param)) {
729
Object defaultValue = getDummyValue(param.getType());
730
if (defaultValue == null) {
731
throw new ParameterNotInstantiableException(param);
733
args.add(defaultValue);
739
private <T> T getDummyValue(TypeToken<T> type) {
740
Class<? super T> rawType = type.getRawType();
741
@SuppressWarnings("unchecked") // Assume all default values are generics safe.
742
T defaultValue = (T) defaultValues.getInstance(rawType);
743
if (defaultValue != null) {
746
@SuppressWarnings("unchecked") // ArbitraryInstances always returns generics-safe dummies.
747
T value = (T) ArbitraryInstances.get(rawType);
751
if (rawType.isInterface()) {
752
return new SerializableDummyProxy(this).newProxy(type);
757
private static <T> T createInstance(Invokable<?, ? extends T> factory, List<?> args)
758
throws FactoryMethodReturnsNullException, InvocationTargetException, IllegalAccessException {
759
T instance = invoke(factory, args);
760
if (instance == null) {
761
throw new FactoryMethodReturnsNullException(factory);
766
private static <T> @Nullable T invoke(Invokable<?, ? extends T> factory, List<?> args)
767
throws InvocationTargetException, IllegalAccessException {
768
T returnValue = factory.invoke(null, args.toArray());
769
if (returnValue == null) {
771
factory + " returns null but it's not annotated with @Nullable", isNullable(factory));
777
* Thrown if the test tries to invoke a constructor or static factory method but failed because
778
* the dummy value of a constructor or method parameter is unknown.
781
static class ParameterNotInstantiableException extends Exception {
782
public ParameterNotInstantiableException(Parameter parameter) {
784
"Cannot determine value for parameter "
787
+ parameter.getDeclaringInvokable());
792
* Thrown if the test fails to generate two distinct non-null values of a constructor or factory
793
* parameter in order to test {@link Object#equals} and {@link Object#hashCode} of the declaring
797
static class ParameterHasNoDistinctValueException extends Exception {
798
ParameterHasNoDistinctValueException(Parameter parameter) {
800
"Cannot generate distinct value for parameter "
803
+ parameter.getDeclaringInvokable());
808
* Thrown if the test tries to invoke a static factory method to test instance methods but the
809
* factory returned null.
812
static class FactoryMethodReturnsNullException extends Exception {
813
public FactoryMethodReturnsNullException(Invokable<?, ?> factory) {
814
super(factory + " returns null and cannot be used to test instance methods.");
818
private static final class SerializableDummyProxy extends DummyProxy implements Serializable {
820
private final transient ClassSanityTester tester;
822
SerializableDummyProxy(ClassSanityTester tester) {
823
this.tester = tester;
827
<R> R dummyReturnValue(TypeToken<R> returnType) {
828
return tester.getDummyValue(returnType);
832
public boolean equals(@Nullable Object obj) {
833
return obj instanceof SerializableDummyProxy;
837
public int hashCode() {