• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.reflect;
18 
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.Iterables;
21 import com.google.common.testing.EqualsTester;
22 import com.google.common.testing.NullPointerTester;
23 
24 import junit.framework.TestCase;
25 
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.lang.reflect.Constructor;
29 import java.lang.reflect.Method;
30 import java.lang.reflect.ParameterizedType;
31 import java.lang.reflect.TypeVariable;
32 import java.util.Collections;
33 
34 import javax.annotation.Nullable;
35 
36 /**
37  * Unit tests for {@link Invokable}.
38  *
39  * @author Ben Yu
40  */
41 public class InvokableTest extends TestCase {
42 
testConstructor_returnType()43   public void testConstructor_returnType() throws Exception {
44     assertEquals(Prepender.class,
45         Prepender.constructor().getReturnType().getType());
46   }
47 
48   private static class WithConstructorAndTypeParameter<T> {
49     @SuppressWarnings("unused") // by reflection
WithConstructorAndTypeParameter()50     <X> WithConstructorAndTypeParameter() {}
51   }
52 
testConstructor_returnType_hasTypeParameter()53   public void testConstructor_returnType_hasTypeParameter() throws Exception {
54     @SuppressWarnings("rawtypes") // Foo.class for Foo<T> is always raw type
55     Class<WithConstructorAndTypeParameter> type = WithConstructorAndTypeParameter.class;
56     @SuppressWarnings("rawtypes") // Foo.class
57     Constructor<WithConstructorAndTypeParameter> constructor = type.getDeclaredConstructor();
58     Invokable<?, ?> factory = Invokable.from(constructor);
59     assertEquals(2, factory.getTypeParameters().length);
60     assertEquals(type.getTypeParameters()[0], factory.getTypeParameters()[0]);
61     assertEquals(constructor.getTypeParameters()[0], factory.getTypeParameters()[1]);
62     ParameterizedType returnType = (ParameterizedType) factory.getReturnType().getType();
63     assertEquals(type, returnType.getRawType());
64     assertEquals(ImmutableList.copyOf(type.getTypeParameters()),
65         ImmutableList.copyOf(returnType.getActualTypeArguments()));
66   }
67 
testConstructor_exceptionTypes()68   public void testConstructor_exceptionTypes() throws Exception {
69     assertEquals(ImmutableList.of(TypeToken.of(NullPointerException.class)),
70         Prepender.constructor(String.class, int.class).getExceptionTypes());
71   }
72 
testConstructor_typeParameters()73   public void testConstructor_typeParameters() throws Exception {
74     TypeVariable<?>[] variables =
75         Prepender.constructor().getTypeParameters();
76     assertEquals(1, variables.length);
77     assertEquals("A", variables[0].getName());
78   }
79 
testConstructor_parameters()80   public void testConstructor_parameters() throws Exception {
81     Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
82     ImmutableList<Parameter> parameters = delegate.getParameters();
83     assertEquals(2, parameters.size());
84     assertEquals(String.class, parameters.get(0).getType().getType());
85     assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
86     assertEquals(int.class, parameters.get(1).getType().getType());
87     assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
88     new EqualsTester()
89         .addEqualityGroup(parameters.get(0))
90         .addEqualityGroup(parameters.get(1))
91         .testEquals();
92   }
93 
testConstructor_call()94   public void testConstructor_call() throws Exception {
95     Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
96     Prepender prepender = delegate.invoke(null, "a", 1);
97     assertEquals("a", prepender.prefix);
98     assertEquals(1, prepender.times);
99   }
100 
testConstructor_returning()101   public void testConstructor_returning() throws Exception {
102     Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class)
103         .returning(Prepender.class);
104     Prepender prepender = delegate.invoke(null, "a", 1);
105     assertEquals("a", prepender.prefix);
106     assertEquals(1, prepender.times);
107   }
108 
testConstructor_invalidReturning()109   public void testConstructor_invalidReturning() throws Exception {
110     Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
111     try {
112       delegate.returning(SubPrepender.class);
113       fail();
114     } catch (IllegalArgumentException expected) {}
115   }
116 
testStaticMethod_returnType()117   public void testStaticMethod_returnType() throws Exception {
118     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
119     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
120   }
121 
testStaticMethod_exceptionTypes()122   public void testStaticMethod_exceptionTypes() throws Exception {
123     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
124     assertEquals(ImmutableList.of(), delegate.getExceptionTypes());
125   }
126 
testStaticMethod_typeParameters()127   public void testStaticMethod_typeParameters() throws Exception {
128     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
129     TypeVariable<?>[] variables = delegate.getTypeParameters();
130     assertEquals(1, variables.length);
131     assertEquals("T", variables[0].getName());
132   }
133 
testStaticMethod_parameters()134   public void testStaticMethod_parameters() throws Exception {
135     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
136     ImmutableList<Parameter> parameters = delegate.getParameters();
137     assertEquals(2, parameters.size());
138     assertEquals(String.class, parameters.get(0).getType().getType());
139     assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
140     assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(1).getType());
141     assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
142     new EqualsTester()
143         .addEqualityGroup(parameters.get(0))
144         .addEqualityGroup(parameters.get(1))
145         .testEquals();
146   }
147 
testStaticMethod_call()148   public void testStaticMethod_call() throws Exception {
149     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
150     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
151     Iterable<String> result = (Iterable<String>)
152         delegate.invoke(null, "a", ImmutableList.of("b", "c"));
153     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
154   }
155 
testStaticMethod_returning()156   public void testStaticMethod_returning() throws Exception {
157     Invokable<?, Iterable<String>> delegate = Prepender.method(
158             "prepend", String.class, Iterable.class)
159         .returning(new TypeToken<Iterable<String>>() {});
160     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
161     Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
162     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
163   }
164 
testStaticMethod_returningRawType()165   public void testStaticMethod_returningRawType() throws Exception {
166     @SuppressWarnings("rawtypes") // the purpose is to test raw type
167     Invokable<?, Iterable> delegate = Prepender.method(
168             "prepend", String.class, Iterable.class)
169         .returning(Iterable.class);
170     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
171     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
172     Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
173     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
174   }
175 
testStaticMethod_invalidReturning()176   public void testStaticMethod_invalidReturning() throws Exception {
177     Invokable<?, Object> delegate = Prepender.method("prepend", String.class, Iterable.class);
178     try {
179       delegate.returning(new TypeToken<Iterable<Integer>>() {});
180       fail();
181     } catch (IllegalArgumentException expected) {}
182   }
183 
testInstanceMethod_returnType()184   public void testInstanceMethod_returnType() throws Exception {
185     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
186     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
187   }
188 
testInstanceMethod_exceptionTypes()189   public void testInstanceMethod_exceptionTypes() throws Exception {
190     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
191     assertEquals(
192         ImmutableList.of(
193             TypeToken.of(IllegalArgumentException.class),
194             TypeToken.of(NullPointerException.class)),
195         delegate.getExceptionTypes());
196   }
197 
testInstanceMethod_typeParameters()198   public void testInstanceMethod_typeParameters() throws Exception {
199     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
200     assertEquals(0, delegate.getTypeParameters().length);
201   }
202 
testInstanceMethod_parameters()203   public void testInstanceMethod_parameters() throws Exception {
204     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
205     ImmutableList<Parameter> parameters = delegate.getParameters();
206     assertEquals(1, parameters.size());
207     assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(0).getType());
208     assertEquals(0, parameters.get(0).getAnnotations().length);
209     new EqualsTester()
210         .addEqualityGroup(parameters.get(0))
211         .testEquals();
212   }
213 
testInstanceMethod_call()214   public void testInstanceMethod_call() throws Exception {
215     Invokable<Prepender, ?> delegate = Prepender.method("prepend", Iterable.class);
216     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
217     Iterable<String> result = (Iterable<String>)
218         delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
219     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
220   }
221 
testInstanceMethod_returning()222   public void testInstanceMethod_returning() throws Exception {
223     Invokable<Prepender, Iterable<String>> delegate = Prepender.method(
224             "prepend", Iterable.class)
225         .returning(new TypeToken<Iterable<String>>() {});
226     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
227     Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
228     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
229   }
230 
testInstanceMethod_returningRawType()231   public void testInstanceMethod_returningRawType() throws Exception {
232     @SuppressWarnings("rawtypes") // the purpose is to test raw type
233     Invokable<Prepender, Iterable> delegate = Prepender.method("prepend", Iterable.class)
234         .returning(Iterable.class);
235     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
236     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
237     Iterable<String> result = delegate.invoke(
238         new Prepender("a", 2), ImmutableList.of("b", "c"));
239     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
240   }
241 
testInstanceMethod_invalidReturning()242   public void testInstanceMethod_invalidReturning() throws Exception {
243     Invokable<?, Object> delegate = Prepender.method("prepend", Iterable.class);
244     try {
245       delegate.returning(new TypeToken<Iterable<Integer>>() {});
246       fail();
247     } catch (IllegalArgumentException expected) {}
248   }
249 
testPrivateInstanceMethod_isOverridable()250   public void testPrivateInstanceMethod_isOverridable() throws Exception {
251     Invokable<?, ?> delegate = Prepender.method("privateMethod");
252     assertTrue(delegate.isPrivate());
253     assertFalse(delegate.isOverridable());
254     assertFalse(delegate.isVarArgs());
255   }
256 
testPrivateFinalInstanceMethod_isOverridable()257   public void testPrivateFinalInstanceMethod_isOverridable() throws Exception {
258     Invokable<?, ?> delegate = Prepender.method("privateFinalMethod");
259     assertTrue(delegate.isPrivate());
260     assertTrue(delegate.isFinal());
261     assertFalse(delegate.isOverridable());
262     assertFalse(delegate.isVarArgs());
263   }
264 
testStaticMethod_isOverridable()265   public void testStaticMethod_isOverridable() throws Exception {
266     Invokable<?, ?> delegate = Prepender.method("staticMethod");
267     assertTrue(delegate.isStatic());
268     assertFalse(delegate.isOverridable());
269     assertFalse(delegate.isVarArgs());
270   }
271 
testStaticFinalMethod_isFinal()272   public void testStaticFinalMethod_isFinal() throws Exception {
273     Invokable<?, ?> delegate = Prepender.method("staticFinalMethod");
274     assertTrue(delegate.isStatic());
275     assertTrue(delegate.isFinal());
276     assertFalse(delegate.isOverridable());
277     assertFalse(delegate.isVarArgs());
278   }
279 
280   static class Foo {}
281 
testConstructor_isOverridablel()282   public void testConstructor_isOverridablel() throws Exception {
283     Invokable<?, ?> delegate = Invokable.from(Foo.class.getDeclaredConstructor());
284     assertFalse(delegate.isOverridable());
285     assertFalse(delegate.isVarArgs());
286   }
287 
testMethod_isVarArgs()288   public void testMethod_isVarArgs() throws Exception {
289     Invokable<?, ?> delegate = Prepender.method("privateVarArgsMethod", String[].class);
290     assertTrue(delegate.isVarArgs());
291   }
292 
testConstructor_isVarArgs()293   public void testConstructor_isVarArgs() throws Exception {
294     Invokable<?, ?> delegate = Prepender.constructor(String[].class);
295     assertTrue(delegate.isVarArgs());
296   }
297 
testGetOwnerType_constructor()298   public void testGetOwnerType_constructor() throws Exception {
299     Invokable<String, String> invokable = Invokable.from(String.class.getConstructor());
300     assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
301   }
302 
testGetOwnerType_method()303   public void testGetOwnerType_method() throws Exception {
304     Invokable<?, ?> invokable = Invokable.from(String.class.getMethod("length"));
305     assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
306   }
307 
308   private static final class FinalClass {
309     @SuppressWarnings("unused") // used by reflection
notFinalMethod()310     void notFinalMethod() {}
311   }
312 
testNonFinalMethodInFinalClass_isOverridable()313   public void testNonFinalMethodInFinalClass_isOverridable() throws Exception {
314     Invokable<?, ?> delegate = Invokable.from(
315         FinalClass.class.getDeclaredMethod("notFinalMethod"));
316     assertFalse(delegate.isOverridable());
317     assertFalse(delegate.isVarArgs());
318   }
319 
320   private class InnerWithDefaultConstructor {
321     class NestedInner {}
322   }
323 
testInnerClassDefaultConstructor()324   public void testInnerClassDefaultConstructor() {
325     Constructor<?> constructor =
326         InnerWithDefaultConstructor.class.getDeclaredConstructors() [0];
327     assertEquals(0, Invokable.from(constructor).getParameters().size());
328   }
329 
testNestedInnerClassDefaultConstructor()330   public void testNestedInnerClassDefaultConstructor() {
331     Constructor<?> constructor =
332         InnerWithDefaultConstructor.NestedInner.class.getDeclaredConstructors() [0];
333     assertEquals(0, Invokable.from(constructor).getParameters().size());
334   }
335 
336   private class InnerWithOneParameterConstructor {
337     @SuppressWarnings("unused") // called by reflection
InnerWithOneParameterConstructor(String s)338     public InnerWithOneParameterConstructor(String s) {}
339   }
340 
testInnerClassWithOneParameterConstructor()341   public void testInnerClassWithOneParameterConstructor() {
342     Constructor<?> constructor =
343         InnerWithOneParameterConstructor.class.getDeclaredConstructors()[0];
344     Invokable<?, ?> invokable = Invokable.from(constructor);
345     assertEquals(1, invokable.getParameters().size());
346     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
347   }
348 
349   private class InnerWithAnnotatedConstructorParameter {
350     @SuppressWarnings("unused") // called by reflection
InnerWithAnnotatedConstructorParameter(@ullable String s)351     InnerWithAnnotatedConstructorParameter(@Nullable String s) {}
352   }
353 
testInnerClassWithAnnotatedConstructorParameter()354   public void testInnerClassWithAnnotatedConstructorParameter() {
355     Constructor<?> constructor =
356         InnerWithAnnotatedConstructorParameter.class.getDeclaredConstructors() [0];
357     Invokable<?, ?> invokable = Invokable.from(constructor);
358     assertEquals(1, invokable.getParameters().size());
359     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
360   }
361 
362   private class InnerWithGenericConstructorParameter {
363     @SuppressWarnings("unused") // called by reflection
InnerWithGenericConstructorParameter(Iterable<String> it, String s)364     InnerWithGenericConstructorParameter(Iterable<String> it, String s) {}
365   }
366 
testInnerClassWithGenericConstructorParameter()367   public void testInnerClassWithGenericConstructorParameter() {
368     Constructor<?> constructor =
369         InnerWithGenericConstructorParameter.class.getDeclaredConstructors() [0];
370     Invokable<?, ?> invokable = Invokable.from(constructor);
371     assertEquals(2, invokable.getParameters().size());
372     assertEquals(new TypeToken<Iterable<String>>() {},
373         invokable.getParameters().get(0).getType());
374     assertEquals(TypeToken.of(String.class),
375         invokable.getParameters().get(1).getType());
376   }
377 
testAnonymousClassDefaultConstructor()378   public void testAnonymousClassDefaultConstructor() {
379     final int i = 1;
380     final String s = "hello world";
381     Class<?> anonymous = new Runnable() {
382       @Override public void run() {
383         System.out.println(s + i);
384       }
385     }.getClass();
386     Constructor<?> constructor = anonymous.getDeclaredConstructors() [0];
387     assertEquals(0, Invokable.from(constructor).getParameters().size());
388   }
389 
testAnonymousClassWithTwoParametersConstructor()390   public void testAnonymousClassWithTwoParametersConstructor() {
391     abstract class Base {
392       @SuppressWarnings("unused") // called by reflection
393       Base(String s, int i) {}
394     }
395     Class<?> anonymous = new Base("test", 0) {}.getClass();
396     Constructor<?> constructor = anonymous.getDeclaredConstructors() [0];
397     assertEquals(2, Invokable.from(constructor).getParameters().size());
398   }
399 
testLocalClassDefaultConstructor()400   public void testLocalClassDefaultConstructor() {
401     final int i = 1;
402     final String s = "hello world";
403     class LocalWithDefaultConstructor implements Runnable {
404       @Override public void run() {
405         System.out.println(s + i);
406       }
407     }
408     Constructor<?> constructor = LocalWithDefaultConstructor.class.getDeclaredConstructors() [0];
409     assertEquals(0, Invokable.from(constructor).getParameters().size());
410   }
411 
testStaticAnonymousClassDefaultConstructor()412   public void testStaticAnonymousClassDefaultConstructor() throws Exception {
413     doTestStaticAnonymousClassDefaultConstructor();
414   }
415 
doTestStaticAnonymousClassDefaultConstructor()416   private static void doTestStaticAnonymousClassDefaultConstructor() {
417     final int i = 1;
418     final String s = "hello world";
419     Class<?> anonymous = new Runnable() {
420       @Override public void run() {
421         System.out.println(s + i);
422       }
423     }.getClass();
424     Constructor<?> constructor = anonymous.getDeclaredConstructors() [0];
425     assertEquals(0, Invokable.from(constructor).getParameters().size());
426   }
427 
testAnonymousClassInConstructor()428   public void testAnonymousClassInConstructor() {
429     new AnonymousClassInConstructor();
430   }
431 
432   private static class AnonymousClassInConstructor {
AnonymousClassInConstructor()433     AnonymousClassInConstructor() {
434       final int i = 1;
435       final String s = "hello world";
436       Class<?> anonymous = new Runnable() {
437         @Override public void run() {
438           System.out.println(s + i);
439         }
440       }.getClass();
441       Constructor<?> constructor = anonymous.getDeclaredConstructors() [0];
442       assertEquals(0, Invokable.from(constructor).getParameters().size());
443     }
444   }
445 
testLocalClassInInstanceInitializer()446   public void testLocalClassInInstanceInitializer() {
447     new LocalClassInInstanceInitializer();
448   }
449 
450   private static class LocalClassInInstanceInitializer {
451     {
452       class Local {}
453       Constructor<?> constructor = Local.class.getDeclaredConstructors() [0];
454       assertEquals(0, Invokable.from(constructor).getParameters().size());
455     }
456   }
457 
testLocalClassInStaticInitializer()458   public void testLocalClassInStaticInitializer() {
459     new LocalClassInStaticInitializer();
460   }
461 
462   private static class LocalClassInStaticInitializer {
463     static {
464       class Local {}
465       Constructor<?> constructor = Local.class.getDeclaredConstructors() [0];
466       assertEquals(0, Invokable.from(constructor).getParameters().size());
467     }
468   }
469 
testLocalClassWithSeeminglyHiddenThisInStaticInitializer_BUG()470   public void testLocalClassWithSeeminglyHiddenThisInStaticInitializer_BUG() {
471     new LocalClassWithSeeminglyHiddenThisInStaticInitializer();
472   }
473 
474   /**
475    * This class demonstrates a bug in getParameters() when the local class is inside static
476    * initializer.
477    */
478   private static class LocalClassWithSeeminglyHiddenThisInStaticInitializer {
479     static {
480       class Local {
481         @SuppressWarnings("unused") // through reflection
Local(LocalClassWithSeeminglyHiddenThisInStaticInitializer outer)482         Local(LocalClassWithSeeminglyHiddenThisInStaticInitializer outer) {}
483       }
484       Constructor<?> constructor = Local.class.getDeclaredConstructors() [0];
485       int miscalculated = 0;
assertEquals(miscalculated, Invokable.from(constructor).getParameters().size())486       assertEquals(miscalculated, Invokable.from(constructor).getParameters().size());
487     }
488   }
489 
testLocalClassWithOneParameterConstructor()490   public void testLocalClassWithOneParameterConstructor() throws Exception {
491     final int i = 1;
492     final String s = "hello world";
493     class LocalWithOneParameterConstructor {
494       @SuppressWarnings("unused") // called by reflection
495       public LocalWithOneParameterConstructor(String x) {
496         System.out.println(s + i);
497       }
498     }
499     Constructor<?> constructor =
500         LocalWithOneParameterConstructor.class.getDeclaredConstructors()[0];
501     Invokable<?, ?> invokable = Invokable.from(constructor);
502     assertEquals(1, invokable.getParameters().size());
503     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
504   }
505 
testLocalClassWithAnnotatedConstructorParameter()506   public void testLocalClassWithAnnotatedConstructorParameter() throws Exception {
507     class LocalWithAnnotatedConstructorParameter {
508       @SuppressWarnings("unused") // called by reflection
509       LocalWithAnnotatedConstructorParameter(@Nullable String s) {}
510     }
511     Constructor<?> constructor =
512         LocalWithAnnotatedConstructorParameter.class.getDeclaredConstructors() [0];
513     Invokable<?, ?> invokable = Invokable.from(constructor);
514     assertEquals(1, invokable.getParameters().size());
515     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
516   }
517 
testLocalClassWithGenericConstructorParameter()518   public void testLocalClassWithGenericConstructorParameter() throws Exception {
519     class LocalWithGenericConstructorParameter {
520       @SuppressWarnings("unused") // called by reflection
521       LocalWithGenericConstructorParameter(Iterable<String> it, String s) {}
522     }
523     Constructor<?> constructor =
524         LocalWithGenericConstructorParameter.class.getDeclaredConstructors() [0];
525     Invokable<?, ?> invokable = Invokable.from(constructor);
526     assertEquals(2, invokable.getParameters().size());
527     assertEquals(new TypeToken<Iterable<String>>() {},
528         invokable.getParameters().get(0).getType());
529     assertEquals(TypeToken.of(String.class),
530         invokable.getParameters().get(1).getType());
531   }
532 
testEquals()533   public void testEquals() throws Exception {
534     new EqualsTester()
535         .addEqualityGroup(Prepender.constructor(), Prepender.constructor())
536         .addEqualityGroup(Prepender.constructor(String.class, int.class))
537         .addEqualityGroup(Prepender.method("privateMethod"), Prepender.method("privateMethod"))
538         .addEqualityGroup(Prepender.method("privateFinalMethod"))
539         .testEquals();
540   }
541 
testNulls()542   public void testNulls() {
543     new NullPointerTester().testAllPublicStaticMethods(Invokable.class);
544     new NullPointerTester().testAllPublicInstanceMethods(Prepender.method("staticMethod"));
545   }
546 
547   @Retention(RetentionPolicy.RUNTIME)
548   private @interface NotBlank {}
549 
550   /** Class for testing constructor, static method and instance method. */
551   @SuppressWarnings("unused") // most are called by reflection
552   private static class Prepender {
553 
554     private final String prefix;
555     private final int times;
556 
Prepender(@otBlank String prefix, int times)557     Prepender(@NotBlank String prefix, int times) throws NullPointerException {
558       this.prefix = prefix;
559       this.times = times;
560     }
561 
Prepender(String... varargs)562     Prepender(String... varargs) {
563       this(null, 0);
564     }
565 
566     // just for testing
Prepender()567     private <A> Prepender() {
568       this(null, 0);
569     }
570 
prepend(@otBlank String first, Iterable<String> tail)571     static <T> Iterable<String> prepend(@NotBlank String first, Iterable<String> tail) {
572       return Iterables.concat(ImmutableList.of(first), tail);
573     }
574 
prepend(Iterable<String> tail)575     Iterable<String> prepend(Iterable<String> tail)
576         throws IllegalArgumentException, NullPointerException {
577       return Iterables.concat(Collections.nCopies(times, prefix), tail);
578     }
579 
constructor(Class<?>.... parameterTypes)580     static Invokable<?, Prepender> constructor(Class<?>... parameterTypes) throws Exception {
581       Constructor<Prepender> constructor = Prepender.class.getDeclaredConstructor(parameterTypes);
582       return Invokable.from(constructor);
583     }
584 
method(String name, Class<?>... parameterTypes)585     static Invokable<Prepender, Object> method(String name, Class<?>... parameterTypes) {
586       try {
587         Method method = Prepender.class.getDeclaredMethod(name, parameterTypes);
588         @SuppressWarnings("unchecked") // The method is from Prepender.
589         Invokable<Prepender, Object> invokable = (Invokable<Prepender, Object>)
590             Invokable.from(method);
591         return invokable;
592       } catch (NoSuchMethodException e) {
593         throw new IllegalArgumentException(e);
594       }
595     }
596 
privateMethod()597     private void privateMethod() {}
598 
privateFinalMethod()599     private final void privateFinalMethod() {}
600 
staticMethod()601     static void staticMethod() {}
602 
staticFinalMethod()603     static final void staticFinalMethod() {}
604 
privateVarArgsMethod(String... varargs)605     private void privateVarArgsMethod(String... varargs) {}
606   }
607 
608   private static class SubPrepender extends Prepender {
609     @SuppressWarnings("unused") // needed to satisfy compiler, never called
SubPrepender()610     public SubPrepender() throws NullPointerException {
611       throw new AssertionError();
612     }
613   }
614 }
615