/* * 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() { @Override public Arithmetic apply(Arithmetic arithmetic) { return new ForwardingArithmetic(arithmetic); } }); tester.testForwarding(ParameterTypesDifferent.class, new Function() { @Override public ParameterTypesDifferent apply(ParameterTypesDifferent delegate) { return new ParameterTypesDifferentForwarder(delegate); } }); } public void testVoidMethodForwarding() { tester.testForwarding(Runnable.class, new Function() { @Override public Runnable apply(final Runnable runnable) { return new ForwardingRunnable(runnable); } }); } public void testToStringForwarding() { tester.testForwarding(Runnable.class, new Function() { @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() { @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() { @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() { @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() { @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() { @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() { @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() { @Override public Adder apply(Adder adder) { return new FailsToForwardParameters(adder); } }, "add(", "Parameter #0"); } public void testForwardsToTheWrongMethod() { assertFailure(Arithmetic.class, new Function() { @Override public Arithmetic apply(Arithmetic adder) { return new ForwardsToTheWrongMethod(adder); } }, "minus"); } public void testFailsToForwardReturnValue() { assertFailure(Adder.class, new Function() { @Override public Adder apply(Adder adder) { return new FailsToForwardReturnValue(adder); } }, "add(", "Return value"); } public void testFailsToPropagateException() { assertFailure(Adder.class, new Function() { @Override public Adder apply(Adder adder) { return new FailsToPropagageException(adder); } }, "add(", "exception"); } public void testNotInterfaceType() { try { new ForwardingWrapperTester().testForwarding(String.class, Functions.identity()); fail(); } catch (IllegalArgumentException expected) {} } public void testNulls() { new NullPointerTester() .setDefault(Class.class, Runnable.class) .testAllPublicInstanceMethods(new ForwardingWrapperTester()); } private void assertFailure( Class interfaceType, Function 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 eq, Exception e, InputStream in, Comparable c, Ordering 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 eq, Exception e, InputStream in, Comparable c, Ordering 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() { @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 WRAPPER = new Function() { @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() { @Override public ChainingCalls apply(ChainingCalls delegate) { return new ForwardingChainingCalls(delegate); } }); } }