blob: 2d1382afbfdf0dcf1b88cf808feeab28fa0d098b [file] [log] [blame]
/*
* Copyright (C) 2012 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.testing.anotherpackage;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.base.Equivalence;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.Ordering;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.primitives.UnsignedLong;
import com.google.common.testing.ForwardingWrapperTester;
import com.google.common.testing.NullPointerTester;
import junit.framework.TestCase;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* Tests for {@link ForwardingWrapperTester}. Live in a different package to detect reflection
* access issues, if any.
*
* @author Ben Yu
*/
public class ForwardingWrapperTesterTest extends TestCase {
private final ForwardingWrapperTester tester = new ForwardingWrapperTester();
public void testGoodForwarder() {
tester.testForwarding(Arithmetic.class,
new Function<Arithmetic, Arithmetic>() {
@Override public Arithmetic apply(Arithmetic arithmetic) {
return new ForwardingArithmetic(arithmetic);
}
});
tester.testForwarding(ParameterTypesDifferent.class,
new Function<ParameterTypesDifferent, ParameterTypesDifferent>() {
@Override public ParameterTypesDifferent apply(ParameterTypesDifferent delegate) {
return new ParameterTypesDifferentForwarder(delegate);
}
});
}
public void testVoidMethodForwarding() {
tester.testForwarding(Runnable.class,
new Function<Runnable, Runnable>() {
@Override public Runnable apply(final Runnable runnable) {
return new ForwardingRunnable(runnable);
}
});
}
public void testToStringForwarding() {
tester.testForwarding(Runnable.class,
new Function<Runnable, Runnable>() {
@Override public Runnable apply(final Runnable runnable) {
return new ForwardingRunnable(runnable) {
@Override public String toString() {
return runnable.toString();
}
};
}
});
}
public void testFailsToForwardToString() {
assertFailure(Runnable.class, new Function<Runnable, Runnable>() {
@Override public Runnable apply(final Runnable runnable) {
return new ForwardingRunnable(runnable) {
@Override public String toString() {
return "";
}
};
}
}, "toString()");
}
public void testFailsToForwardHashCode() {
tester.includingEquals();
assertFailure(Runnable.class, new Function<Runnable, Runnable>() {
@Override public Runnable apply(final Runnable runnable) {
return new ForwardingRunnable(runnable) {
@Override public boolean equals(Object o) {
if (o instanceof ForwardingRunnable) {
ForwardingRunnable that = (ForwardingRunnable) o;
return runnable.equals(that.runnable);
}
return false;
}
};
}
}, "Runnable");
}
public void testEqualsAndHashCodeForwarded() {
tester.includingEquals();
tester.testForwarding(Runnable.class, new Function<Runnable, Runnable>() {
@Override public Runnable apply(final Runnable runnable) {
return new ForwardingRunnable(runnable) {
@Override public boolean equals(Object o) {
if (o instanceof ForwardingRunnable) {
ForwardingRunnable that = (ForwardingRunnable) o;
return runnable.equals(that.runnable);
}
return false;
}
@Override public int hashCode() {
return runnable.hashCode();
}
};
}
});
}
public void testFailsToForwardEquals() {
tester.includingEquals();
assertFailure(Runnable.class, new Function<Runnable, Runnable>() {
@Override public Runnable apply(final Runnable runnable) {
return new ForwardingRunnable(runnable) {
@Override public int hashCode() {
return runnable.hashCode();
}
};
}
}, "Runnable");
}
public void testFailsToForward() {
assertFailure(Runnable.class,
new Function<Runnable, Runnable>() {
@Override public Runnable apply(Runnable runnable) {
return new ForwardingRunnable(runnable) {
@Override public void run() {}
};
}
}, "run()", "Failed to forward");
}
public void testRedundantForwarding() {
assertFailure(Runnable.class,
new Function<Runnable, Runnable>() {
@Override public Runnable apply(final Runnable runnable) {
return new Runnable() {
@Override public void run() {
runnable.run();
runnable.run();
}
};
}
}, "run()", "invoked more than once");
}
public void testFailsToForwardParameters() {
assertFailure(Adder.class, new Function<Adder, Adder>() {
@Override public Adder apply(Adder adder) {
return new FailsToForwardParameters(adder);
}
}, "add(", "Parameter #0");
}
public void testForwardsToTheWrongMethod() {
assertFailure(Arithmetic.class, new Function<Arithmetic, Arithmetic>() {
@Override public Arithmetic apply(Arithmetic adder) {
return new ForwardsToTheWrongMethod(adder);
}
}, "minus");
}
public void testFailsToForwardReturnValue() {
assertFailure(Adder.class, new Function<Adder, Adder>() {
@Override public Adder apply(Adder adder) {
return new FailsToForwardReturnValue(adder);
}
}, "add(", "Return value");
}
public void testFailsToPropagateException() {
assertFailure(Adder.class, new Function<Adder, Adder>() {
@Override public Adder apply(Adder adder) {
return new FailsToPropagageException(adder);
}
}, "add(", "exception");
}
public void testNotInterfaceType() {
try {
new ForwardingWrapperTester().testForwarding(String.class, Functions.<String>identity());
fail();
} catch (IllegalArgumentException expected) {}
}
public void testNulls() {
new NullPointerTester()
.setDefault(Class.class, Runnable.class)
.testAllPublicInstanceMethods(new ForwardingWrapperTester());
}
private <T> void assertFailure(
Class<T> interfaceType, Function<T, ? extends T> wrapperFunction,
String... expectedMessages) {
try {
tester.testForwarding(interfaceType, wrapperFunction);
} catch (AssertionError expected) {
for (String message : expectedMessages) {
assertThat(expected.getMessage()).contains(message);
}
return;
}
fail("expected failure not reported");
}
private class ForwardingRunnable implements Runnable {
private final Runnable runnable;
ForwardingRunnable(Runnable runnable) {
this.runnable = runnable;
}
@Override public void run() {
runnable.run();
}
@Override public String toString() {
return runnable.toString();
}
}
private interface Adder {
int add(int a, int b);
}
private static class ForwardingArithmetic implements Arithmetic {
private final Arithmetic arithmetic;
public ForwardingArithmetic(Arithmetic arithmetic) {
this.arithmetic = arithmetic;
}
@Override public int add(int a, int b) {
return arithmetic.add(a, b);
}
@Override public int minus(int a, int b) {
return arithmetic.minus(a, b);
}
@Override public String toString() {
return arithmetic.toString();
}
}
private static class FailsToForwardParameters implements Adder {
private final Adder adder;
FailsToForwardParameters(Adder adder) {
this.adder = adder;
}
@Override public int add(int a, int b) {
return adder.add(b, a);
}
@Override public String toString() {
return adder.toString();
}
}
private static class FailsToForwardReturnValue implements Adder {
private final Adder adder;
FailsToForwardReturnValue(Adder adder) {
this.adder = adder;
}
@Override public int add(int a, int b) {
return adder.add(a, b) + 1;
}
@Override public String toString() {
return adder.toString();
}
}
private static class FailsToPropagageException implements Adder {
private final Adder adder;
FailsToPropagageException(Adder adder) {
this.adder = adder;
}
@Override public int add(int a, int b) {
try {
return adder.add(a, b);
} catch (Exception e) {
// swallow!
return 0;
}
}
@Override public String toString() {
return adder.toString();
}
}
public interface Arithmetic extends Adder {
int minus(int a, int b);
}
private static class ForwardsToTheWrongMethod implements Arithmetic {
private final Arithmetic arithmetic;
ForwardsToTheWrongMethod(Arithmetic arithmetic) {
this.arithmetic = arithmetic;
}
@Override public int minus(int a, int b) { // bad!
return arithmetic.add(b, a);
}
@Override public int add(int a, int b) {
return arithmetic.add(b, a);
}
@Override public String toString() {
return arithmetic.toString();
}
}
private interface ParameterTypesDifferent {
void foo(String s, Runnable r, Number n, Iterable<?> it, boolean b, Equivalence<String> eq,
Exception e, InputStream in, Comparable<?> c, Ordering<Integer> ord,
Charset charset, TimeUnit unit, Class<?> cls, Joiner joiner,
Pattern pattern, UnsignedInteger ui, UnsignedLong ul, StringBuilder sb,
Predicate<?> pred, Function<?, ?> func, Object obj);
}
private static class ParameterTypesDifferentForwarder implements ParameterTypesDifferent {
private final ParameterTypesDifferent delegate;
public ParameterTypesDifferentForwarder(ParameterTypesDifferent delegate) {
this.delegate = delegate;
}
@Override public void foo(
String s, Runnable r, Number n, Iterable<?> it, boolean b, Equivalence<String> eq,
Exception e, InputStream in, Comparable<?> c, Ordering<Integer> ord,
Charset charset, TimeUnit unit, Class<?> cls, Joiner joiner,
Pattern pattern, UnsignedInteger ui, UnsignedLong ul, StringBuilder sb,
Predicate<?> pred, Function<?, ?> func, Object obj) {
delegate.foo(s,
r, n, it, b, eq, e, in, c, ord, charset, unit, cls, joiner, pattern,
ui, ul, sb, pred, func, obj);
}
@Override public String toString() {
return delegate.toString();
}
}
public void testCovariantReturn() {
new ForwardingWrapperTester().testForwarding(Sub.class, new Function<Sub, Sub>() {
@Override public Sub apply(Sub sub) {
return new ForwardingSub(sub);
}
});
}
interface Base {
CharSequence getId();
}
interface Sub extends Base {
@Override String getId();
}
private static class ForwardingSub implements Sub {
private final Sub delegate;
ForwardingSub(Sub delegate) {
this.delegate = delegate;
}
@Override public String getId() {
return delegate.getId();
}
@Override public String toString() {
return delegate.toString();
}
}
private interface Equals {
@Override boolean equals(Object obj);
@Override int hashCode();
@Override String toString();
}
private static class NoDelegateToEquals implements Equals {
private static Function<Equals, Equals> WRAPPER = new Function<Equals, Equals>() {
@Override public NoDelegateToEquals apply(Equals delegate) {
return new NoDelegateToEquals(delegate);
}
};
private final Equals delegate;
NoDelegateToEquals(Equals delegate) {
this.delegate = delegate;
}
@Override public String toString() {
return delegate.toString();
}
}
public void testExplicitEqualsAndHashCodeNotDelegatedByDefault() {
new ForwardingWrapperTester()
.testForwarding(Equals.class, NoDelegateToEquals.WRAPPER);
}
public void testExplicitEqualsAndHashCodeDelegatedWhenExplicitlyAsked() {
try {
new ForwardingWrapperTester()
.includingEquals()
.testForwarding(Equals.class, NoDelegateToEquals.WRAPPER);
} catch (AssertionError expected) {
return;
}
fail("Should have failed");
}
/**
* An interface for the 2 ways that a chaining call might be defined.
*/
private interface ChainingCalls {
// A method that is defined to 'return this'
ChainingCalls chainingCall();
// A method that just happens to return a ChainingCalls object
ChainingCalls nonChainingCall();
}
private static class ForwardingChainingCalls implements ChainingCalls {
final ChainingCalls delegate;
ForwardingChainingCalls(ChainingCalls delegate) {
this.delegate = delegate;
}
@Override public ForwardingChainingCalls chainingCall() {
delegate.chainingCall();
return this;
}
@Override public ChainingCalls nonChainingCall() {
return delegate.nonChainingCall();
}
@Override public String toString() {
return delegate.toString();
}
}
public void testChainingCalls() {
tester.testForwarding(ChainingCalls.class,
new Function<ChainingCalls, ChainingCalls>() {
@Override public ChainingCalls apply(ChainingCalls delegate) {
return new ForwardingChainingCalls(delegate);
}
});
}
}