/* * Copyright (C) 2012 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.testing; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Throwables.throwIfUnchecked; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.MutableClassToInstanceMap; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; import com.google.common.primitives.Ints; import com.google.common.reflect.Invokable; import com.google.common.reflect.Parameter; import com.google.common.reflect.Reflection; import com.google.common.reflect.TypeToken; import com.google.common.testing.NullPointerTester.Visibility; import com.google.common.testing.RelationshipTester.Item; import com.google.common.testing.RelationshipTester.ItemReporter; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; import junit.framework.Assert; import junit.framework.AssertionFailedError; /** * Tester that runs automated sanity tests for any given class. A typical use case is to test static * factory classes like: <pre> * interface Book {...} * public class Books { * public static Book hardcover(String title) {...} * public static Book paperback(String title) {...} * } * </pre> * <p>And all the created {@code Book} instances can be tested with: <pre> * new ClassSanityTester() * .forAllPublicStaticMethods(Books.class) * .thatReturn(Book.class) * .testEquals(); // or testNulls(), testSerializable() etc. * </pre> * * @author Ben Yu * @since 14.0 */ @Beta @GwtIncompatible public final class ClassSanityTester { private static final Ordering<Invokable<?, ?>> BY_METHOD_NAME = new Ordering<Invokable<?, ?>>() { @Override public int compare(Invokable<?, ?> left, Invokable<?, ?> right) { return left.getName().compareTo(right.getName()); } }; private static final Ordering<Invokable<?, ?>> BY_PARAMETERS = new Ordering<Invokable<?, ?>>() { @Override public int compare(Invokable<?, ?> left, Invokable<?, ?> right) { return Ordering.usingToString().compare(left.getParameters(), right.getParameters()); } }; private static final Ordering<Invokable<?, ?>> BY_NUMBER_OF_PARAMETERS = new Ordering<Invokable<?, ?>>() { @Override public int compare(Invokable<?, ?> left, Invokable<?, ?> right) { return Ints.compare(left.getParameters().size(), right.getParameters().size()); } }; private final MutableClassToInstanceMap<Object> defaultValues = MutableClassToInstanceMap.create(); private final ListMultimap<Class<?>, Object> distinctValues = ArrayListMultimap.create(); private final NullPointerTester nullPointerTester = new NullPointerTester(); public ClassSanityTester() { // TODO(benyu): bake these into ArbitraryInstances. setDefault(byte.class, (byte) 1); setDefault(Byte.class, (byte) 1); setDefault(short.class, (short) 1); setDefault(Short.class, (short) 1); setDefault(int.class, 1); setDefault(Integer.class, 1); setDefault(long.class, 1L); setDefault(Long.class, 1L); setDefault(float.class, 1F); setDefault(Float.class, 1F); setDefault(double.class, 1D); setDefault(Double.class, 1D); setDefault(Class.class, Class.class); } /** * Sets the default value for {@code type}. The default value isn't used in testing {@link * Object#equals} because more than one sample instances are needed for testing inequality. * To set distinct values for equality testing, use {@link #setDistinctValues} instead. */ public <T> ClassSanityTester setDefault(Class<T> type, T value) { nullPointerTester.setDefault(type, value); defaultValues.putInstance(type, value); return this; } /** * Sets distinct values for {@code type}, so that when a class {@code Foo} is tested for {@link * Object#equals} and {@link Object#hashCode}, and its construction requires a parameter of {@code * type}, the distinct values of {@code type} can be passed as parameters to create {@code Foo} * instances that are unequal. * * <p>Calling {@code setDistinctValues(type, v1, v2)} also sets the default value for {@code type} * that's used for {@link #testNulls}. * * <p>Only necessary for types where {@link ClassSanityTester} doesn't already know how to create * distinct values. * * @return this tester instance * @since 17.0 */ public <T> ClassSanityTester setDistinctValues(Class<T> type, T value1, T value2) { checkNotNull(type); checkNotNull(value1); checkNotNull(value2); checkArgument(!Objects.equal(value1, value2), "Duplicate value provided."); distinctValues.replaceValues(type, ImmutableList.of(value1, value2)); setDefault(type, value1); return this; } /** * Tests that {@code cls} properly checks null on all constructor and method parameters that * aren't annotated with {@link Nullable}. In details: * <ul> * <li>All non-private static methods are checked such that passing null for any parameter that's * not annotated with {@link javax.annotation.Nullable} should throw {@link * NullPointerException}. * <li>If there is any non-private constructor or non-private static factory method declared by * {@code cls}, all non-private instance methods will be checked too using the instance * created by invoking the constructor or static factory method. * <li>If there is any non-private constructor or non-private static factory method declared by * {@code cls}: * <ul> * <li>Test will fail if default value for a parameter cannot be determined. * <li>Test will fail if the factory method returns null so testing instance methods is * impossible. * <li>Test will fail if the constructor or factory method throws exception. * </ul> * <li>If there is no non-private constructor or non-private static factory method declared by * {@code cls}, instance methods are skipped for nulls test. * <li>Nulls test is not performed on method return values unless the method is a non-private * static factory method whose return type is {@code cls} or {@code cls}'s subtype. * </ul> */ public void testNulls(Class<?> cls) { try { doTestNulls(cls, Visibility.PACKAGE); } catch (Exception e) { throwIfUnchecked(e); throw new RuntimeException(e); } } void doTestNulls(Class<?> cls, Visibility visibility) throws ParameterNotInstantiableException, IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException { if (!Modifier.isAbstract(cls.getModifiers())) { nullPointerTester.testConstructors(cls, visibility); } nullPointerTester.testStaticMethods(cls, visibility); if (hasInstanceMethodToTestNulls(cls, visibility)) { Object instance = instantiate(cls); if (instance != null) { nullPointerTester.testInstanceMethods(instance, visibility); } } } private boolean hasInstanceMethodToTestNulls(Class<?> c, Visibility visibility) { for (Method method : nullPointerTester.getInstanceMethodsToTest(c, visibility)) { for (Parameter param : Invokable.from(method).getParameters()) { if (!NullPointerTester.isPrimitiveOrNullable(param)) { return true; } } } return false; } /** * Tests the {@link Object#equals} and {@link Object#hashCode} of {@code cls}. In details: * <ul> * <li>The non-private constructor or non-private static factory method with the most parameters * is used to construct the sample instances. In case of tie, the candidate constructors or * factories are tried one after another until one can be used to construct sample instances. * <li>For the constructor or static factory method used to construct instances, it's checked that * when equal parameters are passed, the result instance should also be equal; and vice versa. * <li>If a non-private constructor or non-private static factory method exists: <ul> * <li>Test will fail if default value for a parameter cannot be determined. * <li>Test will fail if the factory method returns null so testing instance methods is * impossible. * <li>Test will fail if the constructor or factory method throws exception. * </ul> * <li>If there is no non-private constructor or non-private static factory method declared by * {@code cls}, no test is performed. * <li>Equality test is not performed on method return values unless the method is a non-private * static factory method whose return type is {@code cls} or {@code cls}'s subtype. * <li>Inequality check is not performed against state mutation methods such as {@link List#add}, * or functional update methods such as {@link com.google.common.base.Joiner#skipNulls}. * </ul> * * <p>Note that constructors taking a builder object cannot be tested effectively because * semantics of builder can be arbitrarily complex. Still, a factory class can be created in the * test to facilitate equality testing. For example: <pre> * public class FooTest { * * private static class FooFactoryForTest { * public static Foo create(String a, String b, int c, boolean d) { * return Foo.builder() * .setA(a) * .setB(b) * .setC(c) * .setD(d) * .build(); * } * } * * public void testEquals() { * new ClassSanityTester() * .forAllPublicStaticMethods(FooFactoryForTest.class) * .thatReturn(Foo.class) * .testEquals(); * } * } * </pre> * <p>It will test that Foo objects created by the {@code create(a, b, c, d)} factory method with * equal parameters are equal and vice versa, thus indirectly tests the builder equality. */ public void testEquals(Class<?> cls) { try { doTestEquals(cls); } catch (Exception e) { throwIfUnchecked(e); throw new RuntimeException(e); } } void doTestEquals(Class<?> cls) throws ParameterNotInstantiableException, ParameterHasNoDistinctValueException, IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException { if (cls.isEnum()) { return; } List<? extends Invokable<?, ?>> factories = Lists.reverse(getFactories(TypeToken.of(cls))); if (factories.isEmpty()) { return; } int numberOfParameters = factories.get(0).getParameters().size(); List<ParameterNotInstantiableException> paramErrors = Lists.newArrayList(); List<ParameterHasNoDistinctValueException> distinctValueErrors = Lists.newArrayList(); List<InvocationTargetException> instantiationExceptions = Lists.newArrayList(); List<FactoryMethodReturnsNullException> nullErrors = Lists.newArrayList(); // Try factories with the greatest number of parameters. for (Invokable<?, ?> factory : factories) { if (factory.getParameters().size() == numberOfParameters) { try { testEqualsUsing(factory); return; } catch (ParameterNotInstantiableException e) { paramErrors.add(e); } catch (ParameterHasNoDistinctValueException e) { distinctValueErrors.add(e); } catch (InvocationTargetException e) { instantiationExceptions.add(e); } catch (FactoryMethodReturnsNullException e) { nullErrors.add(e); } } } throwFirst(paramErrors); throwFirst(distinctValueErrors); throwFirst(instantiationExceptions); throwFirst(nullErrors); } /** * Instantiates {@code cls} by invoking one of its non-private constructors or non-private static * factory methods with the parameters automatically provided using dummy values. * * @return The instantiated instance, or {@code null} if the class has no non-private constructor * or factory method to be constructed. */ @Nullable <T> T instantiate(Class<T> cls) throws ParameterNotInstantiableException, IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException { if (cls.isEnum()) { T[] constants = cls.getEnumConstants(); if (constants.length > 0) { return constants[0]; } else { return null; } } TypeToken<T> type = TypeToken.of(cls); List<ParameterNotInstantiableException> paramErrors = Lists.newArrayList(); List<InvocationTargetException> instantiationExceptions = Lists.newArrayList(); List<FactoryMethodReturnsNullException> nullErrors = Lists.newArrayList(); for (Invokable<?, ? extends T> factory : getFactories(type)) { T instance; try { instance = instantiate(factory); } catch (ParameterNotInstantiableException e) { paramErrors.add(e); continue; } catch (InvocationTargetException e) { instantiationExceptions.add(e); continue; } if (instance == null) { nullErrors.add(new FactoryMethodReturnsNullException(factory)); } else { return instance; } } throwFirst(paramErrors); throwFirst(instantiationExceptions); throwFirst(nullErrors); return null; } /** * Returns an object responsible for performing sanity tests against the return values * of all public static methods declared by {@code cls}, excluding superclasses. */ public FactoryMethodReturnValueTester forAllPublicStaticMethods(Class<?> cls) { ImmutableList.Builder<Invokable<?, ?>> builder = ImmutableList.builder(); for (Method method : cls.getDeclaredMethods()) { Invokable<?, ?> invokable = Invokable.from(method); invokable.setAccessible(true); if (invokable.isPublic() && invokable.isStatic() && !invokable.isSynthetic()) { builder.add(invokable); } } return new FactoryMethodReturnValueTester(cls, builder.build(), "public static methods"); } /** Runs sanity tests against return values of static factory methods declared by a class. */ public final class FactoryMethodReturnValueTester { private final Set<String> packagesToTest = Sets.newHashSet(); private final Class<?> declaringClass; private final ImmutableList<Invokable<?, ?>> factories; private final String factoryMethodsDescription; private Class<?> returnTypeToTest = Object.class; private FactoryMethodReturnValueTester( Class<?> declaringClass, ImmutableList<Invokable<?, ?>> factories, String factoryMethodsDescription) { this.declaringClass = declaringClass; this.factories = factories; this.factoryMethodsDescription = factoryMethodsDescription; packagesToTest.add(Reflection.getPackageName(declaringClass)); } /** * Specifies that only the methods that are declared to return {@code returnType} or its subtype * are tested. * * @return this tester object */ public FactoryMethodReturnValueTester thatReturn(Class<?> returnType) { this.returnTypeToTest = returnType; return this; } /** * Tests null checks against the instance methods of the return values, if any. * * <p>Test fails if default value cannot be determined for a constructor or factory method * parameter, or if the constructor or factory method throws exception. * * @return this tester */ public FactoryMethodReturnValueTester testNulls() throws Exception { for (Invokable<?, ?> factory : getFactoriesToTest()) { Object instance = instantiate(factory); if (instance != null && packagesToTest.contains(Reflection.getPackageName(instance.getClass()))) { try { nullPointerTester.testAllPublicInstanceMethods(instance); } catch (AssertionError e) { AssertionError error = new AssertionFailedError( "Null check failed on return value of " + factory); error.initCause(e); throw error; } } } return this; } /** * Tests {@link Object#equals} and {@link Object#hashCode} against the return values of the * static methods, by asserting that when equal parameters are passed to the same static method, * the return value should also be equal; and vice versa. * * <p>Test fails if default value cannot be determined for a constructor or factory method * parameter, or if the constructor or factory method throws exception. * * @return this tester */ public FactoryMethodReturnValueTester testEquals() throws Exception { for (Invokable<?, ?> factory : getFactoriesToTest()) { try { testEqualsUsing(factory); } catch (FactoryMethodReturnsNullException e) { // If the factory returns null, we just skip it. } } return this; } /** * Runs serialization test on the return values of the static methods. * * <p>Test fails if default value cannot be determined for a constructor or factory method * parameter, or if the constructor or factory method throws exception. * * @return this tester */ public FactoryMethodReturnValueTester testSerializable() throws Exception { for (Invokable<?, ?> factory : getFactoriesToTest()) { Object instance = instantiate(factory); if (instance != null) { try { SerializableTester.reserialize(instance); } catch (RuntimeException e) { AssertionError error = new AssertionFailedError( "Serialization failed on return value of " + factory); error.initCause(e.getCause()); throw error; } } } return this; } /** * Runs equals and serialization test on the return values. * * <p>Test fails if default value cannot be determined for a constructor or factory method * parameter, or if the constructor or factory method throws exception. * * @return this tester */ public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Exception { for (Invokable<?, ?> factory : getFactoriesToTest()) { try { testEqualsUsing(factory); } catch (FactoryMethodReturnsNullException e) { // If the factory returns null, we just skip it. } Object instance = instantiate(factory); if (instance != null) { try { SerializableTester.reserializeAndAssert(instance); } catch (RuntimeException e) { AssertionError error = new AssertionFailedError( "Serialization failed on return value of " + factory); error.initCause(e.getCause()); throw error; } catch (AssertionFailedError e) { AssertionError error = new AssertionFailedError( "Return value of " + factory + " reserialized to an unequal value"); error.initCause(e); throw error; } } } return this; } private ImmutableList<Invokable<?, ?>> getFactoriesToTest() { ImmutableList.Builder<Invokable<?, ?>> builder = ImmutableList.builder(); for (Invokable<?, ?> factory : factories) { if (returnTypeToTest.isAssignableFrom(factory.getReturnType().getRawType())) { builder.add(factory); } } ImmutableList<Invokable<?, ?>> factoriesToTest = builder.build(); Assert.assertFalse("No " + factoryMethodsDescription + " that return " + returnTypeToTest.getName() + " or subtype are found in " + declaringClass + ".", factoriesToTest.isEmpty()); return factoriesToTest; } } /** * Instantiates using {@code factory}. If {@code factory} is annotated with {@link Nullable} and * returns null, null will be returned. * * @throws ParameterNotInstantiableException if the static methods cannot be invoked because * the default value of a parameter cannot be determined. * @throws IllegalAccessException if the class isn't public or is nested inside a non-public * class, preventing its methods from being accessible. * @throws InvocationTargetException if a static method threw exception. */ @Nullable private <T> T instantiate(Invokable<?, ? extends T> factory) throws ParameterNotInstantiableException, InvocationTargetException, IllegalAccessException { return invoke(factory, getDummyArguments(factory)); } private void testEqualsUsing(final Invokable<?, ?> factory) throws ParameterNotInstantiableException, ParameterHasNoDistinctValueException, IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException { List<Parameter> params = factory.getParameters(); List<FreshValueGenerator> argGenerators = Lists.newArrayListWithCapacity(params.size()); List<Object> args = Lists.newArrayListWithCapacity(params.size()); for (Parameter param : params) { FreshValueGenerator generator = newFreshValueGenerator(); argGenerators.add(generator); args.add(generateDummyArg(param, generator)); } Object instance = createInstance(factory, args); List<Object> equalArgs = generateEqualFactoryArguments(factory, params, args); // Each group is a List of items, each item has a list of factory args. final List<List<List<Object>>> argGroups = Lists.newArrayList(); argGroups.add(ImmutableList.of(args, equalArgs)); EqualsTester tester = new EqualsTester(new ItemReporter() { @Override String reportItem(Item<?> item) { List<Object> factoryArgs = argGroups.get(item.groupNumber).get(item.itemNumber); return factory.getName() + "(" + Joiner.on(", ").useForNull("null").join(factoryArgs) + ")"; } }); tester.addEqualityGroup(instance, createInstance(factory, equalArgs)); for (int i = 0; i < params.size(); i++) { List<Object> newArgs = Lists.newArrayList(args); Object newArg = argGenerators.get(i).generateFresh(params.get(i).getType()); if (newArg == null || Objects.equal(args.get(i), newArg)) { if (params.get(i).getType().getRawType().isEnum()) { continue; // Nothing better we can do if it's single-value enum } throw new ParameterHasNoDistinctValueException(params.get(i)); } newArgs.set(i, newArg); tester.addEqualityGroup(createInstance(factory, newArgs)); argGroups.add(ImmutableList.of(newArgs)); } tester.testEquals(); } /** * Returns dummy factory arguments that are equal to {@code args} but may be different instances, * to be used to construct a second instance of the same equality group. */ private List<Object> generateEqualFactoryArguments( Invokable<?, ?> factory, List<Parameter> params, List<Object> args) throws ParameterNotInstantiableException, FactoryMethodReturnsNullException, InvocationTargetException, IllegalAccessException { List<Object> equalArgs = Lists.newArrayList(args); for (int i = 0; i < args.size(); i++) { Parameter param = params.get(i); Object arg = args.get(i); // Use new fresh value generator because 'args' were populated with new fresh generator each. // Two newFreshValueGenerator() instances should normally generate equal value sequence. Object shouldBeEqualArg = generateDummyArg(param, newFreshValueGenerator()); if (arg != shouldBeEqualArg && Objects.equal(arg, shouldBeEqualArg) && hashCodeInsensitiveToArgReference(factory, args, i, shouldBeEqualArg) && hashCodeInsensitiveToArgReference( factory, args, i, generateDummyArg(param, newFreshValueGenerator()))) { // If the implementation uses identityHashCode(), referential equality is // probably intended. So no point in using an equal-but-different factory argument. // We check twice to avoid confusion caused by accidental hash collision. equalArgs.set(i, shouldBeEqualArg); } } return equalArgs; } private static boolean hashCodeInsensitiveToArgReference( Invokable<?, ?> factory, List<Object> args, int i, Object alternateArg) throws FactoryMethodReturnsNullException, InvocationTargetException, IllegalAccessException { List<Object> tentativeArgs = Lists.newArrayList(args); tentativeArgs.set(i, alternateArg); return createInstance(factory, tentativeArgs).hashCode() == createInstance(factory, args).hashCode(); } // distinctValues is a type-safe class-values mapping, but we don't have a type-safe data // structure to hold the mappings. @SuppressWarnings({"unchecked", "rawtypes"}) private FreshValueGenerator newFreshValueGenerator() { FreshValueGenerator generator = new FreshValueGenerator() { @Override Object interfaceMethodCalled(Class<?> interfaceType, Method method) { return getDummyValue(TypeToken.of(interfaceType).method(method).getReturnType()); } }; for (Map.Entry<Class<?>, Collection<Object>> entry : distinctValues.asMap().entrySet()) { generator.addSampleInstances((Class) entry.getKey(), entry.getValue()); } return generator; } @Nullable private static Object generateDummyArg(Parameter param, FreshValueGenerator generator) throws ParameterNotInstantiableException { if (param.isAnnotationPresent(Nullable.class)) { return null; } Object arg = generator.generateFresh(param.getType()); if (arg == null) { throw new ParameterNotInstantiableException(param); } return arg; } private static <X extends Throwable> void throwFirst(List<X> exceptions) throws X { if (!exceptions.isEmpty()) { throw exceptions.get(0); } } /** Factories with the least number of parameters are listed first. */ private static <T> ImmutableList<Invokable<?, ? extends T>> getFactories(TypeToken<T> type) { List<Invokable<?, ? extends T>> factories = Lists.newArrayList(); for (Method method : type.getRawType().getDeclaredMethods()) { Invokable<?, ?> invokable = type.method(method); if (!invokable.isPrivate() && !invokable.isSynthetic() && invokable.isStatic() && type.isSupertypeOf(invokable.getReturnType())) { @SuppressWarnings("unchecked") // guarded by isAssignableFrom() Invokable<?, ? extends T> factory = (Invokable<?, ? extends T>) invokable; factories.add(factory); } } if (!Modifier.isAbstract(type.getRawType().getModifiers())) { for (Constructor<?> constructor : type.getRawType().getDeclaredConstructors()) { Invokable<T, T> invokable = type.constructor(constructor); if (!invokable.isPrivate() && !invokable.isSynthetic()) { factories.add(invokable); } } } for (Invokable<?, ?> factory : factories) { factory.setAccessible(true); } // Sorts methods/constructors with least number of parameters first since it's likely easier to // fill dummy parameter values for them. Ties are broken by name then by the string form of the // parameter list. return BY_NUMBER_OF_PARAMETERS.compound(BY_METHOD_NAME).compound(BY_PARAMETERS) .immutableSortedCopy(factories); } private List<Object> getDummyArguments(Invokable<?, ?> invokable) throws ParameterNotInstantiableException { List<Object> args = Lists.newArrayList(); for (Parameter param : invokable.getParameters()) { if (param.isAnnotationPresent(Nullable.class)) { args.add(null); continue; } Object defaultValue = getDummyValue(param.getType()); if (defaultValue == null) { throw new ParameterNotInstantiableException(param); } args.add(defaultValue); } return args; } private <T> T getDummyValue(TypeToken<T> type) { Class<? super T> rawType = type.getRawType(); @SuppressWarnings("unchecked") // Assume all default values are generics safe. T defaultValue = (T) defaultValues.getInstance(rawType); if (defaultValue != null) { return defaultValue; } @SuppressWarnings("unchecked") // ArbitraryInstances always returns generics-safe dummies. T value = (T) ArbitraryInstances.get(rawType); if (value != null) { return value; } if (rawType.isInterface()) { return new SerializableDummyProxy(this).newProxy(type); } return null; } private static <T> T createInstance(Invokable<?, ? extends T> factory, List<?> args) throws FactoryMethodReturnsNullException, InvocationTargetException, IllegalAccessException { T instance = invoke(factory, args); if (instance == null) { throw new FactoryMethodReturnsNullException(factory); } return instance; } @Nullable private static <T> T invoke(Invokable<?, ? extends T> factory, List<?> args) throws InvocationTargetException, IllegalAccessException { T returnValue = factory.invoke(null, args.toArray()); if (returnValue == null) { Assert.assertTrue(factory + " returns null but it's not annotated with @Nullable", factory.isAnnotationPresent(Nullable.class)); } return returnValue; } /** * Thrown if the test tries to invoke a constructor or static factory method but failed because * the dummy value of a constructor or method parameter is unknown. */ @VisibleForTesting static class ParameterNotInstantiableException extends Exception { public ParameterNotInstantiableException(Parameter parameter) { super("Cannot determine value for parameter " + parameter + " of " + parameter.getDeclaringInvokable()); } } /** * Thrown if the test fails to generate two distinct non-null values of a constructor or factory * parameter in order to test {@link Object#equals} and {@link Object#hashCode} of the declaring * class. */ @VisibleForTesting static class ParameterHasNoDistinctValueException extends Exception { ParameterHasNoDistinctValueException(Parameter parameter) { super("Cannot generate distinct value for parameter " + parameter + " of " + parameter.getDeclaringInvokable()); } } /** * Thrown if the test tries to invoke a static factory method to test instance methods but the * factory returned null. */ @VisibleForTesting static class FactoryMethodReturnsNullException extends Exception { public FactoryMethodReturnsNullException(Invokable<?, ?> factory) { super(factory + " returns null and cannot be used to test instance methods."); } } private static final class SerializableDummyProxy extends DummyProxy implements Serializable { private final transient ClassSanityTester tester; SerializableDummyProxy(ClassSanityTester tester) { this.tester = tester; } @Override <R> R dummyReturnValue(TypeToken<R> returnType) { return tester.getDummyValue(returnType); } @Override public boolean equals(Object obj) { return obj instanceof SerializableDummyProxy; } @Override public int hashCode() { return 0; } } }