/*
* Copyright (C) 2005 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.truth.Truth.assertThat;
import com.google.common.base.Converter;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Table;
import com.google.common.reflect.TypeToken;
import com.google.common.testing.NullPointerTester.Visibility;
import com.google.common.testing.anotherpackage.SomeClassThatDoesNotUseNullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Unit test for {@link NullPointerTester}.
*
* @author Kevin Bourrillion
* @author Mick Killianey
*/
@SuppressWarnings("CheckReturnValue")
public class NullPointerTesterTest extends TestCase {
/** Non-NPE RuntimeException. */
public static class FooException extends RuntimeException {
private static final long serialVersionUID = 1L;
}
/**
* Class for testing all permutations of static/non-static one-argument methods using
* methodParameter().
*/
@SuppressWarnings("unused") // used by reflection
public static class OneArg {
public static void staticOneArgCorrectlyThrowsNpe(String s) {
checkNotNull(s); // expect NPE here on null
}
public static void staticOneArgThrowsOtherThanNpe(String s) {
throw new FooException(); // should catch as failure
}
public static void staticOneArgShouldThrowNpeButDoesnt(String s) {
// should catch as failure
}
public static void staticOneArgCheckForNullCorrectlyDoesNotThrowNPE(
@javax.annotation.CheckForNull String s) {
// null? no problem
}
public static void staticOneArgJsr305NullableCorrectlyDoesNotThrowNPE(
@javax.annotation.Nullable String s) {
// null? no problem
}
public static void staticOneArgNullableCorrectlyDoesNotThrowNPE(@Nullable String s) {
// null? no problem
}
public static void staticOneArgCheckForNullCorrectlyThrowsOtherThanNPE(
@javax.annotation.CheckForNull String s) {
throw new FooException(); // ok, as long as it's not NullPointerException
}
public static void staticOneArgNullableCorrectlyThrowsOtherThanNPE(@Nullable String s) {
throw new FooException(); // ok, as long as it's not NullPointerException
}
public static void staticOneArgCheckForNullThrowsNPE(@javax.annotation.CheckForNull String s) {
checkNotNull(s); // doesn't check if you said you'd accept null, but you don't
}
public static void staticOneArgNullableThrowsNPE(@Nullable String s) {
checkNotNull(s); // doesn't check if you said you'd accept null, but you don't
}
public void oneArgCorrectlyThrowsNpe(String s) {
checkNotNull(s); // expect NPE here on null
}
public void oneArgThrowsOtherThanNpe(String s) {
throw new FooException(); // should catch as failure
}
public void oneArgShouldThrowNpeButDoesnt(String s) {
// should catch as failure
}
public void oneArgCheckForNullCorrectlyDoesNotThrowNPE(
@javax.annotation.CheckForNull String s) {
// null? no problem
}
public void oneArgNullableCorrectlyDoesNotThrowNPE(@Nullable String s) {
// null? no problem
}
public void oneArgCheckForNullCorrectlyThrowsOtherThanNPE(
@javax.annotation.CheckForNull String s) {
throw new FooException(); // ok, as long as it's not NullPointerException
}
public void oneArgNullableCorrectlyThrowsOtherThanNPE(@Nullable String s) {
throw new FooException(); // ok, as long as it's not NullPointerException
}
public void oneArgCheckForNullThrowsNPE(@javax.annotation.CheckForNull String s) {
checkNotNull(s); // doesn't check if you said you'd accept null, but you don't
}
public void oneArgNullableThrowsNPE(@Nullable String s) {
checkNotNull(s); // doesn't check if you said you'd accept null, but you don't
}
}
private static final String[] STATIC_ONE_ARG_METHODS_SHOULD_PASS = {
"staticOneArgCorrectlyThrowsNpe",
"staticOneArgCheckForNullCorrectlyDoesNotThrowNPE",
"staticOneArgCheckForNullCorrectlyThrowsOtherThanNPE",
"staticOneArgCheckForNullThrowsNPE",
"staticOneArgNullableCorrectlyDoesNotThrowNPE",
"staticOneArgNullableCorrectlyThrowsOtherThanNPE",
"staticOneArgNullableThrowsNPE",
};
private static final String[] STATIC_ONE_ARG_METHODS_SHOULD_FAIL = {
"staticOneArgThrowsOtherThanNpe", "staticOneArgShouldThrowNpeButDoesnt",
};
private static final String[] NONSTATIC_ONE_ARG_METHODS_SHOULD_PASS = {
"oneArgCorrectlyThrowsNpe",
"oneArgCheckForNullCorrectlyDoesNotThrowNPE",
"oneArgCheckForNullCorrectlyThrowsOtherThanNPE",
"oneArgCheckForNullThrowsNPE",
"oneArgNullableCorrectlyDoesNotThrowNPE",
"oneArgNullableCorrectlyThrowsOtherThanNPE",
"oneArgNullableThrowsNPE",
};
private static final String[] NONSTATIC_ONE_ARG_METHODS_SHOULD_FAIL = {
"oneArgThrowsOtherThanNpe", "oneArgShouldThrowNpeButDoesnt",
};
private static class ThrowsIae {
public static void christenPoodle(String name) {
checkArgument(name != null);
}
}
private static class ThrowsNpe {
public static void christenPoodle(String name) {
checkNotNull(name);
}
}
private static class ThrowsUoe {
public static void christenPoodle(String name) {
throw new UnsupportedOperationException();
}
}
private static class ThrowsSomethingElse {
public static void christenPoodle(String name) {
throw new RuntimeException();
}
}
private interface InterfaceStaticMethodFailsToCheckNull {
static String create(String s) {
return "I don't check";
}
}
private interface InterfaceStaticMethodChecksNull {
static String create(String s) {
return checkNotNull(s);
}
}
private interface InterfaceDefaultMethodFailsToCheckNull {
static InterfaceDefaultMethodFailsToCheckNull create() {
return new InterfaceDefaultMethodFailsToCheckNull() {};
}
default void doNotCheckNull(String s) {}
}
private interface InterfaceDefaultMethodChecksNull {
static InterfaceDefaultMethodChecksNull create() {
return new InterfaceDefaultMethodChecksNull() {};
}
default void checksNull(String s) {
checkNotNull(s);
}
}
public void testInterfaceStaticMethod() {
NullPointerTester tester = new NullPointerTester();
tester.testAllPublicStaticMethods(InterfaceStaticMethodChecksNull.class);
try {
tester.testAllPublicStaticMethods(InterfaceStaticMethodFailsToCheckNull.class);
} catch (AssertionError expected) {
return;
}
fail();
}
public void testInterfaceDefaultMethod() {
NullPointerTester tester = new NullPointerTester();
tester.testAllPublicInstanceMethods(InterfaceDefaultMethodChecksNull.create());
try {
tester.testAllPublicInstanceMethods(InterfaceDefaultMethodFailsToCheckNull.create());
} catch (AssertionError expected) {
return;
}
fail();
}
public void testDontAcceptIae() {
NullPointerTester tester = new NullPointerTester();
tester.testAllPublicStaticMethods(ThrowsNpe.class);
tester.testAllPublicStaticMethods(ThrowsUoe.class);
try {
tester.testAllPublicStaticMethods(ThrowsIae.class);
} catch (AssertionFailedError expected) {
return;
}
fail();
}
public void testStaticOneArgMethodsThatShouldPass() throws Exception {
for (String methodName : STATIC_ONE_ARG_METHODS_SHOULD_PASS) {
Method method = OneArg.class.getMethod(methodName, String.class);
try {
new NullPointerTester().testMethodParameter(new OneArg(), method, 0);
} catch (AssertionFailedError unexpected) {
fail("Should not have flagged method " + methodName);
}
}
}
public void testStaticOneArgMethodsThatShouldFail() throws Exception {
for (String methodName : STATIC_ONE_ARG_METHODS_SHOULD_FAIL) {
Method method = OneArg.class.getMethod(methodName, String.class);
boolean foundProblem = false;
try {
new NullPointerTester().testMethodParameter(new OneArg(), method, 0);
} catch (AssertionFailedError expected) {
foundProblem = true;
}
assertTrue("Should report error in method " + methodName, foundProblem);
}
}
public void testNonStaticOneArgMethodsThatShouldPass() throws Exception {
OneArg foo = new OneArg();
for (String methodName : NONSTATIC_ONE_ARG_METHODS_SHOULD_PASS) {
Method method = OneArg.class.getMethod(methodName, String.class);
try {
new NullPointerTester().testMethodParameter(foo, method, 0);
} catch (AssertionFailedError unexpected) {
fail("Should not have flagged method " + methodName);
}
}
}
public void testNonStaticOneArgMethodsThatShouldFail() throws Exception {
OneArg foo = new OneArg();
for (String methodName : NONSTATIC_ONE_ARG_METHODS_SHOULD_FAIL) {
Method method = OneArg.class.getMethod(methodName, String.class);
boolean foundProblem = false;
try {
new NullPointerTester().testMethodParameter(foo, method, 0);
} catch (AssertionFailedError expected) {
foundProblem = true;
}
assertTrue("Should report error in method " + methodName, foundProblem);
}
}
public void testMessageOtherException() throws Exception {
Method method = OneArg.class.getMethod("staticOneArgThrowsOtherThanNpe", String.class);
boolean foundProblem = false;
try {
new NullPointerTester().testMethodParameter(new OneArg(), method, 0);
} catch (AssertionFailedError expected) {
assertThat(expected.getMessage()).contains("index 0");
assertThat(expected.getMessage()).contains("[null]");
foundProblem = true;
}
assertTrue("Should report error when different exception is thrown", foundProblem);
}
public void testMessageNoException() throws Exception {
Method method = OneArg.class.getMethod("staticOneArgShouldThrowNpeButDoesnt", String.class);
boolean foundProblem = false;
try {
new NullPointerTester().testMethodParameter(new OneArg(), method, 0);
} catch (AssertionFailedError expected) {
assertThat(expected.getMessage()).contains("index 0");
assertThat(expected.getMessage()).contains("[null]");
foundProblem = true;
}
assertTrue("Should report error when no exception is thrown", foundProblem);
}
/**
* Class for testing all permutations of nullable/non-nullable two-argument methods using
* testMethod().
*
*
* - normalNormal: two params, neither is Nullable
*
- nullableNormal: only first param is Nullable
*
- normalNullable: only second param is Nullable
*
- nullableNullable: both params are Nullable
*
*/
public static class TwoArg {
/** Action to take on a null param. */
public enum Action {
THROW_A_NPE {
@Override
public void act() {
throw new NullPointerException();
}
},
THROW_OTHER {
@Override
public void act() {
throw new FooException();
}
},
JUST_RETURN {
@Override
public void act() {}
};
public abstract void act();
}
Action actionWhenFirstParamIsNull;
Action actionWhenSecondParamIsNull;
public TwoArg(Action actionWhenFirstParamIsNull, Action actionWhenSecondParamIsNull) {
this.actionWhenFirstParamIsNull = actionWhenFirstParamIsNull;
this.actionWhenSecondParamIsNull = actionWhenSecondParamIsNull;
}
/** Method that decides how to react to parameters. */
public void reactToNullParameters(Object first, Object second) {
if (first == null) {
actionWhenFirstParamIsNull.act();
}
if (second == null) {
actionWhenSecondParamIsNull.act();
}
}
/** Two-arg method with no Nullable params. */
@SuppressWarnings("GoodTime") // false positive; b/122617528
public void normalNormal(String first, Integer second) {
reactToNullParameters(first, second);
}
/** Two-arg method with the second param Nullable. */
@SuppressWarnings("GoodTime") // false positive; b/122617528
public void normalNullable(String first, @Nullable Integer second) {
reactToNullParameters(first, second);
}
/** Two-arg method with the first param Nullable. */
@SuppressWarnings("GoodTime") // false positive; b/122617528
public void nullableNormal(@Nullable String first, Integer second) {
reactToNullParameters(first, second);
}
/** Two-arg method with the both params Nullable. */
@SuppressWarnings("GoodTime") // false positive; b/122617528
public void nullableNullable(@Nullable String first, @Nullable Integer second) {
reactToNullParameters(first, second);
}
/** To provide sanity during debugging. */
@Override
public String toString() {
return rootLocaleFormat(
"Bar(%s, %s)", actionWhenFirstParamIsNull, actionWhenSecondParamIsNull);
}
}
public void verifyBarPass(Method method, TwoArg bar) {
try {
new NullPointerTester().testMethod(bar, method);
} catch (AssertionFailedError incorrectError) {
String errorMessage =
rootLocaleFormat("Should not have flagged method %s for %s", method.getName(), bar);
assertNull(errorMessage, incorrectError);
}
}
public void verifyBarFail(Method method, TwoArg bar) {
try {
new NullPointerTester().testMethod(bar, method);
} catch (AssertionFailedError expected) {
return; // good...we wanted a failure
}
String errorMessage =
rootLocaleFormat("Should have flagged method %s for %s", method.getName(), bar);
fail(errorMessage);
}
public void testTwoArgNormalNormal() throws Exception {
Method method = TwoArg.class.getMethod("normalNormal", String.class, Integer.class);
for (TwoArg.Action first : TwoArg.Action.values()) {
for (TwoArg.Action second : TwoArg.Action.values()) {
TwoArg bar = new TwoArg(first, second);
if (first.equals(TwoArg.Action.THROW_A_NPE) && second.equals(TwoArg.Action.THROW_A_NPE)) {
verifyBarPass(method, bar); // require both params to throw NPE
} else {
verifyBarFail(method, bar);
}
}
}
}
public void testTwoArgNormalNullable() throws Exception {
Method method = TwoArg.class.getMethod("normalNullable", String.class, Integer.class);
for (TwoArg.Action first : TwoArg.Action.values()) {
for (TwoArg.Action second : TwoArg.Action.values()) {
TwoArg bar = new TwoArg(first, second);
if (first.equals(TwoArg.Action.THROW_A_NPE)) {
verifyBarPass(method, bar); // only pass if 1st param throws NPE
} else {
verifyBarFail(method, bar);
}
}
}
}
public void testTwoArgNullableNormal() throws Exception {
Method method = TwoArg.class.getMethod("nullableNormal", String.class, Integer.class);
for (TwoArg.Action first : TwoArg.Action.values()) {
for (TwoArg.Action second : TwoArg.Action.values()) {
TwoArg bar = new TwoArg(first, second);
if (second.equals(TwoArg.Action.THROW_A_NPE)) {
verifyBarPass(method, bar); // only pass if 2nd param throws NPE
} else {
verifyBarFail(method, bar);
}
}
}
}
public void testTwoArgNullableNullable() throws Exception {
Method method = TwoArg.class.getMethod("nullableNullable", String.class, Integer.class);
for (TwoArg.Action first : TwoArg.Action.values()) {
for (TwoArg.Action second : TwoArg.Action.values()) {
TwoArg bar = new TwoArg(first, second);
verifyBarPass(method, bar); // All args nullable: anything goes!
}
}
}
/*
* This next part consists of several sample classes that provide
* demonstrations of conditions that cause NullPointerTester
* to succeed/fail.
*/
/** Lots of well-behaved methods. */
@SuppressWarnings("unused") // used by reflection
private static class PassObject extends SomeClassThatDoesNotUseNullable {
public static void doThrow(Object arg) {
if (arg == null) {
throw new FooException();
}
}
public void noArg() {}
public void oneArg(String s) {
checkNotNull(s);
}
void packagePrivateOneArg(String s) {
checkNotNull(s);
}
protected void protectedOneArg(String s) {
checkNotNull(s);
}
public void oneNullableArg(@Nullable String s) {}
public void oneNullableArgThrows(@Nullable String s) {
doThrow(s);
}
public void twoArg(String s, Integer i) {
checkNotNull(s);
i.intValue();
}
public void twoMixedArgs(String s, @Nullable Integer i) {
checkNotNull(s);
}
public void twoMixedArgs(@Nullable Integer i, String s) {
checkNotNull(s);
}
public void twoMixedArgsThrows(String s, @Nullable Integer i) {
checkNotNull(s);
doThrow(i);
}
public void twoMixedArgsThrows(@Nullable Integer i, String s) {
checkNotNull(s);
doThrow(i);
}
public void twoNullableArgs(@Nullable String s, @javax.annotation.Nullable Integer i) {}
public void twoNullableArgsThrowsFirstArg(@Nullable String s, @Nullable Integer i) {
doThrow(s);
}
public void twoNullableArgsThrowsSecondArg(@Nullable String s, @Nullable Integer i) {
doThrow(i);
}
public static void staticOneArg(String s) {
checkNotNull(s);
}
public static void staticOneNullableArg(@Nullable String s) {}
public static void staticOneNullableArgThrows(@Nullable String s) {
doThrow(s);
}
}
public void testGoodClass() {
shouldPass(new PassObject());
}
private static class FailOneArgDoesntThrowNPE extends PassObject {
@Override
public void oneArg(String s) {
// Fail: missing NPE for s
}
}
public void testFailOneArgDoesntThrowNpe() {
shouldFail(new FailOneArgDoesntThrowNPE());
}
private static class FailOneArgThrowsWrongType extends PassObject {
@Override
public void oneArg(String s) {
doThrow(s); // Fail: throwing non-NPE exception for null s
}
}
public void testFailOneArgThrowsWrongType() {
shouldFail(new FailOneArgThrowsWrongType());
}
private static class PassOneNullableArgThrowsNPE extends PassObject {
@Override
public void oneNullableArg(@Nullable String s) {
checkNotNull(s); // ok to throw NPE
}
}
public void testPassOneNullableArgThrowsNPE() {
shouldPass(new PassOneNullableArgThrowsNPE());
}
private static class FailTwoArgsFirstArgDoesntThrowNPE extends PassObject {
@Override
public void twoArg(String s, Integer i) {
// Fail: missing NPE for s
i.intValue();
}
}
public void testFailTwoArgsFirstArgDoesntThrowNPE() {
shouldFail(new FailTwoArgsFirstArgDoesntThrowNPE());
}
private static class FailTwoArgsFirstArgThrowsWrongType extends PassObject {
@Override
public void twoArg(String s, Integer i) {
doThrow(s); // Fail: throwing non-NPE exception for null s
i.intValue();
}
}
public void testFailTwoArgsFirstArgThrowsWrongType() {
shouldFail(new FailTwoArgsFirstArgThrowsWrongType());
}
private static class FailTwoArgsSecondArgDoesntThrowNPE extends PassObject {
@Override
public void twoArg(String s, Integer i) {
checkNotNull(s);
// Fail: missing NPE for i
}
}
public void testFailTwoArgsSecondArgDoesntThrowNPE() {
shouldFail(new FailTwoArgsSecondArgDoesntThrowNPE());
}
private static class FailTwoArgsSecondArgThrowsWrongType extends PassObject {
@Override
public void twoArg(String s, Integer i) {
checkNotNull(s);
doThrow(i); // Fail: throwing non-NPE exception for null i
}
}
public void testFailTwoArgsSecondArgThrowsWrongType() {
shouldFail(new FailTwoArgsSecondArgThrowsWrongType());
}
private static class FailTwoMixedArgsFirstArgDoesntThrowNPE extends PassObject {
@Override
public void twoMixedArgs(String s, @Nullable Integer i) {
// Fail: missing NPE for s
}
}
public void testFailTwoMixedArgsFirstArgDoesntThrowNPE() {
shouldFail(new FailTwoMixedArgsFirstArgDoesntThrowNPE());
}
private static class FailTwoMixedArgsFirstArgThrowsWrongType extends PassObject {
@Override
public void twoMixedArgs(String s, @Nullable Integer i) {
doThrow(s); // Fail: throwing non-NPE exception for null s
}
}
public void testFailTwoMixedArgsFirstArgThrowsWrongType() {
shouldFail(new FailTwoMixedArgsFirstArgThrowsWrongType());
}
private static class PassTwoMixedArgsNullableArgThrowsNPE extends PassObject {
@Override
public void twoMixedArgs(String s, @Nullable Integer i) {
checkNotNull(s);
i.intValue(); // ok to throw NPE?
}
}
public void testPassTwoMixedArgsNullableArgThrowsNPE() {
shouldPass(new PassTwoMixedArgsNullableArgThrowsNPE());
}
private static class PassTwoMixedArgSecondNullableArgThrowsOther extends PassObject {
@Override
public void twoMixedArgs(String s, @Nullable Integer i) {
checkNotNull(s);
doThrow(i); // ok to throw non-NPE exception for null i
}
}
public void testPassTwoMixedArgSecondNullableArgThrowsOther() {
shouldPass(new PassTwoMixedArgSecondNullableArgThrowsOther());
}
private static class FailTwoMixedArgsSecondArgDoesntThrowNPE extends PassObject {
@Override
public void twoMixedArgs(@Nullable Integer i, String s) {
// Fail: missing NPE for null s
}
}
public void testFailTwoMixedArgsSecondArgDoesntThrowNPE() {
shouldFail(new FailTwoMixedArgsSecondArgDoesntThrowNPE());
}
private static class FailTwoMixedArgsSecondArgThrowsWrongType extends PassObject {
@Override
public void twoMixedArgs(@Nullable Integer i, String s) {
doThrow(s); // Fail: throwing non-NPE exception for null s
}
}
public void testFailTwoMixedArgsSecondArgThrowsWrongType() {
shouldFail(new FailTwoMixedArgsSecondArgThrowsWrongType());
}
private static class PassTwoNullableArgsFirstThrowsNPE extends PassObject {
@Override
public void twoNullableArgs(@Nullable String s, @Nullable Integer i) {
checkNotNull(s); // ok to throw NPE?
}
}
public void testPassTwoNullableArgsFirstThrowsNPE() {
shouldPass(new PassTwoNullableArgsFirstThrowsNPE());
}
private static class PassTwoNullableArgsFirstThrowsOther extends PassObject {
@Override
public void twoNullableArgs(@Nullable String s, @Nullable Integer i) {
doThrow(s); // ok to throw non-NPE exception for null s
}
}
public void testPassTwoNullableArgsFirstThrowsOther() {
shouldPass(new PassTwoNullableArgsFirstThrowsOther());
}
private static class PassTwoNullableArgsSecondThrowsNPE extends PassObject {
@Override
public void twoNullableArgs(@Nullable String s, @Nullable Integer i) {
i.intValue(); // ok to throw NPE?
}
}
public void testPassTwoNullableArgsSecondThrowsNPE() {
shouldPass(new PassTwoNullableArgsSecondThrowsNPE());
}
private static class PassTwoNullableArgsSecondThrowsOther extends PassObject {
@Override
public void twoNullableArgs(@Nullable String s, @Nullable Integer i) {
doThrow(i); // ok to throw non-NPE exception for null i
}
}
public void testPassTwoNullableArgsSecondThrowsOther() {
shouldPass(new PassTwoNullableArgsSecondThrowsOther());
}
private static class PassTwoNullableArgsNeitherThrowsAnything extends PassObject {
@Override
public void twoNullableArgs(@Nullable String s, @Nullable Integer i) {
// ok to do nothing
}
}
public void testPassTwoNullableArgsNeitherThrowsAnything() {
shouldPass(new PassTwoNullableArgsNeitherThrowsAnything());
}
@SuppressWarnings("unused") // for NullPointerTester
private abstract static class BaseClassThatFailsToThrow {
public void oneArg(String s) {}
}
private static class SubclassWithBadSuperclass extends BaseClassThatFailsToThrow {}
public void testSubclassWithBadSuperclass() {
shouldFail(new SubclassWithBadSuperclass());
}
@SuppressWarnings("unused") // for NullPointerTester
private abstract static class BaseClassThatFailsToThrowForPackagePrivate {
void packagePrivateOneArg(String s) {}
}
private static class SubclassWithBadSuperclassForPackagePrivate
extends BaseClassThatFailsToThrowForPackagePrivate {}
public void testSubclassWithBadSuperclassForPackagePrivateMethod() {
shouldFail(new SubclassWithBadSuperclassForPackagePrivate(), Visibility.PACKAGE);
}
@SuppressWarnings("unused") // for NullPointerTester
private abstract static class BaseClassThatFailsToThrowForProtected {
protected void protectedOneArg(String s) {}
}
private static class SubclassWithBadSuperclassForProtected
extends BaseClassThatFailsToThrowForProtected {}
public void testSubclassWithBadSuperclassForPackageProtectedMethod() {
shouldFail(new SubclassWithBadSuperclassForProtected(), Visibility.PROTECTED);
}
private static class SubclassThatOverridesBadSuperclassMethod extends BaseClassThatFailsToThrow {
@Override
public void oneArg(@Nullable String s) {}
}
public void testSubclassThatOverridesBadSuperclassMethod() {
shouldPass(new SubclassThatOverridesBadSuperclassMethod());
}
@SuppressWarnings("unused") // for NullPointerTester
private static class SubclassOverridesTheWrongMethod extends BaseClassThatFailsToThrow {
public void oneArg(@Nullable CharSequence s) {}
}
public void testSubclassOverridesTheWrongMethod() {
shouldFail(new SubclassOverridesTheWrongMethod());
}
@SuppressWarnings("unused") // for NullPointerTester
private static class ClassThatFailsToThrowForStatic {
static void staticOneArg(String s) {}
}
public void testClassThatFailsToThrowForStatic() {
shouldFail(ClassThatFailsToThrowForStatic.class);
}
private static class SubclassThatFailsToThrowForStatic extends ClassThatFailsToThrowForStatic {}
public void testSubclassThatFailsToThrowForStatic() {
shouldFail(SubclassThatFailsToThrowForStatic.class);
}
private static class SubclassThatTriesToOverrideBadStaticMethod
extends ClassThatFailsToThrowForStatic {
static void staticOneArg(@Nullable String s) {}
}
public void testSubclassThatTriesToOverrideBadStaticMethod() {
shouldFail(SubclassThatTriesToOverrideBadStaticMethod.class);
}
private static final class HardToCreate {
private HardToCreate(HardToCreate x) {}
}
@SuppressWarnings("unused") // used by reflection
private static class CanCreateDefault {
public void foo(@Nullable HardToCreate ignored, String required) {
checkNotNull(required);
}
}
public void testCanCreateDefault() {
shouldPass(new CanCreateDefault());
}
@SuppressWarnings("unused") // used by reflection
private static class CannotCreateDefault {
public void foo(HardToCreate ignored, String required) {
checkNotNull(ignored);
checkNotNull(required);
}
}
public void testCannotCreateDefault() {
shouldFail(new CannotCreateDefault());
}
private static void shouldPass(Object instance, Visibility visibility) {
new NullPointerTester().testInstanceMethods(instance, visibility);
}
private static void shouldPass(Object instance) {
shouldPass(instance, Visibility.PACKAGE);
shouldPass(instance, Visibility.PROTECTED);
shouldPass(instance, Visibility.PUBLIC);
}
// TODO(cpovirk): eliminate surprising Object/Class overloading of shouldFail
private static void shouldFail(Object instance, Visibility visibility) {
try {
new NullPointerTester().testInstanceMethods(instance, visibility);
} catch (AssertionFailedError expected) {
return;
}
fail("Should detect problem in " + instance.getClass().getSimpleName());
}
private static void shouldFail(Object instance) {
shouldFail(instance, Visibility.PACKAGE);
shouldFail(instance, Visibility.PROTECTED);
shouldFail(instance, Visibility.PUBLIC);
}
private static void shouldFail(Class> cls, Visibility visibility) {
try {
new NullPointerTester().testStaticMethods(cls, visibility);
} catch (AssertionFailedError expected) {
return;
}
fail("Should detect problem in " + cls.getSimpleName());
}
private static void shouldFail(Class> cls) {
shouldFail(cls, Visibility.PACKAGE);
}
@SuppressWarnings("unused") // used by reflection
private static class PrivateClassWithPrivateConstructor {
private PrivateClassWithPrivateConstructor(@Nullable Integer argument) {}
}
public void testPrivateClass() {
NullPointerTester tester = new NullPointerTester();
for (Constructor> constructor :
PrivateClassWithPrivateConstructor.class.getDeclaredConstructors()) {
tester.testConstructor(constructor);
}
}
private interface Foo {
void doSomething(T bar, Integer baz);
}
private static class StringFoo implements Foo {
@Override
public void doSomething(String bar, Integer baz) {
checkNotNull(bar);
checkNotNull(baz);
}
}
public void testBridgeMethodIgnored() {
new NullPointerTester().testAllPublicInstanceMethods(new StringFoo());
}
private abstract static class DefaultValueChecker {
private final Map arguments = Maps.newHashMap();
final DefaultValueChecker runTester() {
new NullPointerTester().testInstanceMethods(this, Visibility.PACKAGE);
return this;
}
final void assertNonNullValues(Object... expectedValues) {
assertEquals(expectedValues.length, arguments.size());
for (int i = 0; i < expectedValues.length; i++) {
assertEquals("Default value for parameter #" + i, expectedValues[i], arguments.get(i));
}
}
final Object getDefaultParameterValue(int position) {
return arguments.get(position);
}
final void calledWith(Object... args) {
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
arguments.put(i, args[i]);
}
}
for (Object arg : args) {
checkNotNull(arg); // to fulfill null check
}
}
}
private enum Gender {
MALE,
FEMALE
}
private static class AllDefaultValuesChecker extends DefaultValueChecker {
@SuppressWarnings("unused") // called by NullPointerTester
public void checkDefaultValuesForTheseTypes(
Gender gender,
Integer integer,
int i,
String string,
CharSequence charSequence,
List list,
ImmutableList immutableList,
Map map,
ImmutableMap immutableMap,
Set set,
ImmutableSet immutableSet,
SortedSet sortedSet,
ImmutableSortedSet immutableSortedSet,
Multiset multiset,
ImmutableMultiset immutableMultiset,
Multimap multimap,
ImmutableMultimap immutableMultimap,
Table table,
ImmutableTable immutableTable) {
calledWith(
gender,
integer,
i,
string,
charSequence,
list,
immutableList,
map,
immutableMap,
set,
immutableSet,
sortedSet,
immutableSortedSet,
multiset,
immutableMultiset,
multimap,
immutableMultimap,
table,
immutableTable);
}
final void check() {
runTester()
.assertNonNullValues(
Gender.MALE,
Integer.valueOf(0),
0,
"",
"",
ImmutableList.of(),
ImmutableList.of(),
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableSet.of(),
ImmutableSet.of(),
ImmutableSortedSet.of(),
ImmutableSortedSet.of(),
ImmutableMultiset.of(),
ImmutableMultiset.of(),
ImmutableMultimap.of(),
ImmutableMultimap.of(),
ImmutableTable.of(),
ImmutableTable.of());
}
}
public void testDefaultValues() {
new AllDefaultValuesChecker().check();
}
private static class ObjectArrayDefaultValueChecker extends DefaultValueChecker {
@SuppressWarnings("unused") // called by NullPointerTester
public void checkArray(Object[] array, String s) {
calledWith(array, s);
}
void check() {
runTester();
Object[] defaultArray = (Object[]) getDefaultParameterValue(0);
assertThat(defaultArray).isEmpty();
}
}
public void testObjectArrayDefaultValue() {
new ObjectArrayDefaultValueChecker().check();
}
private static class StringArrayDefaultValueChecker extends DefaultValueChecker {
@SuppressWarnings("unused") // called by NullPointerTester
public void checkArray(String[] array, String s) {
calledWith(array, s);
}
void check() {
runTester();
String[] defaultArray = (String[]) getDefaultParameterValue(0);
assertThat(defaultArray).isEmpty();
}
}
public void testStringArrayDefaultValue() {
new StringArrayDefaultValueChecker().check();
}
private static class IntArrayDefaultValueChecker extends DefaultValueChecker {
@SuppressWarnings("unused") // called by NullPointerTester
public void checkArray(int[] array, String s) {
calledWith(array, s);
}
void check() {
runTester();
int[] defaultArray = (int[]) getDefaultParameterValue(0);
assertEquals(0, defaultArray.length);
}
}
public void testIntArrayDefaultValue() {
new IntArrayDefaultValueChecker().check();
}
private enum EmptyEnum {}
private static class EmptyEnumDefaultValueChecker extends DefaultValueChecker {
@SuppressWarnings("unused") // called by NullPointerTester
public void checkArray(EmptyEnum object, String s) {
calledWith(object, s);
}
void check() {
try {
runTester();
} catch (AssertionFailedError expected) {
return;
}
fail("Should have failed because enum has no constant");
}
}
public void testEmptyEnumDefaultValue() {
new EmptyEnumDefaultValueChecker().check();
}
private static class GenericClassTypeDefaultValueChecker extends DefaultValueChecker {
@SuppressWarnings("unused") // called by NullPointerTester
public void checkArray(Class extends List>> cls, String s) {
calledWith(cls, s);
}
void check() {
runTester();
Class> defaultClass = (Class>) getDefaultParameterValue(0);
assertEquals(List.class, defaultClass);
}
}
public void testGenericClassDefaultValue() {
new GenericClassTypeDefaultValueChecker().check();
}
private static class NonGenericClassTypeDefaultValueChecker extends DefaultValueChecker {
@SuppressWarnings("unused") // called by NullPointerTester
public void checkArray(@SuppressWarnings("rawtypes") Class cls, String s) {
calledWith(cls, s);
}
void check() {
runTester();
Class> defaultClass = (Class>) getDefaultParameterValue(0);
assertEquals(Object.class, defaultClass);
}
}
public void testNonGenericClassDefaultValue() {
new NonGenericClassTypeDefaultValueChecker().check();
}
private static class GenericTypeTokenDefaultValueChecker extends DefaultValueChecker {
@SuppressWarnings("unused") // called by NullPointerTester
public void checkArray(TypeToken extends List super Number>> type, String s) {
calledWith(type, s);
}
void check() {
runTester();
TypeToken> defaultType = (TypeToken>) getDefaultParameterValue(0);
assertTrue(new TypeToken>() {}.isSupertypeOf(defaultType));
}
}
public void testGenericTypeTokenDefaultValue() {
new GenericTypeTokenDefaultValueChecker().check();
}
private static class NonGenericTypeTokenDefaultValueChecker extends DefaultValueChecker {
@SuppressWarnings("unused") // called by NullPointerTester
public void checkArray(@SuppressWarnings("rawtypes") TypeToken type, String s) {
calledWith(type, s);
}
void check() {
runTester();
TypeToken> defaultType = (TypeToken>) getDefaultParameterValue(0);
assertEquals(new TypeToken