/*
* Copyright (C) 2006 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.base;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.testing.ArbitraryInstances;
import com.google.common.testing.NullPointerTester;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
/**
* Unit test for {@link Preconditions}.
*
* @author Kevin Bourrillion
* @author Jared Levy
*/
@GwtCompatible(emulated = true)
public class PreconditionsTest extends TestCase {
public void testCheckArgument_simple_success() {
Preconditions.checkArgument(true);
}
public void testCheckArgument_simple_failure() {
try {
Preconditions.checkArgument(false);
fail("no exception thrown");
} catch (IllegalArgumentException expected) {
}
}
public void testCheckArgument_simpleMessage_success() {
Preconditions.checkArgument(true, IGNORE_ME);
}
public void testCheckArgument_simpleMessage_failure() {
try {
Preconditions.checkArgument(false, new Message());
fail("no exception thrown");
} catch (IllegalArgumentException expected) {
verifySimpleMessage(expected);
}
}
public void testCheckArgument_nullMessage_failure() {
try {
Preconditions.checkArgument(false, null);
fail("no exception thrown");
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("null");
}
}
public void testCheckArgument_complexMessage_success() {
Preconditions.checkArgument(true, "%s", IGNORE_ME);
}
public void testCheckArgument_complexMessage_failure() {
try {
Preconditions.checkArgument(false, FORMAT, 5);
fail("no exception thrown");
} catch (IllegalArgumentException expected) {
verifyComplexMessage(expected);
}
}
public void testCheckState_simple_success() {
Preconditions.checkState(true);
}
public void testCheckState_simple_failure() {
try {
Preconditions.checkState(false);
fail("no exception thrown");
} catch (IllegalStateException expected) {
}
}
public void testCheckState_simpleMessage_success() {
Preconditions.checkState(true, IGNORE_ME);
}
public void testCheckState_simpleMessage_failure() {
try {
Preconditions.checkState(false, new Message());
fail("no exception thrown");
} catch (IllegalStateException expected) {
verifySimpleMessage(expected);
}
}
public void testCheckState_nullMessage_failure() {
try {
Preconditions.checkState(false, null);
fail("no exception thrown");
} catch (IllegalStateException expected) {
assertThat(expected).hasMessage("null");
}
}
public void testCheckState_complexMessage_success() {
Preconditions.checkState(true, "%s", IGNORE_ME);
}
public void testCheckState_complexMessage_failure() {
try {
Preconditions.checkState(false, FORMAT, 5);
fail("no exception thrown");
} catch (IllegalStateException expected) {
verifyComplexMessage(expected);
}
}
private static final String NON_NULL_STRING = "foo";
public void testCheckNotNull_simple_success() {
String result = Preconditions.checkNotNull(NON_NULL_STRING);
assertSame(NON_NULL_STRING, result);
}
public void testCheckNotNull_simple_failure() {
try {
Preconditions.checkNotNull(null);
fail("no exception thrown");
} catch (NullPointerException expected) {
}
}
public void testCheckNotNull_simpleMessage_success() {
String result = Preconditions.checkNotNull(NON_NULL_STRING, IGNORE_ME);
assertSame(NON_NULL_STRING, result);
}
public void testCheckNotNull_simpleMessage_failure() {
try {
Preconditions.checkNotNull(null, new Message());
fail("no exception thrown");
} catch (NullPointerException expected) {
verifySimpleMessage(expected);
}
}
public void testCheckNotNull_complexMessage_success() {
String result = Preconditions.checkNotNull(
NON_NULL_STRING, "%s", IGNORE_ME);
assertSame(NON_NULL_STRING, result);
}
public void testCheckNotNull_complexMessage_failure() {
try {
Preconditions.checkNotNull(null, FORMAT, 5);
fail("no exception thrown");
} catch (NullPointerException expected) {
verifyComplexMessage(expected);
}
}
public void testCheckElementIndex_ok() {
assertEquals(0, Preconditions.checkElementIndex(0, 1));
assertEquals(0, Preconditions.checkElementIndex(0, 2));
assertEquals(1, Preconditions.checkElementIndex(1, 2));
}
public void testCheckElementIndex_badSize() {
try {
Preconditions.checkElementIndex(1, -1);
fail();
} catch (IllegalArgumentException expected) {
// don't care what the message text is, as this is an invalid usage of
// the Preconditions class, unlike all the other exceptions it throws
}
}
public void testCheckElementIndex_negative() {
try {
Preconditions.checkElementIndex(-1, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("index (-1) must not be negative");
}
}
public void testCheckElementIndex_tooHigh() {
try {
Preconditions.checkElementIndex(1, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("index (1) must be less than size (1)");
}
}
public void testCheckElementIndex_withDesc_negative() {
try {
Preconditions.checkElementIndex(-1, 1, "foo");
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("foo (-1) must not be negative");
}
}
public void testCheckElementIndex_withDesc_tooHigh() {
try {
Preconditions.checkElementIndex(1, 1, "foo");
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("foo (1) must be less than size (1)");
}
}
public void testCheckPositionIndex_ok() {
assertEquals(0, Preconditions.checkPositionIndex(0, 0));
assertEquals(0, Preconditions.checkPositionIndex(0, 1));
assertEquals(1, Preconditions.checkPositionIndex(1, 1));
}
public void testCheckPositionIndex_badSize() {
try {
Preconditions.checkPositionIndex(1, -1);
fail();
} catch (IllegalArgumentException expected) {
// don't care what the message text is, as this is an invalid usage of
// the Preconditions class, unlike all the other exceptions it throws
}
}
public void testCheckPositionIndex_negative() {
try {
Preconditions.checkPositionIndex(-1, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("index (-1) must not be negative");
}
}
public void testCheckPositionIndex_tooHigh() {
try {
Preconditions.checkPositionIndex(2, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("index (2) must not be greater than size (1)");
}
}
public void testCheckPositionIndex_withDesc_negative() {
try {
Preconditions.checkPositionIndex(-1, 1, "foo");
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("foo (-1) must not be negative");
}
}
public void testCheckPositionIndex_withDesc_tooHigh() {
try {
Preconditions.checkPositionIndex(2, 1, "foo");
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("foo (2) must not be greater than size (1)");
}
}
public void testCheckPositionIndexes_ok() {
Preconditions.checkPositionIndexes(0, 0, 0);
Preconditions.checkPositionIndexes(0, 0, 1);
Preconditions.checkPositionIndexes(0, 1, 1);
Preconditions.checkPositionIndexes(1, 1, 1);
}
public void testCheckPositionIndexes_badSize() {
try {
Preconditions.checkPositionIndexes(1, 1, -1);
fail();
} catch (IllegalArgumentException expected) {
}
}
public void testCheckPositionIndex_startNegative() {
try {
Preconditions.checkPositionIndexes(-1, 1, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("start index (-1) must not be negative");
}
}
public void testCheckPositionIndexes_endTooHigh() {
try {
Preconditions.checkPositionIndexes(0, 2, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("end index (2) must not be greater than size (1)");
}
}
public void testCheckPositionIndexes_reversed() {
try {
Preconditions.checkPositionIndexes(1, 0, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
assertThat(expected).hasMessage("end index (0) must not be less than start index (1)");
}
}
public void testFormat() {
assertEquals("%s", Preconditions.format("%s"));
assertEquals("5", Preconditions.format("%s", 5));
assertEquals("foo [5]", Preconditions.format("foo", 5));
assertEquals("foo [5, 6, 7]", Preconditions.format("foo", 5, 6, 7));
assertEquals("%s 1 2", Preconditions.format("%s %s %s", "%s", 1, 2));
assertEquals(" [5, 6]", Preconditions.format("", 5, 6));
assertEquals("123", Preconditions.format("%s%s%s", 1, 2, 3));
assertEquals("1%s%s", Preconditions.format("%s%s%s", 1));
assertEquals("5 + 6 = 11", Preconditions.format("%s + 6 = 11", 5));
assertEquals("5 + 6 = 11", Preconditions.format("5 + %s = 11", 6));
assertEquals("5 + 6 = 11", Preconditions.format("5 + 6 = %s", 11));
assertEquals("5 + 6 = 11", Preconditions.format("%s + %s = %s", 5, 6, 11));
assertEquals("null [null, null]",
Preconditions.format("%s", null, null, null));
assertEquals("null [5, 6]", Preconditions.format(null, 5, 6));
}
@GwtIncompatible("Reflection")
public void testAllOverloads_checkArgument() throws Exception {
for (ImmutableList<Class<?>> sig : allSignatures(boolean.class)) {
Method checkArgumentMethod =
Preconditions.class.getMethod("checkArgument", sig.toArray(new Class<?>[] {}));
checkArgumentMethod.invoke(null /* static method */, getParametersForSignature(true, sig));
Object[] failingParams = getParametersForSignature(false, sig);
try {
checkArgumentMethod.invoke(null /* static method */, failingParams);
fail();
} catch (InvocationTargetException ite) {
assertFailureCause(ite.getCause(), IllegalArgumentException.class, failingParams);
}
}
}
@GwtIncompatible("Reflection")
public void testAllOverloads_checkState() throws Exception {
for (ImmutableList<Class<?>> sig : allSignatures(boolean.class)) {
Method checkArgumentMethod =
Preconditions.class.getMethod("checkState", sig.toArray(new Class<?>[] {}));
checkArgumentMethod.invoke(null /* static method */, getParametersForSignature(true, sig));
Object[] failingParams = getParametersForSignature(false, sig);
try {
checkArgumentMethod.invoke(null /* static method */, failingParams);
fail();
} catch (InvocationTargetException ite) {
assertFailureCause(ite.getCause(), IllegalStateException.class, failingParams);
}
}
}
@GwtIncompatible("Reflection")
public void testAllOverloads_checkNotNull() throws Exception {
for (ImmutableList<Class<?>> sig : allSignatures(Object.class)) {
Method checkArgumentMethod =
Preconditions.class.getMethod("checkNotNull", sig.toArray(new Class<?>[] {}));
checkArgumentMethod.invoke(
null /* static method */, getParametersForSignature(new Object(), sig));
Object[] failingParams = getParametersForSignature(null, sig);
try {
checkArgumentMethod.invoke(null /* static method */, failingParams);
fail();
} catch (InvocationTargetException ite) {
assertFailureCause(ite.getCause(), NullPointerException.class, failingParams);
}
}
}
/**
* Asserts that the given throwable has the given class and then asserts on the message as using
* the full set of method parameters.
*/
private void assertFailureCause(
Throwable throwable, Class<? extends Throwable> clazz, Object[] params) {
assertThat(throwable).isInstanceOf(clazz);
if (params.length == 1) {
assertThat(throwable).hasMessage(null);
} else if (params.length == 2) {
assertThat(throwable).hasMessage("");
} else {
assertThat(throwable)
.hasMessage(Preconditions.format("", Arrays.copyOfRange(params, 2, params.length)));
}
}
/**
* Returns an array containing parameters for invoking a checkArgument, checkNotNull or checkState
* method reflectively
*
* @param firstParam The first parameter
* @param sig The method signature
*/
@GwtIncompatible("ArbitraryInstances")
private Object[] getParametersForSignature(Object firstParam, ImmutableList<Class<?>> sig) {
Object[] params = new Object[sig.size()];
params[0] = firstParam;
if (params.length > 1) {
params[1] = "";
if (params.length > 2) {
// fill in the rest of the array with arbitrary instances
for (int i = 2; i < params.length; i++) {
params[i] = ArbitraryInstances.get(sig.get(i));
}
}
}
return params;
}
private static final ImmutableList<Class<?>> possibleParamTypes =
ImmutableList.of(
char.class,
int.class,
long.class,
Object.class);
/**
* Returns a list of parameters for invoking an overload of checkState, checkArgument or
* checkNotNull
*
* @param predicateType The first parameter to the method (boolean or Object)
*/
private static ImmutableList<ImmutableList<Class<?>>> allSignatures(Class<?> predicateType) {
ImmutableSet.Builder<ImmutableList<Class<?>>> allOverloads = ImmutableSet.builder();
// The first two are for the overloads that don't take formatting args, e.g.
// checkArgument(boolean) and checkArgument(boolean, Object)
allOverloads.add(ImmutableList.<Class<?>>of(predicateType));
allOverloads.add(ImmutableList.<Class<?>>of(predicateType, Object.class));
List<List<Class<?>>> typesLists = new ArrayList<List<Class<?>>>();
for (int i = 0; i < 2; i++) {
typesLists.add(possibleParamTypes);
for (List<Class<?>> curr : Lists.cartesianProduct(typesLists)) {
allOverloads.add(
ImmutableList.<Class<?>>builder()
.add(predicateType)
.add(String.class) // the format string
.addAll(curr)
.build());
}
}
return allOverloads.build().asList();
}
// 'test' to demonstrate some potentially ambiguous overloads. This 'test' is kind of strange,
// but essentially each line will be a call to a Preconditions method that, but for a documented
// change would be a compiler error.
// See http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2 for the spec on
// how javac selects overloads
@SuppressWarnings("null")
public void overloadSelection() {
Boolean boxedBoolean = null;
boolean aBoolean = true;
Long boxedLong = null;
int anInt = 1;
// With a boxed predicate, no overloads can be selected in phase 1
// ambiguous without the call to .booleanValue to unbox the Boolean
checkState(boxedBoolean.booleanValue(), "", 1);
// ambiguous without the cast to Object because the boxed predicate prevents any overload from
// being selected in phase 1
checkState(boxedBoolean, "", (Object) boxedLong);
// ternaries introduce their own problems. because of the ternary (which requires a boxing
// operation) no overload can be selected in phase 1. and in phase 2 it is ambiguous since it
// matches with the second parameter being boxed and without it being boxed. The cast to Object
// avoids this.
checkState(aBoolean, "", aBoolean ? "" : anInt, (Object) anInt);
// ambiguous without the .booleanValue() call since the boxing forces us into phase 2 resolution
short s = 2;
checkState(boxedBoolean.booleanValue(), "", s);
}
@GwtIncompatible // NullPointerTester
public void testNullPointers() {
NullPointerTester tester = new NullPointerTester();
tester.testAllPublicStaticMethods(Preconditions.class);
}
private static final Object IGNORE_ME = new Object() {
@Override public String toString() {
throw new AssertionFailedError();
}
};
private static class Message {
boolean invoked;
@Override public String toString() {
assertFalse(invoked);
invoked = true;
return "A message";
}
}
private static final String FORMAT = "I ate %s pies.";
private static void verifySimpleMessage(Exception e) {
assertThat(e).hasMessage("A message");
}
private static void verifyComplexMessage(Exception e) {
assertThat(e).hasMessage("I ate 5 pies.");
}
}