• 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.ImmutableSet;
23 import com.google.common.collect.Iterables;
24 import com.google.common.testing.EqualsTester;
25 import com.google.common.testing.NullPointerTester;
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.lang.reflect.AccessibleObject;
29 import java.lang.reflect.Constructor;
30 import java.lang.reflect.Method;
31 import java.lang.reflect.Modifier;
32 import java.lang.reflect.ParameterizedType;
33 import java.lang.reflect.TypeVariable;
34 import java.util.Collections;
35 import javax.annotation.CheckForNull;
36 import junit.framework.TestCase;
37 
38 /**
39  * Unit tests for {@link Invokable}.
40  *
41  * @author Ben Yu
42  */
43 @AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations?
44 public class InvokableTest extends TestCase {
45   // Historically Invokable inherited from java.lang.reflect.AccessibleObject. That's no longer the
46   // case, but we do check that its API still has the same public methods. We exclude some methods
47   // that were added in Java 9 and that people probably weren't calling via Invokable, namely
48   // `boolean canAccess(Object)`.
testApiCompatibleWithAccessibleObject()49   public void testApiCompatibleWithAccessibleObject() {
50     ImmutableSet<String> invokableMethods =
51         publicMethodSignatures(Invokable.class, ImmutableSet.<String>of());
52     ImmutableSet<String> accesibleObjectMethods =
53         publicMethodSignatures(AccessibleObject.class, ImmutableSet.of("canAccess"));
54     assertThat(invokableMethods).containsAtLeastElementsIn(accesibleObjectMethods);
55     Class<?> genericDeclaration;
56     try {
57       genericDeclaration = Class.forName("java.lang.reflect.GenericDeclaration");
58       ImmutableSet<String> genericDeclarationMethods =
59           publicMethodSignatures(genericDeclaration, ImmutableSet.<String>of());
60       assertThat(invokableMethods).containsAtLeastElementsIn(genericDeclarationMethods);
61     } catch (ClassNotFoundException e) {
62       // OK: we're on Java 7, which doesn't have this class
63     }
64   }
65 
publicMethodSignatures( Class<?> c, ImmutableSet<String> ignore)66   private static ImmutableSet<String> publicMethodSignatures(
67       Class<?> c, ImmutableSet<String> ignore) {
68     ImmutableSet.Builder<String> methods = ImmutableSet.builder();
69     for (Method method : c.getMethods()) {
70       if (Modifier.isStatic(method.getModifiers()) || ignore.contains(method.getName())) {
71         continue;
72       }
73       StringBuilder signature =
74           new StringBuilder()
75               .append(typeName(method.getReturnType()))
76               .append(" ")
77               .append(method.getName())
78               .append("(");
79       String sep = "";
80       for (Class<?> param : method.getParameterTypes()) {
81         signature.append(sep).append(typeName(param));
82         sep = ", ";
83       }
84       methods.add(signature.append(")").toString());
85     }
86     return methods.build();
87   }
88 
typeName(Class<?> type)89   private static String typeName(Class<?> type) {
90     return type.isArray() ? typeName(type.getComponentType()) + "[]" : type.getName();
91   }
92 
testConstructor()93   public void testConstructor() throws Exception {
94     Invokable<A, A> invokable = A.constructor();
95     assertTrue(invokable.isPublic());
96     assertFalse(invokable.isPackagePrivate());
97     assertFalse(invokable.isAbstract());
98     assertFalse(invokable.isStatic());
99     assertTrue(invokable.isAnnotationPresent(Tested.class));
100   }
101 
testAbstractMethod()102   public void testAbstractMethod() throws Exception {
103     Invokable<?, Object> invokable = A.method("abstractMethod");
104     assertTrue(invokable.isPackagePrivate());
105     assertTrue(invokable.isAbstract());
106     assertFalse(invokable.isFinal());
107     assertTrue(invokable.isAnnotationPresent(Tested.class));
108   }
109 
testOverridableMethod()110   public void testOverridableMethod() throws Exception {
111     Invokable<?, Object> invokable = A.method("overridableMethod");
112     assertTrue(invokable.isPackagePrivate());
113     assertFalse(invokable.isAbstract());
114     assertFalse(invokable.isFinal());
115     assertTrue(invokable.isAnnotationPresent(Tested.class));
116   }
117 
testPrivateMethod()118   public void testPrivateMethod() throws Exception {
119     Invokable<?, Object> invokable = A.method("privateMethod");
120     assertFalse(invokable.isAbstract());
121     assertTrue(invokable.isPrivate());
122     assertFalse(invokable.isPackagePrivate());
123     assertFalse(invokable.isPublic());
124     assertFalse(invokable.isProtected());
125     assertTrue(invokable.isAnnotationPresent(Tested.class));
126   }
127 
testProtectedMethod()128   public void testProtectedMethod() throws Exception {
129     Invokable<?, Object> invokable = A.method("protectedMethod");
130     assertFalse(invokable.isAbstract());
131     assertFalse(invokable.isPrivate());
132     assertFalse(invokable.isPackagePrivate());
133     assertFalse(invokable.isFinal());
134     assertFalse(invokable.isPublic());
135     assertTrue(invokable.isProtected());
136     assertTrue(invokable.isAnnotationPresent(Tested.class));
137   }
138 
testFinalMethod()139   public void testFinalMethod() throws Exception {
140     Invokable<?, Object> invokable = A.method("publicFinalMethod");
141     assertFalse(invokable.isAbstract());
142     assertFalse(invokable.isPrivate());
143     assertTrue(invokable.isFinal());
144     assertTrue(invokable.isPublic());
145     assertTrue(invokable.isAnnotationPresent(Tested.class));
146   }
147 
testNativeMethod()148   public void testNativeMethod() throws Exception {
149     Invokable<?, Object> invokable = A.method("nativeMethod");
150     assertTrue(invokable.isNative());
151     assertTrue(invokable.isPackagePrivate());
152   }
153 
testSynchronizedMethod()154   public void testSynchronizedMethod() throws Exception {
155     Invokable<?, Object> invokable = A.method("synchronizedMethod");
156     assertTrue(invokable.isSynchronized());
157   }
158 
testUnannotatedMethod()159   public void testUnannotatedMethod() throws Exception {
160     Invokable<?, Object> invokable = A.method("notAnnotatedMethod");
161     assertFalse(invokable.isAnnotationPresent(Tested.class));
162   }
163 
164   @Retention(RetentionPolicy.RUNTIME)
165   private @interface Tested {}
166 
167   private abstract static class A {
168     @Tested private boolean privateField;
169     @Tested int packagePrivateField;
170     @Tested protected int protectedField;
171     @Tested public String publicField;
172     @Tested private static Iterable<String> staticField;
173     @Tested private final Object finalField;
174     private volatile char volatileField;
175     private transient long transientField;
176 
177     @Tested
A(Object finalField)178     public A(Object finalField) {
179       this.finalField = finalField;
180     }
181 
182     @Tested
abstractMethod()183     abstract void abstractMethod();
184 
185     @Tested
overridableMethod()186     void overridableMethod() {}
187 
188     @Tested
protectedMethod()189     protected void protectedMethod() {}
190 
191     @Tested
privateMethod()192     private void privateMethod() {}
193 
194     @Tested
publicFinalMethod()195     public final void publicFinalMethod() {}
196 
notAnnotatedMethod()197     void notAnnotatedMethod() {}
198 
constructor()199     static Invokable<A, A> constructor() throws Exception {
200       Constructor<A> constructor = A.class.getDeclaredConstructor(Object.class);
201       Invokable<A, A> invokable = Invokable.from(constructor);
202       assertEquals(constructor.getName(), invokable.getName());
203       assertEquals(A.class, invokable.getDeclaringClass());
204       return invokable;
205     }
206 
method(String name, Class<?>... parameterTypes)207     static Invokable<?, Object> method(String name, Class<?>... parameterTypes) throws Exception {
208       Invokable<?, Object> invokable =
209           Invokable.from(A.class.getDeclaredMethod(name, parameterTypes));
210       assertEquals(name, invokable.getName());
211       assertEquals(A.class, invokable.getDeclaringClass());
212       return invokable;
213     }
214 
nativeMethod()215     native void nativeMethod();
216 
synchronizedMethod()217     synchronized void synchronizedMethod() {}
218   }
219 
testConstructor_returnType()220   public void testConstructor_returnType() throws Exception {
221     assertEquals(Prepender.class, Prepender.constructor().getReturnType().getType());
222   }
223 
224   private static class WithConstructorAndTypeParameter<T> {
225     @SuppressWarnings("unused") // by reflection
WithConstructorAndTypeParameter()226     <X> WithConstructorAndTypeParameter() {}
227   }
228 
testConstructor_returnType_hasTypeParameter()229   public void testConstructor_returnType_hasTypeParameter() throws Exception {
230     @SuppressWarnings("rawtypes") // Foo.class for Foo<T> is always raw type
231     Class<WithConstructorAndTypeParameter> type = WithConstructorAndTypeParameter.class;
232     @SuppressWarnings("rawtypes") // Foo.class
233     Constructor<WithConstructorAndTypeParameter> constructor = type.getDeclaredConstructor();
234     Invokable<?, ?> factory = Invokable.from(constructor);
235     assertThat(factory.getTypeParameters()).hasLength(2);
236     assertEquals(type.getTypeParameters()[0], factory.getTypeParameters()[0]);
237     assertEquals(constructor.getTypeParameters()[0], factory.getTypeParameters()[1]);
238     ParameterizedType returnType = (ParameterizedType) factory.getReturnType().getType();
239     assertEquals(type, returnType.getRawType());
240     assertEquals(
241         ImmutableList.copyOf(type.getTypeParameters()),
242         ImmutableList.copyOf(returnType.getActualTypeArguments()));
243   }
244 
testConstructor_exceptionTypes()245   public void testConstructor_exceptionTypes() throws Exception {
246     assertEquals(
247         ImmutableList.of(TypeToken.of(NullPointerException.class)),
248         Prepender.constructor(String.class, int.class).getExceptionTypes());
249   }
250 
testConstructor_typeParameters()251   public void testConstructor_typeParameters() throws Exception {
252     TypeVariable<?>[] variables = Prepender.constructor().getTypeParameters();
253     assertThat(variables).hasLength(1);
254     assertEquals("T", variables[0].getName());
255   }
256 
testConstructor_parameters()257   public void testConstructor_parameters() throws Exception {
258     Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
259     ImmutableList<Parameter> parameters = delegate.getParameters();
260     assertEquals(2, parameters.size());
261     assertEquals(String.class, parameters.get(0).getType().getType());
262     assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
263     assertEquals(int.class, parameters.get(1).getType().getType());
264     assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
265     new EqualsTester()
266         .addEqualityGroup(parameters.get(0))
267         .addEqualityGroup(parameters.get(1))
268         .testEquals();
269   }
270 
testConstructor_call()271   public void testConstructor_call() throws Exception {
272     Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
273     Prepender prepender = delegate.invoke(null, "a", 1);
274     assertEquals("a", prepender.prefix);
275     assertEquals(1, prepender.times);
276   }
277 
testConstructor_returning()278   public void testConstructor_returning() throws Exception {
279     Invokable<?, Prepender> delegate =
280         Prepender.constructor(String.class, int.class).returning(Prepender.class);
281     Prepender prepender = delegate.invoke(null, "a", 1);
282     assertEquals("a", prepender.prefix);
283     assertEquals(1, prepender.times);
284   }
285 
testConstructor_invalidReturning()286   public void testConstructor_invalidReturning() throws Exception {
287     Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
288     try {
289       delegate.returning(SubPrepender.class);
290       fail();
291     } catch (IllegalArgumentException expected) {
292     }
293   }
294 
testStaticMethod_returnType()295   public void testStaticMethod_returnType() throws Exception {
296     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
297     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
298   }
299 
testStaticMethod_exceptionTypes()300   public void testStaticMethod_exceptionTypes() throws Exception {
301     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
302     assertEquals(ImmutableList.of(), delegate.getExceptionTypes());
303   }
304 
testStaticMethod_typeParameters()305   public void testStaticMethod_typeParameters() throws Exception {
306     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
307     TypeVariable<?>[] variables = delegate.getTypeParameters();
308     assertThat(variables).hasLength(1);
309     assertEquals("T", variables[0].getName());
310   }
311 
testStaticMethod_parameters()312   public void testStaticMethod_parameters() throws Exception {
313     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
314     ImmutableList<Parameter> parameters = delegate.getParameters();
315     assertEquals(2, parameters.size());
316     assertEquals(String.class, parameters.get(0).getType().getType());
317     assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
318     assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(1).getType());
319     assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
320     new EqualsTester()
321         .addEqualityGroup(parameters.get(0))
322         .addEqualityGroup(parameters.get(1))
323         .testEquals();
324   }
325 
testStaticMethod_call()326   public void testStaticMethod_call() throws Exception {
327     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
328     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
329     Iterable<String> result =
330         (Iterable<String>) delegate.invoke(null, "a", ImmutableList.of("b", "c"));
331     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
332   }
333 
testStaticMethod_returning()334   public void testStaticMethod_returning() throws Exception {
335     Invokable<?, Iterable<String>> delegate =
336         Prepender.method("prepend", String.class, Iterable.class)
337             .returning(new TypeToken<Iterable<String>>() {});
338     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
339     Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
340     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
341   }
342 
testStaticMethod_returningRawType()343   public void testStaticMethod_returningRawType() throws Exception {
344     @SuppressWarnings("rawtypes") // the purpose is to test raw type
345     Invokable<?, Iterable> delegate =
346         Prepender.method("prepend", String.class, Iterable.class).returning(Iterable.class);
347     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
348     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
349     Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
350     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
351   }
352 
testStaticMethod_invalidReturning()353   public void testStaticMethod_invalidReturning() throws Exception {
354     Invokable<?, Object> delegate = Prepender.method("prepend", String.class, Iterable.class);
355     try {
356       delegate.returning(new TypeToken<Iterable<Integer>>() {});
357       fail();
358     } catch (IllegalArgumentException expected) {
359     }
360   }
361 
testInstanceMethod_returnType()362   public void testInstanceMethod_returnType() throws Exception {
363     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
364     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
365   }
366 
testInstanceMethod_exceptionTypes()367   public void testInstanceMethod_exceptionTypes() throws Exception {
368     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
369     assertEquals(
370         ImmutableList.of(
371             TypeToken.of(IllegalArgumentException.class), TypeToken.of(NullPointerException.class)),
372         delegate.getExceptionTypes());
373   }
374 
testInstanceMethod_typeParameters()375   public void testInstanceMethod_typeParameters() throws Exception {
376     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
377     assertThat(delegate.getTypeParameters()).isEmpty();
378   }
379 
testInstanceMethod_parameters()380   public void testInstanceMethod_parameters() throws Exception {
381     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
382     ImmutableList<Parameter> parameters = delegate.getParameters();
383     assertEquals(1, parameters.size());
384     assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(0).getType());
385     assertThat(parameters.get(0).getAnnotations()).isEmpty();
386     new EqualsTester().addEqualityGroup(parameters.get(0)).testEquals();
387   }
388 
testInstanceMethod_call()389   public void testInstanceMethod_call() throws Exception {
390     Invokable<Prepender, ?> delegate = Prepender.method("prepend", Iterable.class);
391     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
392     Iterable<String> result =
393         (Iterable<String>) delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
394     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
395   }
396 
testInstanceMethod_returning()397   public void testInstanceMethod_returning() throws Exception {
398     Invokable<Prepender, Iterable<String>> delegate =
399         Prepender.method("prepend", Iterable.class).returning(new TypeToken<Iterable<String>>() {});
400     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
401     Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
402     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
403   }
404 
testInstanceMethod_returningRawType()405   public void testInstanceMethod_returningRawType() throws Exception {
406     @SuppressWarnings("rawtypes") // the purpose is to test raw type
407     Invokable<Prepender, Iterable> delegate =
408         Prepender.method("prepend", Iterable.class).returning(Iterable.class);
409     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
410     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
411     Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
412     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
413   }
414 
testInstanceMethod_invalidReturning()415   public void testInstanceMethod_invalidReturning() throws Exception {
416     Invokable<?, Object> delegate = Prepender.method("prepend", Iterable.class);
417     try {
418       delegate.returning(new TypeToken<Iterable<Integer>>() {});
419       fail();
420     } catch (IllegalArgumentException expected) {
421     }
422   }
423 
testPrivateInstanceMethod_isOverridable()424   public void testPrivateInstanceMethod_isOverridable() throws Exception {
425     Invokable<?, ?> delegate = Prepender.method("privateMethod");
426     assertTrue(delegate.isPrivate());
427     assertFalse(delegate.isOverridable());
428     assertFalse(delegate.isVarArgs());
429   }
430 
testPrivateFinalInstanceMethod_isOverridable()431   public void testPrivateFinalInstanceMethod_isOverridable() throws Exception {
432     Invokable<?, ?> delegate = Prepender.method("privateFinalMethod");
433     assertTrue(delegate.isPrivate());
434     assertTrue(delegate.isFinal());
435     assertFalse(delegate.isOverridable());
436     assertFalse(delegate.isVarArgs());
437   }
438 
testStaticMethod_isOverridable()439   public void testStaticMethod_isOverridable() throws Exception {
440     Invokable<?, ?> delegate = Prepender.method("staticMethod");
441     assertTrue(delegate.isStatic());
442     assertFalse(delegate.isOverridable());
443     assertFalse(delegate.isVarArgs());
444   }
445 
testStaticFinalMethod_isFinal()446   public void testStaticFinalMethod_isFinal() throws Exception {
447     Invokable<?, ?> delegate = Prepender.method("staticFinalMethod");
448     assertTrue(delegate.isStatic());
449     assertTrue(delegate.isFinal());
450     assertFalse(delegate.isOverridable());
451     assertFalse(delegate.isVarArgs());
452   }
453 
454   static class Foo {}
455 
testConstructor_isOverridablel()456   public void testConstructor_isOverridablel() throws Exception {
457     Invokable<?, ?> delegate = Invokable.from(Foo.class.getDeclaredConstructor());
458     assertFalse(delegate.isOverridable());
459     assertFalse(delegate.isVarArgs());
460   }
461 
testMethod_isVarArgs()462   public void testMethod_isVarArgs() throws Exception {
463     Invokable<?, ?> delegate = Prepender.method("privateVarArgsMethod", String[].class);
464     assertTrue(delegate.isVarArgs());
465   }
466 
testConstructor_isVarArgs()467   public void testConstructor_isVarArgs() throws Exception {
468     Invokable<?, ?> delegate = Prepender.constructor(String[].class);
469     assertTrue(delegate.isVarArgs());
470   }
471 
testGetOwnerType_constructor()472   public void testGetOwnerType_constructor() throws Exception {
473     Invokable<String, String> invokable = Invokable.from(String.class.getConstructor());
474     assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
475   }
476 
testGetOwnerType_method()477   public void testGetOwnerType_method() throws Exception {
478     Invokable<?, ?> invokable = Invokable.from(String.class.getMethod("length"));
479     assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
480   }
481 
482   private static final class FinalClass {
483     @SuppressWarnings("unused") // used by reflection
notFinalMethod()484     void notFinalMethod() {}
485   }
486 
testNonFinalMethodInFinalClass_isOverridable()487   public void testNonFinalMethodInFinalClass_isOverridable() throws Exception {
488     Invokable<?, ?> delegate = Invokable.from(FinalClass.class.getDeclaredMethod("notFinalMethod"));
489     assertFalse(delegate.isOverridable());
490     assertFalse(delegate.isVarArgs());
491   }
492 
493   private class InnerWithDefaultConstructor {
494     class NestedInner {}
495   }
496 
testInnerClassDefaultConstructor()497   public void testInnerClassDefaultConstructor() {
498     Constructor<?> constructor = InnerWithDefaultConstructor.class.getDeclaredConstructors()[0];
499     assertEquals(0, Invokable.from(constructor).getParameters().size());
500   }
501 
testNestedInnerClassDefaultConstructor()502   public void testNestedInnerClassDefaultConstructor() {
503     Constructor<?> constructor =
504         InnerWithDefaultConstructor.NestedInner.class.getDeclaredConstructors()[0];
505     assertEquals(0, Invokable.from(constructor).getParameters().size());
506   }
507 
508   private class InnerWithOneParameterConstructor {
509     @SuppressWarnings("unused") // called by reflection
InnerWithOneParameterConstructor(String s)510     public InnerWithOneParameterConstructor(String s) {}
511   }
512 
testInnerClassWithOneParameterConstructor()513   public void testInnerClassWithOneParameterConstructor() {
514     Constructor<?> constructor =
515         InnerWithOneParameterConstructor.class.getDeclaredConstructors()[0];
516     Invokable<?, ?> invokable = Invokable.from(constructor);
517     assertEquals(1, invokable.getParameters().size());
518     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
519   }
520 
521   private class InnerWithAnnotatedConstructorParameter {
522     @SuppressWarnings("unused") // called by reflection
InnerWithAnnotatedConstructorParameter(@heckForNull String s)523     InnerWithAnnotatedConstructorParameter(@CheckForNull String s) {}
524   }
525 
testInnerClassWithAnnotatedConstructorParameter()526   public void testInnerClassWithAnnotatedConstructorParameter() {
527     Constructor<?> constructor =
528         InnerWithAnnotatedConstructorParameter.class.getDeclaredConstructors()[0];
529     Invokable<?, ?> invokable = Invokable.from(constructor);
530     assertEquals(1, invokable.getParameters().size());
531     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
532   }
533 
534   private class InnerWithGenericConstructorParameter {
535     @SuppressWarnings("unused") // called by reflection
InnerWithGenericConstructorParameter(Iterable<String> it, String s)536     InnerWithGenericConstructorParameter(Iterable<String> it, String s) {}
537   }
538 
testInnerClassWithGenericConstructorParameter()539   public void testInnerClassWithGenericConstructorParameter() {
540     Constructor<?> constructor =
541         InnerWithGenericConstructorParameter.class.getDeclaredConstructors()[0];
542     Invokable<?, ?> invokable = Invokable.from(constructor);
543     assertEquals(2, invokable.getParameters().size());
544     assertEquals(new TypeToken<Iterable<String>>() {}, invokable.getParameters().get(0).getType());
545     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(1).getType());
546   }
547 
testAnonymousClassDefaultConstructor()548   public void testAnonymousClassDefaultConstructor() {
549     final int i = 1;
550     final String s = "hello world";
551     Class<?> anonymous =
552         new Runnable() {
553           @Override
554           public void run() {
555             System.out.println(s + i);
556           }
557         }.getClass();
558     Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
559     assertEquals(0, Invokable.from(constructor).getParameters().size());
560   }
561 
testAnonymousClassWithTwoParametersConstructor()562   public void testAnonymousClassWithTwoParametersConstructor() {
563     abstract class Base {
564       @SuppressWarnings("unused") // called by reflection
565       Base(String s, int i) {}
566     }
567     Class<?> anonymous = new Base("test", 0) {}.getClass();
568     Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
569     assertEquals(2, Invokable.from(constructor).getParameters().size());
570   }
571 
testLocalClassDefaultConstructor()572   public void testLocalClassDefaultConstructor() {
573     final int i = 1;
574     final String s = "hello world";
575     class LocalWithDefaultConstructor implements Runnable {
576       @Override
577       public void run() {
578         System.out.println(s + i);
579       }
580     }
581     Constructor<?> constructor = LocalWithDefaultConstructor.class.getDeclaredConstructors()[0];
582     assertEquals(0, Invokable.from(constructor).getParameters().size());
583   }
584 
testStaticAnonymousClassDefaultConstructor()585   public void testStaticAnonymousClassDefaultConstructor() throws Exception {
586     doTestStaticAnonymousClassDefaultConstructor();
587   }
588 
doTestStaticAnonymousClassDefaultConstructor()589   private static void doTestStaticAnonymousClassDefaultConstructor() {
590     final int i = 1;
591     final String s = "hello world";
592     Class<?> anonymous =
593         new Runnable() {
594           @Override
595           public void run() {
596             System.out.println(s + i);
597           }
598         }.getClass();
599     Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
600     assertEquals(0, Invokable.from(constructor).getParameters().size());
601   }
602 
testAnonymousClassInConstructor()603   public void testAnonymousClassInConstructor() {
604     AnonymousClassInConstructor unused = new AnonymousClassInConstructor();
605   }
606 
607   private static class AnonymousClassInConstructor {
AnonymousClassInConstructor()608     AnonymousClassInConstructor() {
609       final int i = 1;
610       final String s = "hello world";
611       Class<?> anonymous =
612           new Runnable() {
613             @Override
614             public void run() {
615               System.out.println(s + i);
616             }
617           }.getClass();
618       Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
619       assertEquals(0, Invokable.from(constructor).getParameters().size());
620     }
621   }
622 
testLocalClassInInstanceInitializer()623   public void testLocalClassInInstanceInitializer() {
624     LocalClassInInstanceInitializer unused = new LocalClassInInstanceInitializer();
625   }
626 
627   private static class LocalClassInInstanceInitializer {
628     {
629       class Local {}
630       Constructor<?> constructor = Local.class.getDeclaredConstructors()[0];
631       assertEquals(0, Invokable.from(constructor).getParameters().size());
632     }
633   }
634 
testLocalClassInStaticInitializer()635   public void testLocalClassInStaticInitializer() {
636     LocalClassInStaticInitializer unused = new LocalClassInStaticInitializer();
637   }
638 
639   private static class LocalClassInStaticInitializer {
640     static {
641       class Local {}
642       Constructor<?> constructor = Local.class.getDeclaredConstructors()[0];
643       assertEquals(0, Invokable.from(constructor).getParameters().size());
644     }
645   }
646 
testLocalClassWithSeeminglyHiddenThisInStaticInitializer_BUG()647   public void testLocalClassWithSeeminglyHiddenThisInStaticInitializer_BUG() {
648     LocalClassWithSeeminglyHiddenThisInStaticInitializer unused =
649         new LocalClassWithSeeminglyHiddenThisInStaticInitializer();
650   }
651 
652   /**
653    * This class demonstrates a bug in getParameters() when the local class is inside static
654    * initializer.
655    */
656   private static class LocalClassWithSeeminglyHiddenThisInStaticInitializer {
657     static {
658       class Local {
659         @SuppressWarnings("unused") // through reflection
Local(LocalClassWithSeeminglyHiddenThisInStaticInitializer outer)660         Local(LocalClassWithSeeminglyHiddenThisInStaticInitializer outer) {}
661       }
662       Constructor<?> constructor = Local.class.getDeclaredConstructors()[0];
663       int miscalculated = 0;
assertEquals(miscalculated, Invokable.from(constructor).getParameters().size())664       assertEquals(miscalculated, Invokable.from(constructor).getParameters().size());
665     }
666   }
667 
testLocalClassWithOneParameterConstructor()668   public void testLocalClassWithOneParameterConstructor() throws Exception {
669     final int i = 1;
670     final String s = "hello world";
671     class LocalWithOneParameterConstructor {
672       @SuppressWarnings("unused") // called by reflection
673       public LocalWithOneParameterConstructor(String x) {
674         System.out.println(s + i);
675       }
676     }
677     Constructor<?> constructor =
678         LocalWithOneParameterConstructor.class.getDeclaredConstructors()[0];
679     Invokable<?, ?> invokable = Invokable.from(constructor);
680     assertEquals(1, invokable.getParameters().size());
681     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
682   }
683 
testLocalClassWithAnnotatedConstructorParameter()684   public void testLocalClassWithAnnotatedConstructorParameter() throws Exception {
685     class LocalWithAnnotatedConstructorParameter {
686       @SuppressWarnings("unused") // called by reflection
687       LocalWithAnnotatedConstructorParameter(@CheckForNull String s) {}
688     }
689     Constructor<?> constructor =
690         LocalWithAnnotatedConstructorParameter.class.getDeclaredConstructors()[0];
691     Invokable<?, ?> invokable = Invokable.from(constructor);
692     assertEquals(1, invokable.getParameters().size());
693     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
694   }
695 
testLocalClassWithGenericConstructorParameter()696   public void testLocalClassWithGenericConstructorParameter() throws Exception {
697     class LocalWithGenericConstructorParameter {
698       @SuppressWarnings("unused") // called by reflection
699       LocalWithGenericConstructorParameter(Iterable<String> it, String s) {}
700     }
701     Constructor<?> constructor =
702         LocalWithGenericConstructorParameter.class.getDeclaredConstructors()[0];
703     Invokable<?, ?> invokable = Invokable.from(constructor);
704     assertEquals(2, invokable.getParameters().size());
705     assertEquals(new TypeToken<Iterable<String>>() {}, invokable.getParameters().get(0).getType());
706     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(1).getType());
707   }
708 
testEquals()709   public void testEquals() throws Exception {
710     new EqualsTester()
711         .addEqualityGroup(A.constructor(), A.constructor())
712         .addEqualityGroup(A.method("privateMethod"), A.method("privateMethod"))
713         .addEqualityGroup(A.method("publicFinalMethod"))
714         .addEqualityGroup(Prepender.constructor(), Prepender.constructor())
715         .addEqualityGroup(Prepender.constructor(String.class, int.class))
716         .addEqualityGroup(Prepender.method("privateMethod"), Prepender.method("privateMethod"))
717         .addEqualityGroup(Prepender.method("privateFinalMethod"))
718         .testEquals();
719   }
720 
testNulls()721   public void testNulls() {
722     new NullPointerTester().testAllPublicStaticMethods(Invokable.class);
723     new NullPointerTester().testAllPublicInstanceMethods(Prepender.method("staticMethod"));
724   }
725 
726   @Retention(RetentionPolicy.RUNTIME)
727   private @interface NotBlank {}
728 
729   /** Class for testing constructor, static method and instance method. */
730   @SuppressWarnings("unused") // most are called by reflection
731   private static class Prepender {
732 
733     private final String prefix;
734     private final int times;
735 
Prepender(@otBlank String prefix, int times)736     Prepender(@NotBlank String prefix, int times) throws NullPointerException {
737       this.prefix = prefix;
738       this.times = times;
739     }
740 
Prepender(String... varargs)741     Prepender(String... varargs) {
742       this(null, 0);
743     }
744 
745     // just for testing
Prepender()746     private <T> Prepender() {
747       this(null, 0);
748     }
749 
prepend(@otBlank String first, Iterable<String> tail)750     static <T> Iterable<String> prepend(@NotBlank String first, Iterable<String> tail) {
751       return Iterables.concat(ImmutableList.of(first), tail);
752     }
753 
prepend(Iterable<String> tail)754     Iterable<String> prepend(Iterable<String> tail)
755         throws IllegalArgumentException, NullPointerException {
756       return Iterables.concat(Collections.nCopies(times, prefix), tail);
757     }
758 
constructor(Class<?>.... parameterTypes)759     static Invokable<?, Prepender> constructor(Class<?>... parameterTypes) throws Exception {
760       Constructor<Prepender> constructor = Prepender.class.getDeclaredConstructor(parameterTypes);
761       return Invokable.from(constructor);
762     }
763 
method(String name, Class<?>... parameterTypes)764     static Invokable<Prepender, Object> method(String name, Class<?>... parameterTypes) {
765       try {
766         Method method = Prepender.class.getDeclaredMethod(name, parameterTypes);
767         @SuppressWarnings("unchecked") // The method is from Prepender.
768         Invokable<Prepender, Object> invokable =
769             (Invokable<Prepender, Object>) Invokable.from(method);
770         return invokable;
771       } catch (NoSuchMethodException e) {
772         throw new IllegalArgumentException(e);
773       }
774     }
775 
privateMethod()776     private void privateMethod() {}
777 
privateFinalMethod()778     private final void privateFinalMethod() {}
779 
staticMethod()780     static void staticMethod() {}
781 
staticFinalMethod()782     static final void staticFinalMethod() {}
783 
privateVarArgsMethod(String... varargs)784     private void privateVarArgsMethod(String... varargs) {}
785   }
786 
787   private static class SubPrepender extends Prepender {
788     @SuppressWarnings("unused") // needed to satisfy compiler, never called
SubPrepender()789     public SubPrepender() throws NullPointerException {
790       throw new AssertionError();
791     }
792   }
793 }
794