1 /* 2 * Copyright (C) 2012 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.testing.anotherpackage; 18 19 import static org.truth0.Truth.ASSERT; 20 21 import com.google.common.base.Equivalence; 22 import com.google.common.base.Function; 23 import com.google.common.base.Functions; 24 import com.google.common.base.Joiner; 25 import com.google.common.base.Predicate; 26 import com.google.common.collect.Ordering; 27 import com.google.common.primitives.UnsignedInteger; 28 import com.google.common.primitives.UnsignedLong; 29 import com.google.common.testing.ForwardingWrapperTester; 30 import com.google.common.testing.NullPointerTester; 31 32 import junit.framework.TestCase; 33 34 import java.io.InputStream; 35 import java.nio.charset.Charset; 36 import java.util.concurrent.TimeUnit; 37 import java.util.regex.Pattern; 38 39 /** 40 * Tests for {@link ForwardingWrapperTester}. Live in a different package to detect reflection 41 * access issues, if any. 42 * 43 * @author Ben Yu 44 */ 45 public class ForwardingWrapperTesterTest extends TestCase { 46 47 private final ForwardingWrapperTester tester = new ForwardingWrapperTester(); 48 testGoodForwarder()49 public void testGoodForwarder() { 50 tester.testForwarding(Arithmetic.class, 51 new Function<Arithmetic, Arithmetic>() { 52 @Override public Arithmetic apply(Arithmetic arithmetic) { 53 return new ForwardingArithmetic(arithmetic); 54 } 55 }); 56 tester.testForwarding(ParameterTypesDifferent.class, 57 new Function<ParameterTypesDifferent, ParameterTypesDifferent>() { 58 @Override public ParameterTypesDifferent apply(ParameterTypesDifferent delegate) { 59 return new ParameterTypesDifferentForwarder(delegate); 60 } 61 }); 62 } 63 testVoidMethodForwarding()64 public void testVoidMethodForwarding() { 65 tester.testForwarding(Runnable.class, 66 new Function<Runnable, Runnable>() { 67 @Override public Runnable apply(final Runnable runnable) { 68 return new ForwardingRunnable(runnable); 69 } 70 }); 71 } 72 testToStringForwarding()73 public void testToStringForwarding() { 74 tester.testForwarding(Runnable.class, 75 new Function<Runnable, Runnable>() { 76 @Override public Runnable apply(final Runnable runnable) { 77 return new ForwardingRunnable(runnable) { 78 @Override public String toString() { 79 return runnable.toString(); 80 } 81 }; 82 } 83 }); 84 } 85 testFailsToForwardToString()86 public void testFailsToForwardToString() { 87 assertFailure(Runnable.class, new Function<Runnable, Runnable>() { 88 @Override public Runnable apply(final Runnable runnable) { 89 return new ForwardingRunnable(runnable) { 90 @Override public String toString() { 91 return ""; 92 } 93 }; 94 } 95 }, "toString()"); 96 } 97 testFailsToForwardHashCode()98 public void testFailsToForwardHashCode() { 99 tester.includingEquals(); 100 assertFailure(Runnable.class, new Function<Runnable, Runnable>() { 101 @Override public Runnable apply(final Runnable runnable) { 102 return new ForwardingRunnable(runnable) { 103 @Override public boolean equals(Object o) { 104 if (o instanceof ForwardingRunnable) { 105 ForwardingRunnable that = (ForwardingRunnable) o; 106 return runnable.equals(that.runnable); 107 } 108 return false; 109 } 110 }; 111 } 112 }, "Runnable"); 113 } 114 testEqualsAndHashCodeForwarded()115 public void testEqualsAndHashCodeForwarded() { 116 tester.includingEquals(); 117 tester.testForwarding(Runnable.class, new Function<Runnable, Runnable>() { 118 @Override public Runnable apply(final Runnable runnable) { 119 return new ForwardingRunnable(runnable) { 120 @Override public boolean equals(Object o) { 121 if (o instanceof ForwardingRunnable) { 122 ForwardingRunnable that = (ForwardingRunnable) o; 123 return runnable.equals(that.runnable); 124 } 125 return false; 126 } 127 @Override public int hashCode() { 128 return runnable.hashCode(); 129 } 130 }; 131 } 132 }); 133 } 134 testFailsToForwardEquals()135 public void testFailsToForwardEquals() { 136 tester.includingEquals(); 137 assertFailure(Runnable.class, new Function<Runnable, Runnable>() { 138 @Override public Runnable apply(final Runnable runnable) { 139 return new ForwardingRunnable(runnable) { 140 @Override public int hashCode() { 141 return runnable.hashCode(); 142 } 143 }; 144 } 145 }, "Runnable"); 146 } 147 testFailsToForward()148 public void testFailsToForward() { 149 assertFailure(Runnable.class, 150 new Function<Runnable, Runnable>() { 151 @Override public Runnable apply(Runnable runnable) { 152 return new ForwardingRunnable(runnable) { 153 @Override public void run() {} 154 }; 155 } 156 }, "run()", "Failed to forward"); 157 } 158 testRedundantForwarding()159 public void testRedundantForwarding() { 160 assertFailure(Runnable.class, 161 new Function<Runnable, Runnable>() { 162 @Override public Runnable apply(final Runnable runnable) { 163 return new Runnable() { 164 @Override public void run() { 165 runnable.run(); 166 runnable.run(); 167 } 168 }; 169 } 170 }, "run()", "invoked more than once"); 171 } 172 testFailsToForwardParameters()173 public void testFailsToForwardParameters() { 174 assertFailure(Adder.class, new Function<Adder, Adder>() { 175 @Override public Adder apply(Adder adder) { 176 return new FailsToForwardParameters(adder); 177 } 178 }, "add(", "Parameter #0"); 179 } 180 testForwardsToTheWrongMethod()181 public void testForwardsToTheWrongMethod() { 182 assertFailure(Arithmetic.class, new Function<Arithmetic, Arithmetic>() { 183 @Override public Arithmetic apply(Arithmetic adder) { 184 return new ForwardsToTheWrongMethod(adder); 185 } 186 }, "minus"); 187 } 188 testFailsToForwardReturnValue()189 public void testFailsToForwardReturnValue() { 190 assertFailure(Adder.class, new Function<Adder, Adder>() { 191 @Override public Adder apply(Adder adder) { 192 return new FailsToForwardReturnValue(adder); 193 } 194 }, "add(", "Return value"); 195 } 196 testFailsToPropagateException()197 public void testFailsToPropagateException() { 198 assertFailure(Adder.class, new Function<Adder, Adder>() { 199 @Override public Adder apply(Adder adder) { 200 return new FailsToPropagageException(adder); 201 } 202 }, "add(", "exception"); 203 } 204 testNotInterfaceType()205 public void testNotInterfaceType() { 206 try { 207 new ForwardingWrapperTester().testForwarding(String.class, Functions.<String>identity()); 208 fail(); 209 } catch (IllegalArgumentException expected) {} 210 } 211 testNulls()212 public void testNulls() { 213 new NullPointerTester() 214 .setDefault(Class.class, Runnable.class) 215 .testAllPublicInstanceMethods(new ForwardingWrapperTester()); 216 } 217 assertFailure( Class<T> interfaceType, Function<T, ? extends T> wrapperFunction, String... expectedMessages)218 private <T> void assertFailure( 219 Class<T> interfaceType, Function<T, ? extends T> wrapperFunction, 220 String... expectedMessages) { 221 try { 222 tester.testForwarding(interfaceType, wrapperFunction); 223 } catch (AssertionError expected) { 224 for (String message : expectedMessages) { 225 ASSERT.that(expected.getMessage()).contains(message); 226 } 227 return; 228 } 229 fail("expected failure not reported"); 230 } 231 232 private class ForwardingRunnable implements Runnable { 233 234 private final Runnable runnable; 235 ForwardingRunnable(Runnable runnable)236 ForwardingRunnable(Runnable runnable) { 237 this.runnable = runnable; 238 } 239 run()240 @Override public void run() { 241 runnable.run(); 242 } 243 toString()244 @Override public String toString() { 245 return runnable.toString(); 246 } 247 } 248 249 private interface Adder { add(int a, int b)250 int add(int a, int b); 251 } 252 253 private static class ForwardingArithmetic implements Arithmetic { 254 private final Arithmetic arithmetic; 255 ForwardingArithmetic(Arithmetic arithmetic)256 public ForwardingArithmetic(Arithmetic arithmetic) { 257 this.arithmetic = arithmetic; 258 } 259 add(int a, int b)260 @Override public int add(int a, int b) { 261 return arithmetic.add(a, b); 262 } 263 minus(int a, int b)264 @Override public int minus(int a, int b) { 265 return arithmetic.minus(a, b); 266 } 267 toString()268 @Override public String toString() { 269 return arithmetic.toString(); 270 } 271 } 272 273 private static class FailsToForwardParameters implements Adder { 274 private final Adder adder; 275 FailsToForwardParameters(Adder adder)276 FailsToForwardParameters(Adder adder) { 277 this.adder = adder; 278 } 279 add(int a, int b)280 @Override public int add(int a, int b) { 281 return adder.add(b, a); 282 } 283 toString()284 @Override public String toString() { 285 return adder.toString(); 286 } 287 } 288 289 private static class FailsToForwardReturnValue implements Adder { 290 private final Adder adder; 291 FailsToForwardReturnValue(Adder adder)292 FailsToForwardReturnValue(Adder adder) { 293 this.adder = adder; 294 } 295 add(int a, int b)296 @Override public int add(int a, int b) { 297 return adder.add(a, b) + 1; 298 } 299 toString()300 @Override public String toString() { 301 return adder.toString(); 302 } 303 } 304 305 private static class FailsToPropagageException implements Adder { 306 private final Adder adder; 307 FailsToPropagageException(Adder adder)308 FailsToPropagageException(Adder adder) { 309 this.adder = adder; 310 } 311 add(int a, int b)312 @Override public int add(int a, int b) { 313 try { 314 return adder.add(a, b); 315 } catch (Exception e) { 316 // swallow! 317 return 0; 318 } 319 } 320 toString()321 @Override public String toString() { 322 return adder.toString(); 323 } 324 } 325 326 public interface Arithmetic extends Adder { minus(int a, int b)327 int minus(int a, int b); 328 } 329 330 private static class ForwardsToTheWrongMethod implements Arithmetic { 331 private final Arithmetic arithmetic; 332 ForwardsToTheWrongMethod(Arithmetic arithmetic)333 ForwardsToTheWrongMethod(Arithmetic arithmetic) { 334 this.arithmetic = arithmetic; 335 } 336 minus(int a, int b)337 @Override public int minus(int a, int b) { // bad! 338 return arithmetic.add(b, a); 339 } 340 add(int a, int b)341 @Override public int add(int a, int b) { 342 return arithmetic.add(b, a); 343 } 344 toString()345 @Override public String toString() { 346 return arithmetic.toString(); 347 } 348 } 349 350 private interface ParameterTypesDifferent { 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)351 void foo(String s, Runnable r, Number n, Iterable<?> it, boolean b, Equivalence<String> eq, 352 Exception e, InputStream in, Comparable<?> c, Ordering<Integer> ord, 353 Charset charset, TimeUnit unit, Class<?> cls, Joiner joiner, 354 Pattern pattern, UnsignedInteger ui, UnsignedLong ul, StringBuilder sb, 355 Predicate<?> pred, Function<?, ?> func, Object obj); 356 } 357 358 private static class ParameterTypesDifferentForwarder implements ParameterTypesDifferent { 359 private final ParameterTypesDifferent delegate; 360 ParameterTypesDifferentForwarder(ParameterTypesDifferent delegate)361 public ParameterTypesDifferentForwarder(ParameterTypesDifferent delegate) { 362 this.delegate = delegate; 363 } 364 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)365 @Override public void foo( 366 String s, Runnable r, Number n, Iterable<?> it, boolean b, Equivalence<String> eq, 367 Exception e, InputStream in, Comparable<?> c, Ordering<Integer> ord, 368 Charset charset, TimeUnit unit, Class<?> cls, Joiner joiner, 369 Pattern pattern, UnsignedInteger ui, UnsignedLong ul, StringBuilder sb, 370 Predicate<?> pred, Function<?, ?> func, Object obj) { 371 delegate.foo(s, 372 r, n, it, b, eq, e, in, c, ord, charset, unit, cls, joiner, pattern, 373 ui, ul, sb, pred, func, obj); 374 } 375 toString()376 @Override public String toString() { 377 return delegate.toString(); 378 } 379 } 380 testCovariantReturn()381 public void testCovariantReturn() { 382 new ForwardingWrapperTester().testForwarding(Sub.class, new Function<Sub, Sub>() { 383 @Override public Sub apply(Sub sub) { 384 return new ForwardingSub(sub); 385 } 386 }); 387 } 388 389 interface Base { getId()390 CharSequence getId(); 391 } 392 393 interface Sub extends Base { getId()394 @Override String getId(); 395 } 396 397 private static class ForwardingSub implements Sub { 398 private final Sub delegate; 399 ForwardingSub(Sub delegate)400 ForwardingSub(Sub delegate) { 401 this.delegate = delegate; 402 } 403 getId()404 @Override public String getId() { 405 return delegate.getId(); 406 } 407 toString()408 @Override public String toString() { 409 return delegate.toString(); 410 } 411 } 412 413 private interface Equals { equals(Object obj)414 @Override boolean equals(Object obj); hashCode()415 @Override int hashCode(); toString()416 @Override String toString(); 417 } 418 419 private static class NoDelegateToEquals implements Equals { 420 421 private static Function<Equals, Equals> WRAPPER = new Function<Equals, Equals>() { 422 @Override public NoDelegateToEquals apply(Equals delegate) { 423 return new NoDelegateToEquals(delegate); 424 } 425 }; 426 427 private final Equals delegate; 428 NoDelegateToEquals(Equals delegate)429 NoDelegateToEquals(Equals delegate) { 430 this.delegate = delegate; 431 } 432 toString()433 @Override public String toString() { 434 return delegate.toString(); 435 } 436 } 437 testExplicitEqualsAndHashCodeNotDelegatedByDefault()438 public void testExplicitEqualsAndHashCodeNotDelegatedByDefault() { 439 new ForwardingWrapperTester() 440 .testForwarding(Equals.class, NoDelegateToEquals.WRAPPER); 441 } 442 testExplicitEqualsAndHashCodeDelegatedWhenExplicitlyAsked()443 public void testExplicitEqualsAndHashCodeDelegatedWhenExplicitlyAsked() { 444 try { 445 new ForwardingWrapperTester() 446 .includingEquals() 447 .testForwarding(Equals.class, NoDelegateToEquals.WRAPPER); 448 } catch (AssertionError expected) { 449 return; 450 } 451 fail("Should have failed"); 452 } 453 454 /** 455 * An interface for the 2 ways that a chaining call might be defined. 456 */ 457 private interface ChainingCalls { 458 // A method that is defined to 'return this' chainingCall()459 ChainingCalls chainingCall(); 460 // A method that just happens to return a ChainingCalls object nonChainingCall()461 ChainingCalls nonChainingCall(); 462 } 463 464 private static class ForwardingChainingCalls implements ChainingCalls { 465 final ChainingCalls delegate; 466 ForwardingChainingCalls(ChainingCalls delegate)467 ForwardingChainingCalls(ChainingCalls delegate) { 468 this.delegate = delegate; 469 } 470 chainingCall()471 @Override public ForwardingChainingCalls chainingCall() { 472 delegate.chainingCall(); 473 return this; 474 } 475 nonChainingCall()476 @Override public ChainingCalls nonChainingCall() { 477 return delegate.nonChainingCall(); 478 } 479 toString()480 @Override public String toString() { 481 return delegate.toString(); 482 } 483 } 484 testChainingCalls()485 public void testChainingCalls() { 486 tester.testForwarding(ChainingCalls.class, 487 new Function<ChainingCalls, ChainingCalls>() { 488 @Override public ChainingCalls apply(ChainingCalls delegate) { 489 return new ForwardingChainingCalls(delegate); 490 } 491 }); 492 } 493 } 494