• 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 static com.google.common.truth.Truth.assertThat;
20 
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.Iterables;
23 import com.google.common.testing.EqualsTester;
24 import com.google.common.testing.NullPointerTester;
25 import java.lang.annotation.Retention;
26 import java.lang.annotation.RetentionPolicy;
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.Method;
29 import java.lang.reflect.ParameterizedType;
30 import java.lang.reflect.TypeVariable;
31 import java.util.Collections;
32 import junit.framework.TestCase;
33 import org.checkerframework.checker.nullness.compatqual.NullableDecl;
34 
35 /**
36  * Unit tests for {@link Invokable}.
37  *
38  * @author Ben Yu
39  */
40 @AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations?
41 public class InvokableTest extends TestCase {
42 
testConstructor_returnType()43   public void testConstructor_returnType() throws Exception {
44     assertEquals(Prepender.class, Prepender.constructor().getReturnType().getType());
45   }
46 
47   private static class WithConstructorAndTypeParameter<T> {
48     @SuppressWarnings("unused") // by reflection
WithConstructorAndTypeParameter()49     <X> WithConstructorAndTypeParameter() {}
50   }
51 
testConstructor_returnType_hasTypeParameter()52   public void testConstructor_returnType_hasTypeParameter() throws Exception {
53     @SuppressWarnings("rawtypes") // Foo.class for Foo<T> is always raw type
54     Class<WithConstructorAndTypeParameter> type = WithConstructorAndTypeParameter.class;
55     @SuppressWarnings("rawtypes") // Foo.class
56     Constructor<WithConstructorAndTypeParameter> constructor = type.getDeclaredConstructor();
57     Invokable<?, ?> factory = Invokable.from(constructor);
58     assertThat(factory.getTypeParameters()).hasLength(2);
59     assertEquals(type.getTypeParameters()[0], factory.getTypeParameters()[0]);
60     assertEquals(constructor.getTypeParameters()[0], factory.getTypeParameters()[1]);
61     ParameterizedType returnType = (ParameterizedType) factory.getReturnType().getType();
62     assertEquals(type, returnType.getRawType());
63     assertEquals(
64         ImmutableList.copyOf(type.getTypeParameters()),
65         ImmutableList.copyOf(returnType.getActualTypeArguments()));
66   }
67 
testConstructor_exceptionTypes()68   public void testConstructor_exceptionTypes() throws Exception {
69     assertEquals(
70         ImmutableList.of(TypeToken.of(NullPointerException.class)),
71         Prepender.constructor(String.class, int.class).getExceptionTypes());
72   }
73 
testConstructor_typeParameters()74   public void testConstructor_typeParameters() throws Exception {
75     TypeVariable<?>[] variables = Prepender.constructor().getTypeParameters();
76     assertThat(variables).hasLength(1);
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 =
103         Prepender.constructor(String.class, int.class).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   }
117 
testStaticMethod_returnType()118   public void testStaticMethod_returnType() throws Exception {
119     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
120     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
121   }
122 
testStaticMethod_exceptionTypes()123   public void testStaticMethod_exceptionTypes() throws Exception {
124     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
125     assertEquals(ImmutableList.of(), delegate.getExceptionTypes());
126   }
127 
testStaticMethod_typeParameters()128   public void testStaticMethod_typeParameters() throws Exception {
129     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
130     TypeVariable<?>[] variables = delegate.getTypeParameters();
131     assertThat(variables).hasLength(1);
132     assertEquals("T", variables[0].getName());
133   }
134 
testStaticMethod_parameters()135   public void testStaticMethod_parameters() throws Exception {
136     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
137     ImmutableList<Parameter> parameters = delegate.getParameters();
138     assertEquals(2, parameters.size());
139     assertEquals(String.class, parameters.get(0).getType().getType());
140     assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
141     assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(1).getType());
142     assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
143     new EqualsTester()
144         .addEqualityGroup(parameters.get(0))
145         .addEqualityGroup(parameters.get(1))
146         .testEquals();
147   }
148 
testStaticMethod_call()149   public void testStaticMethod_call() throws Exception {
150     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
151     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
152     Iterable<String> result =
153         (Iterable<String>) delegate.invoke(null, "a", ImmutableList.of("b", "c"));
154     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
155   }
156 
testStaticMethod_returning()157   public void testStaticMethod_returning() throws Exception {
158     Invokable<?, Iterable<String>> delegate =
159         Prepender.method("prepend", String.class, Iterable.class)
160             .returning(new TypeToken<Iterable<String>>() {});
161     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
162     Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
163     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
164   }
165 
testStaticMethod_returningRawType()166   public void testStaticMethod_returningRawType() throws Exception {
167     @SuppressWarnings("rawtypes") // the purpose is to test raw type
168     Invokable<?, Iterable> delegate =
169         Prepender.method("prepend", String.class, Iterable.class).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   }
184 
testInstanceMethod_returnType()185   public void testInstanceMethod_returnType() throws Exception {
186     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
187     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
188   }
189 
testInstanceMethod_exceptionTypes()190   public void testInstanceMethod_exceptionTypes() throws Exception {
191     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
192     assertEquals(
193         ImmutableList.of(
194             TypeToken.of(IllegalArgumentException.class), 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     assertThat(delegate.getTypeParameters()).isEmpty();
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     assertThat(parameters.get(0).getAnnotations()).isEmpty();
209     new EqualsTester().addEqualityGroup(parameters.get(0)).testEquals();
210   }
211 
testInstanceMethod_call()212   public void testInstanceMethod_call() throws Exception {
213     Invokable<Prepender, ?> delegate = Prepender.method("prepend", Iterable.class);
214     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
215     Iterable<String> result =
216         (Iterable<String>) delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
217     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
218   }
219 
testInstanceMethod_returning()220   public void testInstanceMethod_returning() throws Exception {
221     Invokable<Prepender, Iterable<String>> delegate =
222         Prepender.method("prepend", Iterable.class).returning(new TypeToken<Iterable<String>>() {});
223     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
224     Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
225     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
226   }
227 
testInstanceMethod_returningRawType()228   public void testInstanceMethod_returningRawType() throws Exception {
229     @SuppressWarnings("rawtypes") // the purpose is to test raw type
230     Invokable<Prepender, Iterable> delegate =
231         Prepender.method("prepend", Iterable.class).returning(Iterable.class);
232     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
233     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
234     Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
235     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
236   }
237 
testInstanceMethod_invalidReturning()238   public void testInstanceMethod_invalidReturning() throws Exception {
239     Invokable<?, Object> delegate = Prepender.method("prepend", Iterable.class);
240     try {
241       delegate.returning(new TypeToken<Iterable<Integer>>() {});
242       fail();
243     } catch (IllegalArgumentException expected) {
244     }
245   }
246 
testPrivateInstanceMethod_isOverridable()247   public void testPrivateInstanceMethod_isOverridable() throws Exception {
248     Invokable<?, ?> delegate = Prepender.method("privateMethod");
249     assertTrue(delegate.isPrivate());
250     assertFalse(delegate.isOverridable());
251     assertFalse(delegate.isVarArgs());
252   }
253 
testPrivateFinalInstanceMethod_isOverridable()254   public void testPrivateFinalInstanceMethod_isOverridable() throws Exception {
255     Invokable<?, ?> delegate = Prepender.method("privateFinalMethod");
256     assertTrue(delegate.isPrivate());
257     assertTrue(delegate.isFinal());
258     assertFalse(delegate.isOverridable());
259     assertFalse(delegate.isVarArgs());
260   }
261 
testStaticMethod_isOverridable()262   public void testStaticMethod_isOverridable() throws Exception {
263     Invokable<?, ?> delegate = Prepender.method("staticMethod");
264     assertTrue(delegate.isStatic());
265     assertFalse(delegate.isOverridable());
266     assertFalse(delegate.isVarArgs());
267   }
268 
testStaticFinalMethod_isFinal()269   public void testStaticFinalMethod_isFinal() throws Exception {
270     Invokable<?, ?> delegate = Prepender.method("staticFinalMethod");
271     assertTrue(delegate.isStatic());
272     assertTrue(delegate.isFinal());
273     assertFalse(delegate.isOverridable());
274     assertFalse(delegate.isVarArgs());
275   }
276 
277   static class Foo {}
278 
testConstructor_isOverridablel()279   public void testConstructor_isOverridablel() throws Exception {
280     Invokable<?, ?> delegate = Invokable.from(Foo.class.getDeclaredConstructor());
281     assertFalse(delegate.isOverridable());
282     assertFalse(delegate.isVarArgs());
283   }
284 
testMethod_isVarArgs()285   public void testMethod_isVarArgs() throws Exception {
286     Invokable<?, ?> delegate = Prepender.method("privateVarArgsMethod", String[].class);
287     assertTrue(delegate.isVarArgs());
288   }
289 
testConstructor_isVarArgs()290   public void testConstructor_isVarArgs() throws Exception {
291     Invokable<?, ?> delegate = Prepender.constructor(String[].class);
292     assertTrue(delegate.isVarArgs());
293   }
294 
testGetOwnerType_constructor()295   public void testGetOwnerType_constructor() throws Exception {
296     Invokable<String, String> invokable = Invokable.from(String.class.getConstructor());
297     assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
298   }
299 
testGetOwnerType_method()300   public void testGetOwnerType_method() throws Exception {
301     Invokable<?, ?> invokable = Invokable.from(String.class.getMethod("length"));
302     assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
303   }
304 
305   private static final class FinalClass {
306     @SuppressWarnings("unused") // used by reflection
notFinalMethod()307     void notFinalMethod() {}
308   }
309 
testNonFinalMethodInFinalClass_isOverridable()310   public void testNonFinalMethodInFinalClass_isOverridable() throws Exception {
311     Invokable<?, ?> delegate = Invokable.from(FinalClass.class.getDeclaredMethod("notFinalMethod"));
312     assertFalse(delegate.isOverridable());
313     assertFalse(delegate.isVarArgs());
314   }
315 
316   private class InnerWithDefaultConstructor {
317     class NestedInner {}
318   }
319 
testInnerClassDefaultConstructor()320   public void testInnerClassDefaultConstructor() {
321     Constructor<?> constructor = InnerWithDefaultConstructor.class.getDeclaredConstructors()[0];
322     assertEquals(0, Invokable.from(constructor).getParameters().size());
323   }
324 
testNestedInnerClassDefaultConstructor()325   public void testNestedInnerClassDefaultConstructor() {
326     Constructor<?> constructor =
327         InnerWithDefaultConstructor.NestedInner.class.getDeclaredConstructors()[0];
328     assertEquals(0, Invokable.from(constructor).getParameters().size());
329   }
330 
331   private class InnerWithOneParameterConstructor {
332     @SuppressWarnings("unused") // called by reflection
InnerWithOneParameterConstructor(String s)333     public InnerWithOneParameterConstructor(String s) {}
334   }
335 
testInnerClassWithOneParameterConstructor()336   public void testInnerClassWithOneParameterConstructor() {
337     Constructor<?> constructor =
338         InnerWithOneParameterConstructor.class.getDeclaredConstructors()[0];
339     Invokable<?, ?> invokable = Invokable.from(constructor);
340     assertEquals(1, invokable.getParameters().size());
341     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
342   }
343 
344   private class InnerWithAnnotatedConstructorParameter {
345     @SuppressWarnings("unused") // called by reflection
InnerWithAnnotatedConstructorParameter(@ullableDecl String s)346     InnerWithAnnotatedConstructorParameter(@NullableDecl String s) {}
347   }
348 
testInnerClassWithAnnotatedConstructorParameter()349   public void testInnerClassWithAnnotatedConstructorParameter() {
350     Constructor<?> constructor =
351         InnerWithAnnotatedConstructorParameter.class.getDeclaredConstructors()[0];
352     Invokable<?, ?> invokable = Invokable.from(constructor);
353     assertEquals(1, invokable.getParameters().size());
354     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
355   }
356 
357   private class InnerWithGenericConstructorParameter {
358     @SuppressWarnings("unused") // called by reflection
InnerWithGenericConstructorParameter(Iterable<String> it, String s)359     InnerWithGenericConstructorParameter(Iterable<String> it, String s) {}
360   }
361 
testInnerClassWithGenericConstructorParameter()362   public void testInnerClassWithGenericConstructorParameter() {
363     Constructor<?> constructor =
364         InnerWithGenericConstructorParameter.class.getDeclaredConstructors()[0];
365     Invokable<?, ?> invokable = Invokable.from(constructor);
366     assertEquals(2, invokable.getParameters().size());
367     assertEquals(new TypeToken<Iterable<String>>() {}, invokable.getParameters().get(0).getType());
368     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(1).getType());
369   }
370 
testAnonymousClassDefaultConstructor()371   public void testAnonymousClassDefaultConstructor() {
372     final int i = 1;
373     final String s = "hello world";
374     Class<?> anonymous =
375         new Runnable() {
376           @Override
377           public void run() {
378             System.out.println(s + i);
379           }
380         }.getClass();
381     Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
382     assertEquals(0, Invokable.from(constructor).getParameters().size());
383   }
384 
testAnonymousClassWithTwoParametersConstructor()385   public void testAnonymousClassWithTwoParametersConstructor() {
386     abstract class Base {
387       @SuppressWarnings("unused") // called by reflection
388       Base(String s, int i) {}
389     }
390     Class<?> anonymous = new Base("test", 0) {}.getClass();
391     Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
392     assertEquals(2, Invokable.from(constructor).getParameters().size());
393   }
394 
testLocalClassDefaultConstructor()395   public void testLocalClassDefaultConstructor() {
396     final int i = 1;
397     final String s = "hello world";
398     class LocalWithDefaultConstructor implements Runnable {
399       @Override
400       public void run() {
401         System.out.println(s + i);
402       }
403     }
404     Constructor<?> constructor = LocalWithDefaultConstructor.class.getDeclaredConstructors()[0];
405     assertEquals(0, Invokable.from(constructor).getParameters().size());
406   }
407 
testStaticAnonymousClassDefaultConstructor()408   public void testStaticAnonymousClassDefaultConstructor() throws Exception {
409     doTestStaticAnonymousClassDefaultConstructor();
410   }
411 
doTestStaticAnonymousClassDefaultConstructor()412   private static void doTestStaticAnonymousClassDefaultConstructor() {
413     final int i = 1;
414     final String s = "hello world";
415     Class<?> anonymous =
416         new Runnable() {
417           @Override
418           public void run() {
419             System.out.println(s + i);
420           }
421         }.getClass();
422     Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
423     assertEquals(0, Invokable.from(constructor).getParameters().size());
424   }
425 
testAnonymousClassInConstructor()426   public void testAnonymousClassInConstructor() {
427     new AnonymousClassInConstructor();
428   }
429 
430   private static class AnonymousClassInConstructor {
AnonymousClassInConstructor()431     AnonymousClassInConstructor() {
432       final int i = 1;
433       final String s = "hello world";
434       Class<?> anonymous =
435           new Runnable() {
436             @Override
437             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(@NullableDecl 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>>() {}, invokable.getParameters().get(0).getType());
528     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(1).getType());
529   }
530 
testEquals()531   public void testEquals() throws Exception {
532     new EqualsTester()
533         .addEqualityGroup(Prepender.constructor(), Prepender.constructor())
534         .addEqualityGroup(Prepender.constructor(String.class, int.class))
535         .addEqualityGroup(Prepender.method("privateMethod"), Prepender.method("privateMethod"))
536         .addEqualityGroup(Prepender.method("privateFinalMethod"))
537         .testEquals();
538   }
539 
testNulls()540   public void testNulls() {
541     new NullPointerTester().testAllPublicStaticMethods(Invokable.class);
542     new NullPointerTester().testAllPublicInstanceMethods(Prepender.method("staticMethod"));
543   }
544 
545   @Retention(RetentionPolicy.RUNTIME)
546   private @interface NotBlank {}
547 
548   /** Class for testing constructor, static method and instance method. */
549   @SuppressWarnings("unused") // most are called by reflection
550   private static class Prepender {
551 
552     private final String prefix;
553     private final int times;
554 
Prepender(@otBlank String prefix, int times)555     Prepender(@NotBlank String prefix, int times) throws NullPointerException {
556       this.prefix = prefix;
557       this.times = times;
558     }
559 
Prepender(String... varargs)560     Prepender(String... varargs) {
561       this(null, 0);
562     }
563 
564     // just for testing
Prepender()565     private <A> Prepender() {
566       this(null, 0);
567     }
568 
prepend(@otBlank String first, Iterable<String> tail)569     static <T> Iterable<String> prepend(@NotBlank String first, Iterable<String> tail) {
570       return Iterables.concat(ImmutableList.of(first), tail);
571     }
572 
prepend(Iterable<String> tail)573     Iterable<String> prepend(Iterable<String> tail)
574         throws IllegalArgumentException, NullPointerException {
575       return Iterables.concat(Collections.nCopies(times, prefix), tail);
576     }
577 
constructor(Class<?>.... parameterTypes)578     static Invokable<?, Prepender> constructor(Class<?>... parameterTypes) throws Exception {
579       Constructor<Prepender> constructor = Prepender.class.getDeclaredConstructor(parameterTypes);
580       return Invokable.from(constructor);
581     }
582 
method(String name, Class<?>... parameterTypes)583     static Invokable<Prepender, Object> method(String name, Class<?>... parameterTypes) {
584       try {
585         Method method = Prepender.class.getDeclaredMethod(name, parameterTypes);
586         @SuppressWarnings("unchecked") // The method is from Prepender.
587         Invokable<Prepender, Object> invokable =
588             (Invokable<Prepender, Object>) Invokable.from(method);
589         return invokable;
590       } catch (NoSuchMethodException e) {
591         throw new IllegalArgumentException(e);
592       }
593     }
594 
privateMethod()595     private void privateMethod() {}
596 
privateFinalMethod()597     private final void privateFinalMethod() {}
598 
staticMethod()599     static void staticMethod() {}
600 
staticFinalMethod()601     static final void staticFinalMethod() {}
602 
privateVarArgsMethod(String... varargs)603     private void privateVarArgsMethod(String... varargs) {}
604   }
605 
606   private static class SubPrepender extends Prepender {
607     @SuppressWarnings("unused") // needed to satisfy compiler, never called
SubPrepender()608     public SubPrepender() throws NullPointerException {
609       throw new AssertionError();
610     }
611   }
612 }
613