• 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     @SuppressWarnings("rawtypes") // Foo.class for Foo<T> is always raw type
232     Class<WithConstructorAndTypeParameter> type = WithConstructorAndTypeParameter.class;
233     @SuppressWarnings("rawtypes") // Foo.class
234     Constructor<WithConstructorAndTypeParameter> constructor = type.getDeclaredConstructor();
235     Invokable<?, ?> factory = Invokable.from(constructor);
236     assertThat(factory.getTypeParameters()).hasLength(2);
237     assertEquals(type.getTypeParameters()[0], factory.getTypeParameters()[0]);
238     assertEquals(constructor.getTypeParameters()[0], factory.getTypeParameters()[1]);
239     ParameterizedType returnType = (ParameterizedType) factory.getReturnType().getType();
240     assertEquals(type, returnType.getRawType());
241     assertEquals(
242         ImmutableList.copyOf(type.getTypeParameters()),
243         ImmutableList.copyOf(returnType.getActualTypeArguments()));
244   }
245 
testConstructor_exceptionTypes()246   public void testConstructor_exceptionTypes() throws Exception {
247     assertEquals(
248         ImmutableList.of(TypeToken.of(NullPointerException.class)),
249         Prepender.constructor(String.class, int.class).getExceptionTypes());
250   }
251 
testConstructor_typeParameters()252   public void testConstructor_typeParameters() throws Exception {
253     TypeVariable<?>[] variables = Prepender.constructor().getTypeParameters();
254     assertThat(variables).hasLength(1);
255     assertEquals("T", variables[0].getName());
256   }
257 
testConstructor_parameters()258   public void testConstructor_parameters() throws Exception {
259     Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
260     ImmutableList<Parameter> parameters = delegate.getParameters();
261     assertEquals(2, parameters.size());
262     assertEquals(String.class, parameters.get(0).getType().getType());
263     assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
264     assertEquals(int.class, parameters.get(1).getType().getType());
265     assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
266     new EqualsTester()
267         .addEqualityGroup(parameters.get(0))
268         .addEqualityGroup(parameters.get(1))
269         .testEquals();
270   }
271 
testConstructor_call()272   public void testConstructor_call() throws Exception {
273     Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
274     Prepender prepender = delegate.invoke(null, "a", 1);
275     assertEquals("a", prepender.prefix);
276     assertEquals(1, prepender.times);
277   }
278 
testConstructor_returning()279   public void testConstructor_returning() throws Exception {
280     Invokable<?, Prepender> delegate =
281         Prepender.constructor(String.class, int.class).returning(Prepender.class);
282     Prepender prepender = delegate.invoke(null, "a", 1);
283     assertEquals("a", prepender.prefix);
284     assertEquals(1, prepender.times);
285   }
286 
testConstructor_invalidReturning()287   public void testConstructor_invalidReturning() throws Exception {
288     Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
289     assertThrows(IllegalArgumentException.class, () -> delegate.returning(SubPrepender.class));
290   }
291 
testStaticMethod_returnType()292   public void testStaticMethod_returnType() throws Exception {
293     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
294     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
295   }
296 
testStaticMethod_exceptionTypes()297   public void testStaticMethod_exceptionTypes() throws Exception {
298     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
299     assertEquals(ImmutableList.of(), delegate.getExceptionTypes());
300   }
301 
testStaticMethod_typeParameters()302   public void testStaticMethod_typeParameters() throws Exception {
303     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
304     TypeVariable<?>[] variables = delegate.getTypeParameters();
305     assertThat(variables).hasLength(1);
306     assertEquals("T", variables[0].getName());
307   }
308 
testStaticMethod_parameters()309   public void testStaticMethod_parameters() throws Exception {
310     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
311     ImmutableList<Parameter> parameters = delegate.getParameters();
312     assertEquals(2, parameters.size());
313     assertEquals(String.class, parameters.get(0).getType().getType());
314     assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
315     assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(1).getType());
316     assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
317     new EqualsTester()
318         .addEqualityGroup(parameters.get(0))
319         .addEqualityGroup(parameters.get(1))
320         .testEquals();
321   }
322 
testStaticMethod_call()323   public void testStaticMethod_call() throws Exception {
324     Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
325     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
326     Iterable<String> result =
327         (Iterable<String>) delegate.invoke(null, "a", ImmutableList.of("b", "c"));
328     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
329   }
330 
testStaticMethod_returning()331   public void testStaticMethod_returning() throws Exception {
332     Invokable<?, Iterable<String>> delegate =
333         Prepender.method("prepend", String.class, Iterable.class)
334             .returning(new TypeToken<Iterable<String>>() {});
335     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
336     Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
337     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
338   }
339 
testStaticMethod_returningRawType()340   public void testStaticMethod_returningRawType() throws Exception {
341     @SuppressWarnings("rawtypes") // the purpose is to test raw type
342     Invokable<?, Iterable> delegate =
343         Prepender.method("prepend", String.class, Iterable.class).returning(Iterable.class);
344     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
345     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
346     Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
347     assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
348   }
349 
testStaticMethod_invalidReturning()350   public void testStaticMethod_invalidReturning() throws Exception {
351     Invokable<?, Object> delegate = Prepender.method("prepend", String.class, Iterable.class);
352     assertThrows(
353         IllegalArgumentException.class,
354         () -> delegate.returning(new TypeToken<Iterable<Integer>>() {}));
355   }
356 
testInstanceMethod_returnType()357   public void testInstanceMethod_returnType() throws Exception {
358     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
359     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
360   }
361 
testInstanceMethod_exceptionTypes()362   public void testInstanceMethod_exceptionTypes() throws Exception {
363     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
364     assertEquals(
365         ImmutableList.of(
366             TypeToken.of(IllegalArgumentException.class), TypeToken.of(NullPointerException.class)),
367         delegate.getExceptionTypes());
368   }
369 
testInstanceMethod_typeParameters()370   public void testInstanceMethod_typeParameters() throws Exception {
371     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
372     assertThat(delegate.getTypeParameters()).isEmpty();
373   }
374 
testInstanceMethod_parameters()375   public void testInstanceMethod_parameters() throws Exception {
376     Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
377     ImmutableList<Parameter> parameters = delegate.getParameters();
378     assertEquals(1, parameters.size());
379     assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(0).getType());
380     assertThat(parameters.get(0).getAnnotations()).isEmpty();
381     new EqualsTester().addEqualityGroup(parameters.get(0)).testEquals();
382   }
383 
testInstanceMethod_call()384   public void testInstanceMethod_call() throws Exception {
385     Invokable<Prepender, ?> delegate = Prepender.method("prepend", Iterable.class);
386     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
387     Iterable<String> result =
388         (Iterable<String>) delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
389     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
390   }
391 
testInstanceMethod_returning()392   public void testInstanceMethod_returning() throws Exception {
393     Invokable<Prepender, Iterable<String>> delegate =
394         Prepender.method("prepend", Iterable.class).returning(new TypeToken<Iterable<String>>() {});
395     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
396     Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
397     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
398   }
399 
testInstanceMethod_returningRawType()400   public void testInstanceMethod_returningRawType() throws Exception {
401     @SuppressWarnings("rawtypes") // the purpose is to test raw type
402     Invokable<Prepender, Iterable> delegate =
403         Prepender.method("prepend", Iterable.class).returning(Iterable.class);
404     assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
405     @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
406     Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
407     assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
408   }
409 
testInstanceMethod_invalidReturning()410   public void testInstanceMethod_invalidReturning() throws Exception {
411     Invokable<?, Object> delegate = Prepender.method("prepend", Iterable.class);
412     assertThrows(
413         IllegalArgumentException.class,
414         () -> delegate.returning(new TypeToken<Iterable<Integer>>() {}));
415   }
416 
testPrivateInstanceMethod_isOverridable()417   public void testPrivateInstanceMethod_isOverridable() throws Exception {
418     Invokable<?, ?> delegate = Prepender.method("privateMethod");
419     assertTrue(delegate.isPrivate());
420     assertFalse(delegate.isOverridable());
421     assertFalse(delegate.isVarArgs());
422   }
423 
testPrivateFinalInstanceMethod_isOverridable()424   public void testPrivateFinalInstanceMethod_isOverridable() throws Exception {
425     Invokable<?, ?> delegate = Prepender.method("privateFinalMethod");
426     assertTrue(delegate.isPrivate());
427     assertTrue(delegate.isFinal());
428     assertFalse(delegate.isOverridable());
429     assertFalse(delegate.isVarArgs());
430   }
431 
testStaticMethod_isOverridable()432   public void testStaticMethod_isOverridable() throws Exception {
433     Invokable<?, ?> delegate = Prepender.method("staticMethod");
434     assertTrue(delegate.isStatic());
435     assertFalse(delegate.isOverridable());
436     assertFalse(delegate.isVarArgs());
437   }
438 
testStaticFinalMethod_isFinal()439   public void testStaticFinalMethod_isFinal() throws Exception {
440     Invokable<?, ?> delegate = Prepender.method("staticFinalMethod");
441     assertTrue(delegate.isStatic());
442     assertTrue(delegate.isFinal());
443     assertFalse(delegate.isOverridable());
444     assertFalse(delegate.isVarArgs());
445   }
446 
447   static class Foo {}
448 
testConstructor_isOverridable()449   public void testConstructor_isOverridable() throws Exception {
450     Invokable<?, ?> delegate = Invokable.from(Foo.class.getDeclaredConstructor());
451     assertFalse(delegate.isOverridable());
452     assertFalse(delegate.isVarArgs());
453   }
454 
testMethod_isVarArgs()455   public void testMethod_isVarArgs() throws Exception {
456     Invokable<?, ?> delegate = Prepender.method("privateVarArgsMethod", String[].class);
457     assertTrue(delegate.isVarArgs());
458   }
459 
testConstructor_isVarArgs()460   public void testConstructor_isVarArgs() throws Exception {
461     Invokable<?, ?> delegate = Prepender.constructor(String[].class);
462     assertTrue(delegate.isVarArgs());
463   }
464 
testGetOwnerType_constructor()465   public void testGetOwnerType_constructor() throws Exception {
466     Invokable<String, String> invokable = Invokable.from(String.class.getConstructor());
467     assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
468   }
469 
testGetOwnerType_method()470   public void testGetOwnerType_method() throws Exception {
471     Invokable<?, ?> invokable = Invokable.from(String.class.getMethod("length"));
472     assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
473   }
474 
475   private static final class FinalClass {
476     @SuppressWarnings("unused") // used by reflection
notFinalMethod()477     void notFinalMethod() {}
478   }
479 
testNonFinalMethodInFinalClass_isOverridable()480   public void testNonFinalMethodInFinalClass_isOverridable() throws Exception {
481     Invokable<?, ?> delegate = Invokable.from(FinalClass.class.getDeclaredMethod("notFinalMethod"));
482     assertFalse(delegate.isOverridable());
483     assertFalse(delegate.isVarArgs());
484   }
485 
486   private class InnerWithDefaultConstructor {
487     class NestedInner {}
488   }
489 
testInnerClassDefaultConstructor()490   public void testInnerClassDefaultConstructor() {
491     Constructor<?> constructor = InnerWithDefaultConstructor.class.getDeclaredConstructors()[0];
492     assertEquals(0, Invokable.from(constructor).getParameters().size());
493   }
494 
testNestedInnerClassDefaultConstructor()495   public void testNestedInnerClassDefaultConstructor() {
496     Constructor<?> constructor =
497         InnerWithDefaultConstructor.NestedInner.class.getDeclaredConstructors()[0];
498     assertEquals(0, Invokable.from(constructor).getParameters().size());
499   }
500 
501   private class InnerWithOneParameterConstructor {
502     @SuppressWarnings("unused") // called by reflection
InnerWithOneParameterConstructor(String s)503     public InnerWithOneParameterConstructor(String s) {}
504   }
505 
testInnerClassWithOneParameterConstructor()506   public void testInnerClassWithOneParameterConstructor() {
507     Constructor<?> constructor =
508         InnerWithOneParameterConstructor.class.getDeclaredConstructors()[0];
509     Invokable<?, ?> invokable = Invokable.from(constructor);
510     assertEquals(1, invokable.getParameters().size());
511     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
512   }
513 
514   private class InnerWithAnnotatedConstructorParameter {
515     @SuppressWarnings("unused") // called by reflection
InnerWithAnnotatedConstructorParameter(@ullable String s)516     InnerWithAnnotatedConstructorParameter(@Nullable String s) {}
517   }
518 
testInnerClassWithAnnotatedConstructorParameter()519   public void testInnerClassWithAnnotatedConstructorParameter() {
520     Constructor<?> constructor =
521         InnerWithAnnotatedConstructorParameter.class.getDeclaredConstructors()[0];
522     Invokable<?, ?> invokable = Invokable.from(constructor);
523     assertEquals(1, invokable.getParameters().size());
524     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
525   }
526 
527   private class InnerWithGenericConstructorParameter {
528     @SuppressWarnings("unused") // called by reflection
InnerWithGenericConstructorParameter(Iterable<String> it, String s)529     InnerWithGenericConstructorParameter(Iterable<String> it, String s) {}
530   }
531 
testInnerClassWithGenericConstructorParameter()532   public void testInnerClassWithGenericConstructorParameter() {
533     Constructor<?> constructor =
534         InnerWithGenericConstructorParameter.class.getDeclaredConstructors()[0];
535     Invokable<?, ?> invokable = Invokable.from(constructor);
536     assertEquals(2, invokable.getParameters().size());
537     assertEquals(new TypeToken<Iterable<String>>() {}, invokable.getParameters().get(0).getType());
538     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(1).getType());
539   }
540 
testAnonymousClassDefaultConstructor()541   public void testAnonymousClassDefaultConstructor() {
542     final int i = 1;
543     final String s = "hello world";
544     Class<?> anonymous =
545         new Runnable() {
546           @Override
547           public void run() {
548             System.out.println(s + i);
549           }
550         }.getClass();
551     Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
552     assertEquals(0, Invokable.from(constructor).getParameters().size());
553   }
554 
testAnonymousClassWithTwoParametersConstructor()555   public void testAnonymousClassWithTwoParametersConstructor() {
556     abstract class Base {
557       @SuppressWarnings("unused") // called by reflection
558       Base(String s, int i) {}
559     }
560     Class<?> anonymous = new Base("test", 0) {}.getClass();
561     Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
562     assertEquals(2, Invokable.from(constructor).getParameters().size());
563   }
564 
testLocalClassDefaultConstructor()565   public void testLocalClassDefaultConstructor() {
566     final int i = 1;
567     final String s = "hello world";
568     class LocalWithDefaultConstructor implements Runnable {
569       @Override
570       public void run() {
571         System.out.println(s + i);
572       }
573     }
574     Constructor<?> constructor = LocalWithDefaultConstructor.class.getDeclaredConstructors()[0];
575     assertEquals(0, Invokable.from(constructor).getParameters().size());
576   }
577 
testStaticAnonymousClassDefaultConstructor()578   public void testStaticAnonymousClassDefaultConstructor() throws Exception {
579     doTestStaticAnonymousClassDefaultConstructor();
580   }
581 
doTestStaticAnonymousClassDefaultConstructor()582   private static void doTestStaticAnonymousClassDefaultConstructor() {
583     final int i = 1;
584     final String s = "hello world";
585     Class<?> anonymous =
586         new Runnable() {
587           @Override
588           public void run() {
589             System.out.println(s + i);
590           }
591         }.getClass();
592     Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
593     assertEquals(0, Invokable.from(constructor).getParameters().size());
594   }
595 
testAnonymousClassInConstructor()596   public void testAnonymousClassInConstructor() {
597     AnonymousClassInConstructor unused = new AnonymousClassInConstructor();
598   }
599 
600   private static class AnonymousClassInConstructor {
AnonymousClassInConstructor()601     AnonymousClassInConstructor() {
602       final int i = 1;
603       final String s = "hello world";
604       Class<?> anonymous =
605           new Runnable() {
606             @Override
607             public void run() {
608               System.out.println(s + i);
609             }
610           }.getClass();
611       Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
612       assertEquals(0, Invokable.from(constructor).getParameters().size());
613     }
614   }
615 
testLocalClassInInstanceInitializer()616   public void testLocalClassInInstanceInitializer() {
617     LocalClassInInstanceInitializer unused = new LocalClassInInstanceInitializer();
618   }
619 
620   private static class LocalClassInInstanceInitializer {
621     {
622       class Local {}
623       Constructor<?> constructor = Local.class.getDeclaredConstructors()[0];
624       assertEquals(0, Invokable.from(constructor).getParameters().size());
625     }
626   }
627 
testLocalClassInStaticInitializer()628   public void testLocalClassInStaticInitializer() {
629     LocalClassInStaticInitializer unused = new LocalClassInStaticInitializer();
630   }
631 
632   private static class LocalClassInStaticInitializer {
633     static {
634       class Local {}
635       Constructor<?> constructor = Local.class.getDeclaredConstructors()[0];
636       assertEquals(0, Invokable.from(constructor).getParameters().size());
637     }
638   }
639 
testLocalClassWithSeeminglyHiddenThisInStaticInitializer_BUG()640   public void testLocalClassWithSeeminglyHiddenThisInStaticInitializer_BUG() {
641     LocalClassWithSeeminglyHiddenThisInStaticInitializer unused =
642         new LocalClassWithSeeminglyHiddenThisInStaticInitializer();
643   }
644 
645   /**
646    * This class demonstrates a bug in getParameters() when the local class is inside static
647    * initializer.
648    */
649   private static class LocalClassWithSeeminglyHiddenThisInStaticInitializer {
650     static {
651       class Local {
652         @SuppressWarnings("unused") // through reflection
Local(LocalClassWithSeeminglyHiddenThisInStaticInitializer outer)653         Local(LocalClassWithSeeminglyHiddenThisInStaticInitializer outer) {}
654       }
655       Constructor<?> constructor = Local.class.getDeclaredConstructors()[0];
656       int miscalculated = 0;
assertEquals(miscalculated, Invokable.from(constructor).getParameters().size())657       assertEquals(miscalculated, Invokable.from(constructor).getParameters().size());
658     }
659   }
660 
testLocalClassWithOneParameterConstructor()661   public void testLocalClassWithOneParameterConstructor() throws Exception {
662     final int i = 1;
663     final String s = "hello world";
664     class LocalWithOneParameterConstructor {
665       @SuppressWarnings("unused") // called by reflection
666       public LocalWithOneParameterConstructor(String x) {
667         System.out.println(s + i);
668       }
669     }
670     Constructor<?> constructor =
671         LocalWithOneParameterConstructor.class.getDeclaredConstructors()[0];
672     Invokable<?, ?> invokable = Invokable.from(constructor);
673     assertEquals(1, invokable.getParameters().size());
674     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
675   }
676 
testLocalClassWithAnnotatedConstructorParameter()677   public void testLocalClassWithAnnotatedConstructorParameter() throws Exception {
678     class LocalWithAnnotatedConstructorParameter {
679       @SuppressWarnings("unused") // called by reflection
680       LocalWithAnnotatedConstructorParameter(@Nullable String s) {}
681     }
682     Constructor<?> constructor =
683         LocalWithAnnotatedConstructorParameter.class.getDeclaredConstructors()[0];
684     Invokable<?, ?> invokable = Invokable.from(constructor);
685     assertEquals(1, invokable.getParameters().size());
686     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
687   }
688 
testLocalClassWithGenericConstructorParameter()689   public void testLocalClassWithGenericConstructorParameter() throws Exception {
690     class LocalWithGenericConstructorParameter {
691       @SuppressWarnings("unused") // called by reflection
692       LocalWithGenericConstructorParameter(Iterable<String> it, String s) {}
693     }
694     Constructor<?> constructor =
695         LocalWithGenericConstructorParameter.class.getDeclaredConstructors()[0];
696     Invokable<?, ?> invokable = Invokable.from(constructor);
697     assertEquals(2, invokable.getParameters().size());
698     assertEquals(new TypeToken<Iterable<String>>() {}, invokable.getParameters().get(0).getType());
699     assertEquals(TypeToken.of(String.class), invokable.getParameters().get(1).getType());
700   }
701 
testEquals()702   public void testEquals() throws Exception {
703     new EqualsTester()
704         .addEqualityGroup(A.constructor(), A.constructor())
705         .addEqualityGroup(A.method("privateMethod"), A.method("privateMethod"))
706         .addEqualityGroup(A.method("publicFinalMethod"))
707         .addEqualityGroup(Prepender.constructor(), Prepender.constructor())
708         .addEqualityGroup(Prepender.constructor(String.class, int.class))
709         .addEqualityGroup(Prepender.method("privateMethod"), Prepender.method("privateMethod"))
710         .addEqualityGroup(Prepender.method("privateFinalMethod"))
711         .testEquals();
712   }
713 
testNulls()714   public void testNulls() {
715     new NullPointerTester().testAllPublicStaticMethods(Invokable.class);
716     new NullPointerTester().testAllPublicInstanceMethods(Prepender.method("staticMethod"));
717   }
718 
719   @Retention(RetentionPolicy.RUNTIME)
720   private @interface NotBlank {}
721 
722   /** Class for testing constructor, static method and instance method. */
723   @SuppressWarnings("unused") // most are called by reflection
724   private static class Prepender {
725 
726     private final String prefix;
727     private final int times;
728 
Prepender(@otBlank @ullable String prefix, int times)729     Prepender(@NotBlank @Nullable String prefix, int times) throws NullPointerException {
730       this.prefix = prefix;
731       this.times = times;
732     }
733 
Prepender(String... varargs)734     Prepender(String... varargs) {
735       this(null, 0);
736     }
737 
738     // just for testing
Prepender()739     private <T> Prepender() {
740       this(null, 0);
741     }
742 
prepend(@otBlank String first, Iterable<String> tail)743     static <T> Iterable<String> prepend(@NotBlank String first, Iterable<String> tail) {
744       return Iterables.concat(ImmutableList.of(first), tail);
745     }
746 
prepend(Iterable<String> tail)747     Iterable<String> prepend(Iterable<String> tail)
748         throws IllegalArgumentException, NullPointerException {
749       return Iterables.concat(Collections.nCopies(times, prefix), tail);
750     }
751 
constructor(Class<?>.... parameterTypes)752     static Invokable<?, Prepender> constructor(Class<?>... parameterTypes) throws Exception {
753       Constructor<Prepender> constructor = Prepender.class.getDeclaredConstructor(parameterTypes);
754       return Invokable.from(constructor);
755     }
756 
method(String name, Class<?>... parameterTypes)757     static Invokable<Prepender, Object> method(String name, Class<?>... parameterTypes) {
758       try {
759         Method method = Prepender.class.getDeclaredMethod(name, parameterTypes);
760         @SuppressWarnings("unchecked") // The method is from Prepender.
761         Invokable<Prepender, Object> invokable =
762             (Invokable<Prepender, Object>) Invokable.from(method);
763         return invokable;
764       } catch (NoSuchMethodException e) {
765         throw new IllegalArgumentException(e);
766       }
767     }
768 
privateMethod()769     private void privateMethod() {}
770 
privateFinalMethod()771     private final void privateFinalMethod() {}
772 
staticMethod()773     static void staticMethod() {}
774 
staticFinalMethod()775     static final void staticFinalMethod() {}
776 
privateVarArgsMethod(String... varargs)777     private void privateVarArgsMethod(String... varargs) {}
778   }
779 
780   private static class SubPrepender extends Prepender {
781     @SuppressWarnings("unused") // needed to satisfy compiler, never called
SubPrepender()782     public SubPrepender() throws NullPointerException {
783       throw new AssertionError();
784     }
785   }
786 }
787