11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/*
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2005 The Guava Authors
31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * you may not use this file except in compliance with the License.
61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * You may obtain a copy of the License at
71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0
91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * See the License for the specific language governing permissions and
141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * limitations under the License.
151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpackage com.google.common.testing;
181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
197dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static com.google.common.base.Preconditions.checkArgument;
207dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static com.google.common.base.Preconditions.checkNotNull;
217dd252788645e940eada959bdde927426e2531c9Paul Duffin
221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.annotations.Beta;
230888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.base.Converter;
247dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.base.Objects;
257dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.ClassToInstanceMap;
267dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.ImmutableList;
271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.Lists;
281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.Maps;
297dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.MutableClassToInstanceMap;
307dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.reflect.Invokable;
317dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.reflect.Parameter;
327dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.reflect.Reflection;
337dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.reflect.TypeToken;
341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport junit.framework.Assert;
361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport junit.framework.AssertionFailedError;
371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.lang.reflect.Constructor;
391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.lang.reflect.InvocationTargetException;
401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.lang.reflect.Member;
411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.lang.reflect.Method;
421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.lang.reflect.Modifier;
437dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.lang.reflect.ParameterizedType;
447dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.lang.reflect.Type;
451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.Arrays;
461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.List;
477dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.ConcurrentMap;
481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport javax.annotation.Nullable;
501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/**
527dd252788645e940eada959bdde927426e2531c9Paul Duffin * A test utility that verifies that your methods and constructors throw {@link
537dd252788645e940eada959bdde927426e2531c9Paul Duffin * NullPointerException} or {@link UnsupportedOperationException} whenever null
547dd252788645e940eada959bdde927426e2531c9Paul Duffin * is passed to a parameter that isn't annotated with {@link Nullable}.
557dd252788645e940eada959bdde927426e2531c9Paul Duffin *
567dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>The tested methods and constructors are invoked -- each time with one
577dd252788645e940eada959bdde927426e2531c9Paul Duffin * parameter being null and the rest not null -- and the test fails if no
587dd252788645e940eada959bdde927426e2531c9Paul Duffin * expected exception is thrown. {@code NullPointerTester} uses best effort to
597dd252788645e940eada959bdde927426e2531c9Paul Duffin * pick non-null default values for many common JDK and Guava types, and also
607dd252788645e940eada959bdde927426e2531c9Paul Duffin * for interfaces and public classes that have public parameter-less
617dd252788645e940eada959bdde927426e2531c9Paul Duffin * constructors. When the non-null default value for a particular parameter type
627dd252788645e940eada959bdde927426e2531c9Paul Duffin * cannot be provided by {@code NullPointerTester}, the caller can provide a
637dd252788645e940eada959bdde927426e2531c9Paul Duffin * custom non-null default value for the parameter type via {@link #setDefault}.
641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author Kevin Bourrillion
661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 10.0
671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert@Beta
691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic final class NullPointerTester {
70dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin
717dd252788645e940eada959bdde927426e2531c9Paul Duffin  private final ClassToInstanceMap<Object> defaults =
727dd252788645e940eada959bdde927426e2531c9Paul Duffin      MutableClassToInstanceMap.create();
737dd252788645e940eada959bdde927426e2531c9Paul Duffin  private final List<Member> ignoredMembers = Lists.newArrayList();
74dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin
750888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private ExceptionTypePolicy policy = ExceptionTypePolicy.NPE_OR_UOE;
760888a09821a98ac0680fad765217302858e70fa4Paul Duffin
771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Sets a default value that can be used for any parameter of type
791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * {@code type}. Returns this object.
801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public <T> NullPointerTester setDefault(Class<T> type, T value) {
827dd252788645e940eada959bdde927426e2531c9Paul Duffin    defaults.putInstance(type, checkNotNull(value));
831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return this;
841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
877dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Ignore {@code method} in the tests that follow. Returns this object.
887dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
897dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @since 13.0
901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
917dd252788645e940eada959bdde927426e2531c9Paul Duffin  public NullPointerTester ignore(Method method) {
927dd252788645e940eada959bdde927426e2531c9Paul Duffin    ignoredMembers.add(checkNotNull(method));
931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return this;
941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
977dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Runs {@link #testConstructor} on every constructor in class {@code c} that
987dd252788645e940eada959bdde927426e2531c9Paul Duffin   * has at least {@code minimalVisibility}.
991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
1007dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testConstructors(Class<?> c, Visibility minimalVisibility) {
1011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (Constructor<?> constructor : c.getDeclaredConstructors()) {
1027dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (minimalVisibility.isVisible(constructor) && !isIgnored(constructor)) {
1031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        testConstructor(constructor);
1041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
1051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
1097dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Runs {@link #testConstructor} on every public constructor in class {@code
1107dd252788645e940eada959bdde927426e2531c9Paul Duffin   * c}.
1117dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1127dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testAllPublicConstructors(Class<?> c) {
1137dd252788645e940eada959bdde927426e2531c9Paul Duffin    testConstructors(c, Visibility.PUBLIC);
1147dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1157dd252788645e940eada959bdde927426e2531c9Paul Duffin
1167dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1177dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Runs {@link #testMethod} on every static method of class {@code c} that has
1187dd252788645e940eada959bdde927426e2531c9Paul Duffin   * at least {@code minimalVisibility}, including those "inherited" from
1197dd252788645e940eada959bdde927426e2531c9Paul Duffin   * superclasses of the same package.
1201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
1217dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testStaticMethods(Class<?> c, Visibility minimalVisibility) {
1227dd252788645e940eada959bdde927426e2531c9Paul Duffin    for (Method method : minimalVisibility.getStaticMethods(c)) {
1237dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (!isIgnored(method)) {
1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        testMethod(null, method);
1251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
1307dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Runs {@link #testMethod} on every public static method of class {@code c},
1317dd252788645e940eada959bdde927426e2531c9Paul Duffin   * including those "inherited" from superclasses of the same package.
1327dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1337dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testAllPublicStaticMethods(Class<?> c) {
1347dd252788645e940eada959bdde927426e2531c9Paul Duffin    testStaticMethods(c, Visibility.PUBLIC);
1357dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1367dd252788645e940eada959bdde927426e2531c9Paul Duffin
1377dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1387dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Runs {@link #testMethod} on every instance method of the class of
1397dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@code instance} with at least {@code minimalVisibility}, including those
1407dd252788645e940eada959bdde927426e2531c9Paul Duffin   * inherited from superclasses of the same package.
1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
1427dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testInstanceMethods(Object instance, Visibility minimalVisibility) {
1437dd252788645e940eada959bdde927426e2531c9Paul Duffin    for (Method method : getInstanceMethodsToTest(instance.getClass(), minimalVisibility)) {
1447dd252788645e940eada959bdde927426e2531c9Paul Duffin      testMethod(instance, method);
1457dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1467dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1477dd252788645e940eada959bdde927426e2531c9Paul Duffin
1487dd252788645e940eada959bdde927426e2531c9Paul Duffin  ImmutableList<Method> getInstanceMethodsToTest(Class<?> c, Visibility minimalVisibility) {
1497dd252788645e940eada959bdde927426e2531c9Paul Duffin    ImmutableList.Builder<Method> builder = ImmutableList.builder();
1507dd252788645e940eada959bdde927426e2531c9Paul Duffin    for (Method method : minimalVisibility.getInstanceMethods(c)) {
1517dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (!isIgnored(method)) {
1527dd252788645e940eada959bdde927426e2531c9Paul Duffin        builder.add(method);
1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1557dd252788645e940eada959bdde927426e2531c9Paul Duffin    return builder.build();
1567dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1577dd252788645e940eada959bdde927426e2531c9Paul Duffin
1587dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1597dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Runs {@link #testMethod} on every public instance method of the class of
1607dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@code instance}, including those inherited from superclasses of the same
1617dd252788645e940eada959bdde927426e2531c9Paul Duffin   * package.
1627dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1637dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testAllPublicInstanceMethods(Object instance) {
1647dd252788645e940eada959bdde927426e2531c9Paul Duffin    testInstanceMethods(instance, Visibility.PUBLIC);
1651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
1681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Verifies that {@code method} produces a {@link NullPointerException}
1691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * or {@link UnsupportedOperationException} whenever <i>any</i> of its
1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * non-{@link Nullable} parameters are null.
1711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *
1721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * @param instance the instance to invoke {@code method} on, or null if
1731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *     {@code method} is static
1741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
1757dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testMethod(@Nullable Object instance, Method method) {
1761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Class<?>[] types = method.getParameterTypes();
1771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int nullIndex = 0; nullIndex < types.length; nullIndex++) {
1781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      testMethodParameter(instance, method, nullIndex);
1791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
1831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Verifies that {@code ctor} produces a {@link NullPointerException} or
1841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * {@link UnsupportedOperationException} whenever <i>any</i> of its
1851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * non-{@link Nullable} parameters are null.
1861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
1877dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testConstructor(Constructor<?> ctor) {
1887dd252788645e940eada959bdde927426e2531c9Paul Duffin    Class<?> declaringClass = ctor.getDeclaringClass();
1897dd252788645e940eada959bdde927426e2531c9Paul Duffin    checkArgument(Modifier.isStatic(declaringClass.getModifiers())
1907dd252788645e940eada959bdde927426e2531c9Paul Duffin        || declaringClass.getEnclosingClass() == null,
1917dd252788645e940eada959bdde927426e2531c9Paul Duffin        "Cannot test constructor of non-static inner class: %s", declaringClass.getName());
1921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Class<?>[] types = ctor.getParameterTypes();
1931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int nullIndex = 0; nullIndex < types.length; nullIndex++) {
1941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      testConstructorParameter(ctor, nullIndex);
1951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
1991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Verifies that {@code method} produces a {@link NullPointerException} or
2001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * {@link UnsupportedOperationException} when the parameter in position {@code
2011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * paramIndex} is null.  If this parameter is marked {@link Nullable}, this
2021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * method does nothing.
2031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *
2041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * @param instance the instance to invoke {@code method} on, or null if
2051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *     {@code method} is static
2061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
2077dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testMethodParameter(
2087dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Nullable final Object instance, final Method method, int paramIndex) {
2091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    method.setAccessible(true);
2107dd252788645e940eada959bdde927426e2531c9Paul Duffin    testParameter(instance, invokable(instance, method), paramIndex, method.getDeclaringClass());
2111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
2141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Verifies that {@code ctor} produces a {@link NullPointerException} or
2151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * {@link UnsupportedOperationException} when the parameter in position {@code
2161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * paramIndex} is null.  If this parameter is marked {@link Nullable}, this
2171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * method does nothing.
2181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
2197dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testConstructorParameter(Constructor<?> ctor, int paramIndex) {
2201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ctor.setAccessible(true);
2217dd252788645e940eada959bdde927426e2531c9Paul Duffin    testParameter(null, Invokable.from(ctor), paramIndex, ctor.getDeclaringClass());
2227dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2237dd252788645e940eada959bdde927426e2531c9Paul Duffin
2247dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Visibility of any method or constructor. */
2257dd252788645e940eada959bdde927426e2531c9Paul Duffin  public enum Visibility {
2267dd252788645e940eada959bdde927426e2531c9Paul Duffin
2277dd252788645e940eada959bdde927426e2531c9Paul Duffin    PACKAGE {
2287dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override boolean isVisible(int modifiers) {
2297dd252788645e940eada959bdde927426e2531c9Paul Duffin        return !Modifier.isPrivate(modifiers);
2307dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2317dd252788645e940eada959bdde927426e2531c9Paul Duffin    },
2327dd252788645e940eada959bdde927426e2531c9Paul Duffin
2337dd252788645e940eada959bdde927426e2531c9Paul Duffin    PROTECTED {
2347dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override boolean isVisible(int modifiers) {
2357dd252788645e940eada959bdde927426e2531c9Paul Duffin        return Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers);
2367dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2377dd252788645e940eada959bdde927426e2531c9Paul Duffin    },
2387dd252788645e940eada959bdde927426e2531c9Paul Duffin
2397dd252788645e940eada959bdde927426e2531c9Paul Duffin    PUBLIC {
2407dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override boolean isVisible(int modifiers) {
2417dd252788645e940eada959bdde927426e2531c9Paul Duffin        return Modifier.isPublic(modifiers);
2427dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2437dd252788645e940eada959bdde927426e2531c9Paul Duffin    };
2447dd252788645e940eada959bdde927426e2531c9Paul Duffin
2457dd252788645e940eada959bdde927426e2531c9Paul Duffin    abstract boolean isVisible(int modifiers);
2467dd252788645e940eada959bdde927426e2531c9Paul Duffin
2477dd252788645e940eada959bdde927426e2531c9Paul Duffin    /**
2487dd252788645e940eada959bdde927426e2531c9Paul Duffin     * Returns {@code true} if {@code member} is visible under {@code this}
2497dd252788645e940eada959bdde927426e2531c9Paul Duffin     * visibility.
2507dd252788645e940eada959bdde927426e2531c9Paul Duffin     */
2517dd252788645e940eada959bdde927426e2531c9Paul Duffin    final boolean isVisible(Member member) {
2527dd252788645e940eada959bdde927426e2531c9Paul Duffin      return isVisible(member.getModifiers());
2537dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2547dd252788645e940eada959bdde927426e2531c9Paul Duffin
2557dd252788645e940eada959bdde927426e2531c9Paul Duffin    final Iterable<Method> getStaticMethods(Class<?> cls) {
2567dd252788645e940eada959bdde927426e2531c9Paul Duffin      ImmutableList.Builder<Method> builder = ImmutableList.builder();
2577dd252788645e940eada959bdde927426e2531c9Paul Duffin      for (Method method : getVisibleMethods(cls)) {
2587dd252788645e940eada959bdde927426e2531c9Paul Duffin        if (Invokable.from(method).isStatic()) {
2597dd252788645e940eada959bdde927426e2531c9Paul Duffin          builder.add(method);
2607dd252788645e940eada959bdde927426e2531c9Paul Duffin        }
2617dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2627dd252788645e940eada959bdde927426e2531c9Paul Duffin      return builder.build();
2637dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2647dd252788645e940eada959bdde927426e2531c9Paul Duffin
2657dd252788645e940eada959bdde927426e2531c9Paul Duffin    final Iterable<Method> getInstanceMethods(Class<?> cls) {
2667dd252788645e940eada959bdde927426e2531c9Paul Duffin      ConcurrentMap<Signature, Method> map = Maps.newConcurrentMap();
2677dd252788645e940eada959bdde927426e2531c9Paul Duffin      for (Method method : getVisibleMethods(cls)) {
2687dd252788645e940eada959bdde927426e2531c9Paul Duffin        if (!Invokable.from(method).isStatic()) {
2697dd252788645e940eada959bdde927426e2531c9Paul Duffin          map.putIfAbsent(new Signature(method), method);
2701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
2717dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2727dd252788645e940eada959bdde927426e2531c9Paul Duffin      return map.values();
2737dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2747dd252788645e940eada959bdde927426e2531c9Paul Duffin
2757dd252788645e940eada959bdde927426e2531c9Paul Duffin    private ImmutableList<Method> getVisibleMethods(Class<?> cls) {
2767dd252788645e940eada959bdde927426e2531c9Paul Duffin      // Don't use cls.getPackage() because it does nasty things like reading
2777dd252788645e940eada959bdde927426e2531c9Paul Duffin      // a file.
2787dd252788645e940eada959bdde927426e2531c9Paul Duffin      String visiblePackage = Reflection.getPackageName(cls);
2797dd252788645e940eada959bdde927426e2531c9Paul Duffin      ImmutableList.Builder<Method> builder = ImmutableList.builder();
2807dd252788645e940eada959bdde927426e2531c9Paul Duffin      for (Class<?> type : TypeToken.of(cls).getTypes().classes().rawTypes()) {
2817dd252788645e940eada959bdde927426e2531c9Paul Duffin        if (!Reflection.getPackageName(type).equals(visiblePackage)) {
2827dd252788645e940eada959bdde927426e2531c9Paul Duffin          break;
2831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
2847dd252788645e940eada959bdde927426e2531c9Paul Duffin        for (Method method : type.getDeclaredMethods()) {
2857dd252788645e940eada959bdde927426e2531c9Paul Duffin          if (!method.isSynthetic() && isVisible(method)) {
2867dd252788645e940eada959bdde927426e2531c9Paul Duffin            builder.add(method);
2877dd252788645e940eada959bdde927426e2531c9Paul Duffin          }
2881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
2897dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2907dd252788645e940eada959bdde927426e2531c9Paul Duffin      return builder.build();
2917dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2927dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2937dd252788645e940eada959bdde927426e2531c9Paul Duffin
2947dd252788645e940eada959bdde927426e2531c9Paul Duffin  // TODO(benyu): Use labs/reflect/Signature if it graduates.
2957dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static final class Signature {
2967dd252788645e940eada959bdde927426e2531c9Paul Duffin    private final String name;
2977dd252788645e940eada959bdde927426e2531c9Paul Duffin    private final ImmutableList<Class<?>> parameterTypes;
2987dd252788645e940eada959bdde927426e2531c9Paul Duffin
2997dd252788645e940eada959bdde927426e2531c9Paul Duffin    Signature(Method method) {
3007dd252788645e940eada959bdde927426e2531c9Paul Duffin      this(method.getName(), ImmutableList.copyOf(method.getParameterTypes()));
3017dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3027dd252788645e940eada959bdde927426e2531c9Paul Duffin
3037dd252788645e940eada959bdde927426e2531c9Paul Duffin    Signature(String name, ImmutableList<Class<?>> parameterTypes) {
3047dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.name = name;
3057dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.parameterTypes = parameterTypes;
3067dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3077dd252788645e940eada959bdde927426e2531c9Paul Duffin
3087dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public boolean equals(Object obj) {
3097dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (obj instanceof Signature) {
3107dd252788645e940eada959bdde927426e2531c9Paul Duffin        Signature that = (Signature) obj;
3117dd252788645e940eada959bdde927426e2531c9Paul Duffin        return name.equals(that.name)
3127dd252788645e940eada959bdde927426e2531c9Paul Duffin            && parameterTypes.equals(that.parameterTypes);
3137dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
3147dd252788645e940eada959bdde927426e2531c9Paul Duffin      return false;
3157dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3167dd252788645e940eada959bdde927426e2531c9Paul Duffin
3177dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public int hashCode() {
3187dd252788645e940eada959bdde927426e2531c9Paul Duffin      return Objects.hashCode(name, parameterTypes);
3197dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
3237dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Verifies that {@code invokable} produces a {@link NullPointerException} or
3241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * {@link UnsupportedOperationException} when the parameter in position {@code
3251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * paramIndex} is null.  If this parameter is marked {@link Nullable}, this
3261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * method does nothing.
3271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *
3287dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @param instance the instance to invoke {@code invokable} on, or null if
3297dd252788645e940eada959bdde927426e2531c9Paul Duffin   *     {@code invokable} is static
3301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
3317dd252788645e940eada959bdde927426e2531c9Paul Duffin  private void testParameter(Object instance, Invokable<?, ?> invokable,
3327dd252788645e940eada959bdde927426e2531c9Paul Duffin      int paramIndex, Class<?> testedClass) {
3337dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (isPrimitiveOrNullable(invokable.getParameters().get(paramIndex))) {
3341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      return; // there's nothing to test
3351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3367dd252788645e940eada959bdde927426e2531c9Paul Duffin    Object[] params = buildParamList(invokable, paramIndex);
3371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
3387dd252788645e940eada959bdde927426e2531c9Paul Duffin      @SuppressWarnings("unchecked") // We'll get a runtime exception if the type is wrong.
3397dd252788645e940eada959bdde927426e2531c9Paul Duffin      Invokable<Object, ?> unsafe = (Invokable<Object, ?>) invokable;
3407dd252788645e940eada959bdde927426e2531c9Paul Duffin      unsafe.invoke(instance, params);
3410888a09821a98ac0680fad765217302858e70fa4Paul Duffin      Assert.fail("No exception thrown for parameter at index " + paramIndex
3420888a09821a98ac0680fad765217302858e70fa4Paul Duffin          + " from " + invokable + Arrays.toString(params) + " for " + testedClass);
3431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    } catch (InvocationTargetException e) {
3441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Throwable cause = e.getCause();
3450888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (policy.isExpectedType(cause)) {
3461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        return;
3471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
3481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      AssertionFailedError error = new AssertionFailedError(
3497dd252788645e940eada959bdde927426e2531c9Paul Duffin          "wrong exception thrown from " + invokable + ": " + cause);
3501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      error.initCause(cause);
3511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throw error;
3527dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalAccessException e) {
3537dd252788645e940eada959bdde927426e2531c9Paul Duffin      throw new RuntimeException(e);
3541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3577dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static String bestAvailableString(
3587dd252788645e940eada959bdde927426e2531c9Paul Duffin      TypeToken type, int position, Invokable<?, ?> invokable) {
3597dd252788645e940eada959bdde927426e2531c9Paul Duffin    checkNotNull(type);
3607dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
3617dd252788645e940eada959bdde927426e2531c9Paul Duffin      return type.toString();
3627dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (NullPointerException androidBug6636) {
3637dd252788645e940eada959bdde927426e2531c9Paul Duffin      // http://stackoverflow.com/a/8169250/28465
3647dd252788645e940eada959bdde927426e2531c9Paul Duffin      return String.format("unknown (arg %s of %s)", position, invokable);
3651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3687dd252788645e940eada959bdde927426e2531c9Paul Duffin  private Object[] buildParamList(Invokable<?, ?> invokable, int indexOfParamToSetToNull) {
3697dd252788645e940eada959bdde927426e2531c9Paul Duffin    ImmutableList<Parameter> params = invokable.getParameters();
3707dd252788645e940eada959bdde927426e2531c9Paul Duffin    Object[] args = new Object[params.size()];
3711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3727dd252788645e940eada959bdde927426e2531c9Paul Duffin    for (int i = 0; i < args.length; i++) {
3737dd252788645e940eada959bdde927426e2531c9Paul Duffin      Parameter param = params.get(i);
374dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin      if (i != indexOfParamToSetToNull) {
3757dd252788645e940eada959bdde927426e2531c9Paul Duffin        args[i] = getDefaultValue(param.getType());
3760888a09821a98ac0680fad765217302858e70fa4Paul Duffin        Assert.assertTrue(
3770888a09821a98ac0680fad765217302858e70fa4Paul Duffin            "Can't find or create a sample instance for type '"
3780888a09821a98ac0680fad765217302858e70fa4Paul Duffin                + bestAvailableString(param.getType(), i, invokable)
3790888a09821a98ac0680fad765217302858e70fa4Paul Duffin                + "'; please provide one using NullPointerTester.setDefault()",
3800888a09821a98ac0680fad765217302858e70fa4Paul Duffin            args[i] != null || isNullable(param));
381dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin      }
3821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3837dd252788645e940eada959bdde927426e2531c9Paul Duffin    return args;
3841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3867dd252788645e940eada959bdde927426e2531c9Paul Duffin  private <T> T getDefaultValue(TypeToken<T> type) {
3877dd252788645e940eada959bdde927426e2531c9Paul Duffin    // We assume that all defaults are generics-safe, even if they aren't,
3887dd252788645e940eada959bdde927426e2531c9Paul Duffin    // we take the risk.
3897dd252788645e940eada959bdde927426e2531c9Paul Duffin    @SuppressWarnings("unchecked")
3907dd252788645e940eada959bdde927426e2531c9Paul Duffin    T defaultValue = (T) defaults.getInstance(type.getRawType());
3917dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (defaultValue != null) {
3927dd252788645e940eada959bdde927426e2531c9Paul Duffin      return defaultValue;
3937dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @SuppressWarnings("unchecked") // All arbitrary instances are generics-safe
3950888a09821a98ac0680fad765217302858e70fa4Paul Duffin    T arbitrary = (T) ArbitraryInstances.get(type.getRawType());
3960888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (arbitrary != null) {
3970888a09821a98ac0680fad765217302858e70fa4Paul Duffin      return arbitrary;
3987dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3997dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (type.getRawType() == Class.class) {
4007dd252788645e940eada959bdde927426e2531c9Paul Duffin      // If parameter is Class<? extends Foo>, we return Foo.class
4017dd252788645e940eada959bdde927426e2531c9Paul Duffin      @SuppressWarnings("unchecked")
4027dd252788645e940eada959bdde927426e2531c9Paul Duffin      T defaultClass = (T) getFirstTypeParameter(type.getType()).getRawType();
4037dd252788645e940eada959bdde927426e2531c9Paul Duffin      return defaultClass;
4047dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4057dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (type.getRawType() == TypeToken.class) {
4067dd252788645e940eada959bdde927426e2531c9Paul Duffin      // If parameter is TypeToken<? extends Foo>, we return TypeToken<Foo>.
4077dd252788645e940eada959bdde927426e2531c9Paul Duffin      @SuppressWarnings("unchecked")
4087dd252788645e940eada959bdde927426e2531c9Paul Duffin      T defaultType = (T) getFirstTypeParameter(type.getType());
4097dd252788645e940eada959bdde927426e2531c9Paul Duffin      return defaultType;
4107dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4110888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (type.getRawType() == Converter.class) {
4120888a09821a98ac0680fad765217302858e70fa4Paul Duffin      TypeToken<?> convertFromType = type.resolveType(
4130888a09821a98ac0680fad765217302858e70fa4Paul Duffin          Converter.class.getTypeParameters()[0]);
4140888a09821a98ac0680fad765217302858e70fa4Paul Duffin      TypeToken<?> convertToType = type.resolveType(
4150888a09821a98ac0680fad765217302858e70fa4Paul Duffin          Converter.class.getTypeParameters()[1]);
4160888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @SuppressWarnings("unchecked") // returns default for both F and T
4170888a09821a98ac0680fad765217302858e70fa4Paul Duffin      T defaultConverter = (T) defaultConverter(convertFromType, convertToType);
4180888a09821a98ac0680fad765217302858e70fa4Paul Duffin      return defaultConverter;
4190888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
4207dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (type.getRawType().isInterface()) {
4217dd252788645e940eada959bdde927426e2531c9Paul Duffin      return newDefaultReturningProxy(type);
4227dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4237dd252788645e940eada959bdde927426e2531c9Paul Duffin    return null;
4247dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4257dd252788645e940eada959bdde927426e2531c9Paul Duffin
4260888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private <F, T> Converter<F, T> defaultConverter(
4270888a09821a98ac0680fad765217302858e70fa4Paul Duffin      final TypeToken<F> convertFromType, final TypeToken<T> convertToType) {
4280888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return new Converter<F, T>() {
4290888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override protected T doForward(F a) {
4300888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return doConvert(convertToType);
4310888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
4320888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override protected F doBackward(T b) {
4330888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return doConvert(convertFromType);
4340888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
4350888a09821a98ac0680fad765217302858e70fa4Paul Duffin
4360888a09821a98ac0680fad765217302858e70fa4Paul Duffin      private /*static*/ <S> S doConvert(TypeToken<S> type) {
4370888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return checkNotNull(getDefaultValue(type));
4380888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
4390888a09821a98ac0680fad765217302858e70fa4Paul Duffin    };
4400888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
4410888a09821a98ac0680fad765217302858e70fa4Paul Duffin
4427dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static TypeToken<?> getFirstTypeParameter(Type type) {
4437dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (type instanceof ParameterizedType) {
4447dd252788645e940eada959bdde927426e2531c9Paul Duffin      return TypeToken.of(
4457dd252788645e940eada959bdde927426e2531c9Paul Duffin          ((ParameterizedType) type).getActualTypeArguments()[0]);
4467dd252788645e940eada959bdde927426e2531c9Paul Duffin    } else {
4477dd252788645e940eada959bdde927426e2531c9Paul Duffin      return TypeToken.of(Object.class);
4487dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4517dd252788645e940eada959bdde927426e2531c9Paul Duffin  private <T> T newDefaultReturningProxy(final TypeToken<T> type) {
4527dd252788645e940eada959bdde927426e2531c9Paul Duffin    return new DummyProxy() {
4537dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override <R> R dummyReturnValue(TypeToken<R> returnType) {
4547dd252788645e940eada959bdde927426e2531c9Paul Duffin        return getDefaultValue(returnType);
4557dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
4567dd252788645e940eada959bdde927426e2531c9Paul Duffin    }.newProxy(type);
4577dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4587dd252788645e940eada959bdde927426e2531c9Paul Duffin
4597dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static Invokable<?, ?> invokable(@Nullable Object instance, Method method) {
4607dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (instance == null) {
4617dd252788645e940eada959bdde927426e2531c9Paul Duffin      return Invokable.from(method);
4627dd252788645e940eada959bdde927426e2531c9Paul Duffin    } else {
4637dd252788645e940eada959bdde927426e2531c9Paul Duffin      return TypeToken.of(instance.getClass()).method(method);
4647dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4677dd252788645e940eada959bdde927426e2531c9Paul Duffin  static boolean isPrimitiveOrNullable(Parameter param) {
4680888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return param.getType().getRawType().isPrimitive() || isNullable(param);
4690888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
4700888a09821a98ac0680fad765217302858e70fa4Paul Duffin
4710888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static boolean isNullable(Parameter param) {
4720888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return param.isAnnotationPresent(Nullable.class);
4731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private boolean isIgnored(Member member) {
4761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return member.isSynthetic() || ignoredMembers.contains(member);
4771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4780888a09821a98ac0680fad765217302858e70fa4Paul Duffin
4790888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /**
4800888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * Strategy for exception type matching used by {@link NullPointerTester}.
4810888a09821a98ac0680fad765217302858e70fa4Paul Duffin   */
4820888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private enum ExceptionTypePolicy {
4830888a09821a98ac0680fad765217302858e70fa4Paul Duffin
4840888a09821a98ac0680fad765217302858e70fa4Paul Duffin    /**
4850888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * Exceptions should be {@link NullPointerException} or
4860888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * {@link UnsupportedOperationException}.
4870888a09821a98ac0680fad765217302858e70fa4Paul Duffin     */
4880888a09821a98ac0680fad765217302858e70fa4Paul Duffin    NPE_OR_UOE() {
4890888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override
4900888a09821a98ac0680fad765217302858e70fa4Paul Duffin      public boolean isExpectedType(Throwable cause) {
4910888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return cause instanceof NullPointerException
4920888a09821a98ac0680fad765217302858e70fa4Paul Duffin            || cause instanceof UnsupportedOperationException;
4930888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
4940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    },
4950888a09821a98ac0680fad765217302858e70fa4Paul Duffin
4960888a09821a98ac0680fad765217302858e70fa4Paul Duffin    /**
4970888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * Exceptions should be {@link NullPointerException},
4980888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * {@link IllegalArgumentException}, or
4990888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * {@link UnsupportedOperationException}.
5000888a09821a98ac0680fad765217302858e70fa4Paul Duffin     */
5010888a09821a98ac0680fad765217302858e70fa4Paul Duffin    NPE_IAE_OR_UOE() {
5020888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override
5030888a09821a98ac0680fad765217302858e70fa4Paul Duffin      public boolean isExpectedType(Throwable cause) {
5040888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return cause instanceof NullPointerException
5050888a09821a98ac0680fad765217302858e70fa4Paul Duffin            || cause instanceof IllegalArgumentException
5060888a09821a98ac0680fad765217302858e70fa4Paul Duffin            || cause instanceof UnsupportedOperationException;
5070888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
5080888a09821a98ac0680fad765217302858e70fa4Paul Duffin    };
5090888a09821a98ac0680fad765217302858e70fa4Paul Duffin
5100888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public abstract boolean isExpectedType(Throwable cause);
5110888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
5121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert}
513