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 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 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 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 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 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 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 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 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 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 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 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 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 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 205 public void testNotInterfaceType() { 206 try { 207 new ForwardingWrapperTester().testForwarding(String.class, Functions.<String>identity()); 208 fail(); 209 } catch (IllegalArgumentException expected) {} 210 } 211 212 public void testNulls() { 213 new NullPointerTester() 214 .setDefault(Class.class, Runnable.class) 215 .testAllPublicInstanceMethods(new ForwardingWrapperTester()); 216 } 217 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 236 ForwardingRunnable(Runnable runnable) { 237 this.runnable = runnable; 238 } 239 240 @Override public void run() { 241 runnable.run(); 242 } 243 244 @Override public String toString() { 245 return runnable.toString(); 246 } 247 } 248 249 private interface Adder { 250 int add(int a, int b); 251 } 252 253 private static class ForwardingArithmetic implements Arithmetic { 254 private final Arithmetic arithmetic; 255 256 public ForwardingArithmetic(Arithmetic arithmetic) { 257 this.arithmetic = arithmetic; 258 } 259 260 @Override public int add(int a, int b) { 261 return arithmetic.add(a, b); 262 } 263 264 @Override public int minus(int a, int b) { 265 return arithmetic.minus(a, b); 266 } 267 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 276 FailsToForwardParameters(Adder adder) { 277 this.adder = adder; 278 } 279 280 @Override public int add(int a, int b) { 281 return adder.add(b, a); 282 } 283 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 292 FailsToForwardReturnValue(Adder adder) { 293 this.adder = adder; 294 } 295 296 @Override public int add(int a, int b) { 297 return adder.add(a, b) + 1; 298 } 299 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 308 FailsToPropagageException(Adder adder) { 309 this.adder = adder; 310 } 311 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 321 @Override public String toString() { 322 return adder.toString(); 323 } 324 } 325 326 public interface Arithmetic extends Adder { 327 int minus(int a, int b); 328 } 329 330 private static class ForwardsToTheWrongMethod implements Arithmetic { 331 private final Arithmetic arithmetic; 332 333 ForwardsToTheWrongMethod(Arithmetic arithmetic) { 334 this.arithmetic = arithmetic; 335 } 336 337 @Override public int minus(int a, int b) { // bad! 338 return arithmetic.add(b, a); 339 } 340 341 @Override public int add(int a, int b) { 342 return arithmetic.add(b, a); 343 } 344 345 @Override public String toString() { 346 return arithmetic.toString(); 347 } 348 } 349 350 private interface ParameterTypesDifferent { 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 361 public ParameterTypesDifferentForwarder(ParameterTypesDifferent delegate) { 362 this.delegate = delegate; 363 } 364 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 376 @Override public String toString() { 377 return delegate.toString(); 378 } 379 } 380 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 { 390 CharSequence getId(); 391 } 392 393 interface Sub extends Base { 394 @Override String getId(); 395 } 396 397 private static class ForwardingSub implements Sub { 398 private final Sub delegate; 399 400 ForwardingSub(Sub delegate) { 401 this.delegate = delegate; 402 } 403 404 @Override public String getId() { 405 return delegate.getId(); 406 } 407 408 @Override public String toString() { 409 return delegate.toString(); 410 } 411 } 412 413 private interface Equals { 414 @Override boolean equals(Object obj); 415 @Override int hashCode(); 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 429 NoDelegateToEquals(Equals delegate) { 430 this.delegate = delegate; 431 } 432 433 @Override public String toString() { 434 return delegate.toString(); 435 } 436 } 437 438 public void testExplicitEqualsAndHashCodeNotDelegatedByDefault() { 439 new ForwardingWrapperTester() 440 .testForwarding(Equals.class, NoDelegateToEquals.WRAPPER); 441 } 442 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' 459 ChainingCalls chainingCall(); 460 // A method that just happens to return a ChainingCalls object 461 ChainingCalls nonChainingCall(); 462 } 463 464 private static class ForwardingChainingCalls implements ChainingCalls { 465 final ChainingCalls delegate; 466 467 ForwardingChainingCalls(ChainingCalls delegate) { 468 this.delegate = delegate; 469 } 470 471 @Override public ForwardingChainingCalls chainingCall() { 472 delegate.chainingCall(); 473 return this; 474 } 475 476 @Override public ChainingCalls nonChainingCall() { 477 return delegate.nonChainingCall(); 478 } 479 480 @Override public String toString() { 481 return delegate.toString(); 482 } 483 } 484 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