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
17package com.google.common.testing.anotherpackage;
18
19import static com.google.common.truth.Truth.assertThat;
20
21import com.google.common.base.Equivalence;
22import com.google.common.base.Function;
23import com.google.common.base.Functions;
24import com.google.common.base.Joiner;
25import com.google.common.base.Predicate;
26import com.google.common.collect.Ordering;
27import com.google.common.primitives.UnsignedInteger;
28import com.google.common.primitives.UnsignedLong;
29import com.google.common.testing.ForwardingWrapperTester;
30import com.google.common.testing.NullPointerTester;
31
32import junit.framework.TestCase;
33
34import java.io.InputStream;
35import java.nio.charset.Charset;
36import java.util.concurrent.TimeUnit;
37import 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 */
45public 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        assertThat(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