aboutsummaryrefslogtreecommitdiffstats
path: root/guava-testlib/src/com/google/common/testing/NullPointerTester.java
diff options
context:
space:
mode:
Diffstat (limited to 'guava-testlib/src/com/google/common/testing/NullPointerTester.java')
-rw-r--r--guava-testlib/src/com/google/common/testing/NullPointerTester.java422
1 files changed, 154 insertions, 268 deletions
diff --git a/guava-testlib/src/com/google/common/testing/NullPointerTester.java b/guava-testlib/src/com/google/common/testing/NullPointerTester.java
index b643182..1142056 100644
--- a/guava-testlib/src/com/google/common/testing/NullPointerTester.java
+++ b/guava-testlib/src/com/google/common/testing/NullPointerTester.java
@@ -16,149 +16,146 @@
package com.google.common.testing;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
import com.google.common.annotations.Beta;
-import com.google.common.base.Objects;
-import com.google.common.collect.ClassToInstanceMap;
-import com.google.common.collect.ImmutableList;
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import com.google.common.collect.MutableClassToInstanceMap;
-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 junit.framework.Assert;
import junit.framework.AssertionFailedError;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
import java.util.List;
-import java.util.concurrent.ConcurrentMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
import javax.annotation.Nullable;
/**
- * A test utility that verifies that your methods and constructors throw {@link
- * NullPointerException} or {@link UnsupportedOperationException} whenever null
- * is passed to a parameter that isn't annotated with {@link Nullable}.
- *
- * <p>The tested methods and constructors are invoked -- each time with one
- * parameter being null and the rest not null -- and the test fails if no
- * expected exception is thrown. {@code NullPointerTester} uses best effort to
- * pick non-null default values for many common JDK and Guava types, and also
- * for interfaces and public classes that have public parameter-less
- * constructors. When the non-null default value for a particular parameter type
- * cannot be provided by {@code NullPointerTester}, the caller can provide a
- * custom non-null default value for the parameter type via {@link #setDefault}.
+ * A test utility that verifies that your methods throw {@link
+ * NullPointerException} or {@link UnsupportedOperationException} whenever any
+ * of their parameters are null. To use it, you must first provide valid default
+ * values for the parameter types used by the class.
*
* @author Kevin Bourrillion
* @since 10.0
*/
@Beta
public final class NullPointerTester {
-
- private final ClassToInstanceMap<Object> defaults =
- MutableClassToInstanceMap.create();
+ private final Map<Class<?>, Object> defaults = Maps.newHashMap();
private final List<Member> ignoredMembers = Lists.newArrayList();
+ public NullPointerTester() {
+ setCommonDefaults();
+ }
+
+ private final void setCommonDefaults() {
+ setDefault(Appendable.class, new StringBuilder());
+ setDefault(CharSequence.class, "");
+ setDefault(Class.class, Class.class);
+ setDefault(Collection.class, Collections.emptySet());
+ setDefault(Comparable.class, 0);
+ setDefault(Comparator.class, Collections.reverseOrder());
+ setDefault(Function.class, Functions.identity());
+ setDefault(Integer.class, 0);
+ setDefault(Iterable.class, Collections.emptySet());
+ setDefault(Iterator.class, Iterators.emptyIterator());
+ setDefault(List.class, Collections.emptyList());
+ setDefault(Map.class, Collections.emptyMap());
+ setDefault(Object.class, new Object());
+ setDefault(Object[].class, new Object[0]);
+ setDefault(Pattern.class, Pattern.compile(""));
+ setDefault(Predicate.class, Predicates.alwaysTrue());
+ setDefault(Set.class, Collections.emptySet());
+ setDefault(SortedSet.class, new TreeSet());
+ setDefault(String.class, "");
+ setDefault(Supplier.class, Suppliers.ofInstance(1));
+ setDefault(Throwable.class, new Exception());
+ setDefault(TimeUnit.class, TimeUnit.SECONDS);
+ setDefault(int.class, 0);
+ setDefault(long.class, 0L);
+ setDefault(short.class, (short) 0);
+ setDefault(char.class, 'a');
+ setDefault(byte.class, (byte) 0);
+ setDefault(float.class, 0.0f);
+ setDefault(double.class, 0.0d);
+ setDefault(boolean.class, false);
+ }
+
/**
* Sets a default value that can be used for any parameter of type
* {@code type}. Returns this object.
*/
public <T> NullPointerTester setDefault(Class<T> type, T value) {
- defaults.putInstance(type, checkNotNull(value));
+ defaults.put(type, value);
return this;
}
/**
- * Ignore {@code method} in the tests that follow. Returns this object.
- *
- * @since 13.0
+ * Ignore a member (constructor or method) in testAllXxx methods. Returns
+ * this object.
*/
- public NullPointerTester ignore(Method method) {
- ignoredMembers.add(checkNotNull(method));
+ public NullPointerTester ignore(Member member) {
+ ignoredMembers.add(member);
return this;
}
/**
- * Runs {@link #testConstructor} on every constructor in class {@code c} that
- * has at least {@code minimalVisibility}.
+ * Runs {@link #testConstructor} on every public constructor in class {@code
+ * c}.
*/
- public void testConstructors(Class<?> c, Visibility minimalVisibility) {
+ public void testAllPublicConstructors(Class<?> c) throws Exception {
for (Constructor<?> constructor : c.getDeclaredConstructors()) {
- if (minimalVisibility.isVisible(constructor) && !isIgnored(constructor)) {
+ if (isPublic(constructor) && !isStatic(constructor)
+ && !isIgnored(constructor)) {
testConstructor(constructor);
}
}
}
/**
- * Runs {@link #testConstructor} on every public constructor in class {@code
- * c}.
- */
- public void testAllPublicConstructors(Class<?> c) {
- testConstructors(c, Visibility.PUBLIC);
- }
-
- /**
- * Runs {@link #testMethod} on every static method of class {@code c} that has
- * at least {@code minimalVisibility}, including those "inherited" from
- * superclasses of the same package.
+ * Runs {@link #testMethod} on every public static method in class
+ * {@code c}.
*/
- public void testStaticMethods(Class<?> c, Visibility minimalVisibility) {
- for (Method method : minimalVisibility.getStaticMethods(c)) {
- if (!isIgnored(method)) {
+ public void testAllPublicStaticMethods(Class<?> c) throws Exception {
+ for (Method method : c.getDeclaredMethods()) {
+ if (isPublic(method) && isStatic(method) && !isIgnored(method)) {
testMethod(null, method);
}
}
}
/**
- * Runs {@link #testMethod} on every public static method of class {@code c},
- * including those "inherited" from superclasses of the same package.
- */
- public void testAllPublicStaticMethods(Class<?> c) {
- testStaticMethods(c, Visibility.PUBLIC);
- }
-
- /**
- * Runs {@link #testMethod} on every instance method of the class of
- * {@code instance} with at least {@code minimalVisibility}, including those
- * inherited from superclasses of the same package.
+ * Runs {@link #testMethod} on every public instance method of
+ * {@code instance}.
*/
- public void testInstanceMethods(Object instance, Visibility minimalVisibility) {
- for (Method method : getInstanceMethodsToTest(instance.getClass(), minimalVisibility)) {
- testMethod(instance, method);
- }
- }
-
- ImmutableList<Method> getInstanceMethodsToTest(Class<?> c, Visibility minimalVisibility) {
- ImmutableList.Builder<Method> builder = ImmutableList.builder();
- for (Method method : minimalVisibility.getInstanceMethods(c)) {
- if (!isIgnored(method)) {
- builder.add(method);
+ public void testAllPublicInstanceMethods(Object instance) throws Exception {
+ Class<?> c = instance.getClass();
+ for (Method method : c.getDeclaredMethods()) {
+ if (isPublic(method) && !isStatic(method) && !isIgnored(method)) {
+ testMethod(instance, method);
}
}
- return builder.build();
- }
-
- /**
- * Runs {@link #testMethod} on every public instance method of the class of
- * {@code instance}, including those inherited from superclasses of the same
- * package.
- */
- public void testAllPublicInstanceMethods(Object instance) {
- testInstanceMethods(instance, Visibility.PUBLIC);
}
/**
@@ -169,7 +166,7 @@ public final class NullPointerTester {
* @param instance the instance to invoke {@code method} on, or null if
* {@code method} is static
*/
- public void testMethod(@Nullable Object instance, Method method) {
+ public void testMethod(Object instance, Method method) throws Exception {
Class<?>[] types = method.getParameterTypes();
for (int nullIndex = 0; nullIndex < types.length; nullIndex++) {
testMethodParameter(instance, method, nullIndex);
@@ -181,11 +178,7 @@ public final class NullPointerTester {
* {@link UnsupportedOperationException} whenever <i>any</i> of its
* non-{@link Nullable} parameters are null.
*/
- public void testConstructor(Constructor<?> ctor) {
- Class<?> declaringClass = ctor.getDeclaringClass();
- checkArgument(Modifier.isStatic(declaringClass.getModifiers())
- || declaringClass.getEnclosingClass() == null,
- "Cannot test constructor of non-static inner class: %s", declaringClass.getName());
+ public void testConstructor(Constructor<?> ctor) throws Exception {
Class<?>[] types = ctor.getParameterTypes();
for (int nullIndex = 0; nullIndex < types.length; nullIndex++) {
testConstructorParameter(ctor, nullIndex);
@@ -201,10 +194,25 @@ public final class NullPointerTester {
* @param instance the instance to invoke {@code method} on, or null if
* {@code method} is static
*/
- public void testMethodParameter(
- @Nullable final Object instance, final Method method, int paramIndex) {
+ public void testMethodParameter(Object instance, final Method method,
+ int paramIndex) throws Exception {
method.setAccessible(true);
- testParameter(instance, invokable(instance, method), paramIndex, method.getDeclaringClass());
+ testFunctorParameter(instance, new Functor() {
+ @Override public Class<?>[] getParameterTypes() {
+ return method.getParameterTypes();
+ }
+ @Override public Annotation[][] getParameterAnnotations() {
+ return method.getParameterAnnotations();
+ }
+ @Override public void invoke(Object instance, Object[] params)
+ throws InvocationTargetException, IllegalAccessException {
+ method.invoke(instance, params);
+ }
+ @Override public String toString() {
+ return method.getName()
+ + "(" + Arrays.toString(getParameterTypes()) + ")";
+ }
+ }, paramIndex, method.getDeclaringClass());
}
/**
@@ -213,129 +221,42 @@ public final class NullPointerTester {
* paramIndex} is null. If this parameter is marked {@link Nullable}, this
* method does nothing.
*/
- public void testConstructorParameter(Constructor<?> ctor, int paramIndex) {
+ public void testConstructorParameter(final Constructor<?> ctor,
+ int paramIndex) throws Exception {
ctor.setAccessible(true);
- testParameter(null, Invokable.from(ctor), paramIndex, ctor.getDeclaringClass());
- }
-
- /** Visibility of any method or constructor. */
- public enum Visibility {
-
- PACKAGE {
- @Override boolean isVisible(int modifiers) {
- return !Modifier.isPrivate(modifiers);
- }
- },
-
- PROTECTED {
- @Override boolean isVisible(int modifiers) {
- return Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers);
- }
- },
-
- PUBLIC {
- @Override boolean isVisible(int modifiers) {
- return Modifier.isPublic(modifiers);
- }
- };
-
- abstract boolean isVisible(int modifiers);
-
- /**
- * Returns {@code true} if {@code member} is visible under {@code this}
- * visibility.
- */
- final boolean isVisible(Member member) {
- return isVisible(member.getModifiers());
- }
-
- final Iterable<Method> getStaticMethods(Class<?> cls) {
- ImmutableList.Builder<Method> builder = ImmutableList.builder();
- for (Method method : getVisibleMethods(cls)) {
- if (Invokable.from(method).isStatic()) {
- builder.add(method);
- }
- }
- return builder.build();
- }
-
- final Iterable<Method> getInstanceMethods(Class<?> cls) {
- ConcurrentMap<Signature, Method> map = Maps.newConcurrentMap();
- for (Method method : getVisibleMethods(cls)) {
- if (!Invokable.from(method).isStatic()) {
- map.putIfAbsent(new Signature(method), method);
+ testFunctorParameter(null, new Functor() {
+ @Override public Class<?>[] getParameterTypes() {
+ return ctor.getParameterTypes();
}
- }
- return map.values();
- }
-
- private ImmutableList<Method> getVisibleMethods(Class<?> cls) {
- // Don't use cls.getPackage() because it does nasty things like reading
- // a file.
- String visiblePackage = Reflection.getPackageName(cls);
- ImmutableList.Builder<Method> builder = ImmutableList.builder();
- for (Class<?> type : TypeToken.of(cls).getTypes().classes().rawTypes()) {
- if (!Reflection.getPackageName(type).equals(visiblePackage)) {
- break;
+ @Override public Annotation[][] getParameterAnnotations() {
+ return ctor.getParameterAnnotations();
}
- for (Method method : type.getDeclaredMethods()) {
- if (!method.isSynthetic() && isVisible(method)) {
- builder.add(method);
- }
+ @Override public void invoke(Object instance, Object[] params)
+ throws InvocationTargetException, IllegalAccessException,
+ InstantiationException {
+ ctor.newInstance(params);
}
- }
- return builder.build();
- }
- }
-
- // TODO(benyu): Use labs/reflect/Signature if it graduates.
- private static final class Signature {
- private final String name;
- private final ImmutableList<Class<?>> parameterTypes;
-
- Signature(Method method) {
- this(method.getName(), ImmutableList.copyOf(method.getParameterTypes()));
- }
-
- Signature(String name, ImmutableList<Class<?>> parameterTypes) {
- this.name = name;
- this.parameterTypes = parameterTypes;
- }
-
- @Override public boolean equals(Object obj) {
- if (obj instanceof Signature) {
- Signature that = (Signature) obj;
- return name.equals(that.name)
- && parameterTypes.equals(that.parameterTypes);
- }
- return false;
- }
-
- @Override public int hashCode() {
- return Objects.hashCode(name, parameterTypes);
- }
+ }, paramIndex, ctor.getDeclaringClass());
}
/**
- * Verifies that {@code invokable} produces a {@link NullPointerException} or
+ * Verifies that {@code func} produces a {@link NullPointerException} or
* {@link UnsupportedOperationException} when the parameter in position {@code
* paramIndex} is null. If this parameter is marked {@link Nullable}, this
* method does nothing.
*
- * @param instance the instance to invoke {@code invokable} on, or null if
- * {@code invokable} is static
+ * @param instance the instance to invoke {@code func} on, or null if
+ * {@code func} is static
*/
- private void testParameter(Object instance, Invokable<?, ?> invokable,
- int paramIndex, Class<?> testedClass) {
- if (isPrimitiveOrNullable(invokable.getParameters().get(paramIndex))) {
+ private void testFunctorParameter(Object instance, Functor func,
+ int paramIndex, Class<?> testedClass) throws Exception {
+ if (parameterIsPrimitiveOrNullable(func, paramIndex)) {
return; // there's nothing to test
}
- Object[] params = buildParamList(invokable, paramIndex);
+ Object[] params = buildParamList(func, paramIndex);
try {
- @SuppressWarnings("unchecked") // We'll get a runtime exception if the type is wrong.
- Invokable<Object, ?> unsafe = (Invokable<Object, ?>) invokable;
- unsafe.invoke(instance, params);
- Assert.fail("No exception thrown from " + invokable +
+ func.invoke(instance, params);
+ Assert.fail("No exception thrown from " + func +
Arrays.toString(params) + " for " + testedClass);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
@@ -344,89 +265,54 @@ public final class NullPointerTester {
return;
}
AssertionFailedError error = new AssertionFailedError(
- "wrong exception thrown from " + invokable + ": " + cause);
+ "wrong exception thrown from " + func + ": " + cause);
error.initCause(cause);
throw error;
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
}
}
- private Object[] buildParamList(Invokable<?, ?> invokable, int indexOfParamToSetToNull) {
- ImmutableList<Parameter> params = invokable.getParameters();
- Object[] args = new Object[params.size()];
-
- for (int i = 0; i < args.length; i++) {
- Parameter param = params.get(i);
- if (i != indexOfParamToSetToNull) {
- args[i] = getDefaultValue(param.getType());
- if (!isPrimitiveOrNullable(param)) {
- Assert.assertTrue("No default value found for " + param + " of "+ invokable,
- args[i] != null);
- }
+ private static boolean parameterIsPrimitiveOrNullable(
+ Functor func, int paramIndex) {
+ if (func.getParameterTypes()[paramIndex].isPrimitive()) {
+ return true;
+ }
+ Annotation[] annotations = func.getParameterAnnotations()[paramIndex];
+ for (Annotation annotation : annotations) {
+ if (annotation instanceof Nullable) {
+ return true;
}
}
- return args;
+ return false;
}
- private <T> T getDefaultValue(TypeToken<T> type) {
- // We assume that all defaults are generics-safe, even if they aren't,
- // we take the risk.
- @SuppressWarnings("unchecked")
- T defaultValue = (T) defaults.getInstance(type.getRawType());
- if (defaultValue != null) {
- return defaultValue;
- }
- @SuppressWarnings("unchecked") // All null values are generics-safe
- T nullValue = (T) ArbitraryInstances.get(type.getRawType());
- if (nullValue != null) {
- return nullValue;
- }
- if (type.getRawType() == Class.class) {
- // If parameter is Class<? extends Foo>, we return Foo.class
- @SuppressWarnings("unchecked")
- T defaultClass = (T) getFirstTypeParameter(type.getType()).getRawType();
- return defaultClass;
- }
- if (type.getRawType() == TypeToken.class) {
- // If parameter is TypeToken<? extends Foo>, we return TypeToken<Foo>.
- @SuppressWarnings("unchecked")
- T defaultType = (T) getFirstTypeParameter(type.getType());
- return defaultType;
- }
- if (type.getRawType().isInterface()) {
- return newDefaultReturningProxy(type);
- }
- return null;
- }
+ private Object[] buildParamList(Functor func, int indexOfParamToSetToNull) {
+ Class<?>[] types = func.getParameterTypes();
+ Object[] params = new Object[types.length];
- private static TypeToken<?> getFirstTypeParameter(Type type) {
- if (type instanceof ParameterizedType) {
- return TypeToken.of(
- ((ParameterizedType) type).getActualTypeArguments()[0]);
- } else {
- return TypeToken.of(Object.class);
+ for (int i = 0; i < types.length; i++) {
+ if (i != indexOfParamToSetToNull) {
+ params[i] = defaults.get(types[i]);
+ if (!parameterIsPrimitiveOrNullable(func, i)) {
+ Assert.assertTrue("No default value found for " + types[i].getName(),
+ params[i] != null);
+ }
+ }
}
+ return params;
}
- private <T> T newDefaultReturningProxy(final TypeToken<T> type) {
- return new DummyProxy() {
- @Override <R> R dummyReturnValue(TypeToken<R> returnType) {
- return getDefaultValue(returnType);
- }
- }.newProxy(type);
+ private interface Functor {
+ Class<?>[] getParameterTypes();
+ Annotation[][] getParameterAnnotations();
+ void invoke(Object o, Object[] params) throws Exception;
}
- private static Invokable<?, ?> invokable(@Nullable Object instance, Method method) {
- if (instance == null) {
- return Invokable.from(method);
- } else {
- return TypeToken.of(instance.getClass()).method(method);
- }
+ private static boolean isPublic(Member member) {
+ return Modifier.isPublic(member.getModifiers());
}
- static boolean isPrimitiveOrNullable(Parameter param) {
- return param.getType().getRawType().isPrimitive() || param.isAnnotationPresent(Nullable.class);
+ private static boolean isStatic(Member member) {
+ return Modifier.isStatic(member.getModifiers());
}
private boolean isIgnored(Member member) {