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