blob: 1b48ba673035981785f6864cbfa9d6db9be2c058 [file] [log] [blame]
/*
* Copyright (C) 2012 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.reflect;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.testing.EqualsTester;
import com.google.common.testing.NullPointerTester;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;
import java.util.Collections;
import junit.framework.TestCase;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Unit tests for {@link Invokable}.
*
* @author Ben Yu
*/
@AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations?
public class InvokableTest extends TestCase {
public void testConstructor_returnType() throws Exception {
assertEquals(Prepender.class, Prepender.constructor().getReturnType().getType());
}
private static class WithConstructorAndTypeParameter<T> {
@SuppressWarnings("unused") // by reflection
<X> WithConstructorAndTypeParameter() {}
}
public void testConstructor_returnType_hasTypeParameter() throws Exception {
@SuppressWarnings("rawtypes") // Foo.class for Foo<T> is always raw type
Class<WithConstructorAndTypeParameter> type = WithConstructorAndTypeParameter.class;
@SuppressWarnings("rawtypes") // Foo.class
Constructor<WithConstructorAndTypeParameter> constructor = type.getDeclaredConstructor();
Invokable<?, ?> factory = Invokable.from(constructor);
assertThat(factory.getTypeParameters()).hasLength(2);
assertEquals(type.getTypeParameters()[0], factory.getTypeParameters()[0]);
assertEquals(constructor.getTypeParameters()[0], factory.getTypeParameters()[1]);
ParameterizedType returnType = (ParameterizedType) factory.getReturnType().getType();
assertEquals(type, returnType.getRawType());
assertEquals(
ImmutableList.copyOf(type.getTypeParameters()),
ImmutableList.copyOf(returnType.getActualTypeArguments()));
}
public void testConstructor_exceptionTypes() throws Exception {
assertEquals(
ImmutableList.of(TypeToken.of(NullPointerException.class)),
Prepender.constructor(String.class, int.class).getExceptionTypes());
}
public void testConstructor_typeParameters() throws Exception {
TypeVariable<?>[] variables = Prepender.constructor().getTypeParameters();
assertThat(variables).hasLength(1);
assertEquals("A", variables[0].getName());
}
public void testConstructor_parameters() throws Exception {
Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
ImmutableList<Parameter> parameters = delegate.getParameters();
assertEquals(2, parameters.size());
assertEquals(String.class, parameters.get(0).getType().getType());
assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
assertEquals(int.class, parameters.get(1).getType().getType());
assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
new EqualsTester()
.addEqualityGroup(parameters.get(0))
.addEqualityGroup(parameters.get(1))
.testEquals();
}
public void testConstructor_call() throws Exception {
Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
Prepender prepender = delegate.invoke(null, "a", 1);
assertEquals("a", prepender.prefix);
assertEquals(1, prepender.times);
}
public void testConstructor_returning() throws Exception {
Invokable<?, Prepender> delegate =
Prepender.constructor(String.class, int.class).returning(Prepender.class);
Prepender prepender = delegate.invoke(null, "a", 1);
assertEquals("a", prepender.prefix);
assertEquals(1, prepender.times);
}
public void testConstructor_invalidReturning() throws Exception {
Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
try {
delegate.returning(SubPrepender.class);
fail();
} catch (IllegalArgumentException expected) {
}
}
public void testStaticMethod_returnType() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
}
public void testStaticMethod_exceptionTypes() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
assertEquals(ImmutableList.of(), delegate.getExceptionTypes());
}
public void testStaticMethod_typeParameters() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
TypeVariable<?>[] variables = delegate.getTypeParameters();
assertThat(variables).hasLength(1);
assertEquals("T", variables[0].getName());
}
public void testStaticMethod_parameters() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
ImmutableList<Parameter> parameters = delegate.getParameters();
assertEquals(2, parameters.size());
assertEquals(String.class, parameters.get(0).getType().getType());
assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(1).getType());
assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
new EqualsTester()
.addEqualityGroup(parameters.get(0))
.addEqualityGroup(parameters.get(1))
.testEquals();
}
public void testStaticMethod_call() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
@SuppressWarnings("unchecked") // prepend() returns Iterable<String>
Iterable<String> result =
(Iterable<String>) delegate.invoke(null, "a", ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
}
public void testStaticMethod_returning() throws Exception {
Invokable<?, Iterable<String>> delegate =
Prepender.method("prepend", String.class, Iterable.class)
.returning(new TypeToken<Iterable<String>>() {});
assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
}
public void testStaticMethod_returningRawType() throws Exception {
@SuppressWarnings("rawtypes") // the purpose is to test raw type
Invokable<?, Iterable> delegate =
Prepender.method("prepend", String.class, Iterable.class).returning(Iterable.class);
assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
@SuppressWarnings("unchecked") // prepend() returns Iterable<String>
Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
}
public void testStaticMethod_invalidReturning() throws Exception {
Invokable<?, Object> delegate = Prepender.method("prepend", String.class, Iterable.class);
try {
delegate.returning(new TypeToken<Iterable<Integer>>() {});
fail();
} catch (IllegalArgumentException expected) {
}
}
public void testInstanceMethod_returnType() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
}
public void testInstanceMethod_exceptionTypes() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
assertEquals(
ImmutableList.of(
TypeToken.of(IllegalArgumentException.class), TypeToken.of(NullPointerException.class)),
delegate.getExceptionTypes());
}
public void testInstanceMethod_typeParameters() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
assertThat(delegate.getTypeParameters()).isEmpty();
}
public void testInstanceMethod_parameters() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
ImmutableList<Parameter> parameters = delegate.getParameters();
assertEquals(1, parameters.size());
assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(0).getType());
assertThat(parameters.get(0).getAnnotations()).isEmpty();
new EqualsTester().addEqualityGroup(parameters.get(0)).testEquals();
}
public void testInstanceMethod_call() throws Exception {
Invokable<Prepender, ?> delegate = Prepender.method("prepend", Iterable.class);
@SuppressWarnings("unchecked") // prepend() returns Iterable<String>
Iterable<String> result =
(Iterable<String>) delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
}
public void testInstanceMethod_returning() throws Exception {
Invokable<Prepender, Iterable<String>> delegate =
Prepender.method("prepend", Iterable.class).returning(new TypeToken<Iterable<String>>() {});
assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
}
public void testInstanceMethod_returningRawType() throws Exception {
@SuppressWarnings("rawtypes") // the purpose is to test raw type
Invokable<Prepender, Iterable> delegate =
Prepender.method("prepend", Iterable.class).returning(Iterable.class);
assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
@SuppressWarnings("unchecked") // prepend() returns Iterable<String>
Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
}
public void testInstanceMethod_invalidReturning() throws Exception {
Invokable<?, Object> delegate = Prepender.method("prepend", Iterable.class);
try {
delegate.returning(new TypeToken<Iterable<Integer>>() {});
fail();
} catch (IllegalArgumentException expected) {
}
}
public void testPrivateInstanceMethod_isOverridable() throws Exception {
Invokable<?, ?> delegate = Prepender.method("privateMethod");
assertTrue(delegate.isPrivate());
assertFalse(delegate.isOverridable());
assertFalse(delegate.isVarArgs());
}
public void testPrivateFinalInstanceMethod_isOverridable() throws Exception {
Invokable<?, ?> delegate = Prepender.method("privateFinalMethod");
assertTrue(delegate.isPrivate());
assertTrue(delegate.isFinal());
assertFalse(delegate.isOverridable());
assertFalse(delegate.isVarArgs());
}
public void testStaticMethod_isOverridable() throws Exception {
Invokable<?, ?> delegate = Prepender.method("staticMethod");
assertTrue(delegate.isStatic());
assertFalse(delegate.isOverridable());
assertFalse(delegate.isVarArgs());
}
public void testStaticFinalMethod_isFinal() throws Exception {
Invokable<?, ?> delegate = Prepender.method("staticFinalMethod");
assertTrue(delegate.isStatic());
assertTrue(delegate.isFinal());
assertFalse(delegate.isOverridable());
assertFalse(delegate.isVarArgs());
}
static class Foo {}
public void testConstructor_isOverridablel() throws Exception {
Invokable<?, ?> delegate = Invokable.from(Foo.class.getDeclaredConstructor());
assertFalse(delegate.isOverridable());
assertFalse(delegate.isVarArgs());
}
public void testMethod_isVarArgs() throws Exception {
Invokable<?, ?> delegate = Prepender.method("privateVarArgsMethod", String[].class);
assertTrue(delegate.isVarArgs());
}
public void testConstructor_isVarArgs() throws Exception {
Invokable<?, ?> delegate = Prepender.constructor(String[].class);
assertTrue(delegate.isVarArgs());
}
public void testGetOwnerType_constructor() throws Exception {
Invokable<String, String> invokable = Invokable.from(String.class.getConstructor());
assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
}
public void testGetOwnerType_method() throws Exception {
Invokable<?, ?> invokable = Invokable.from(String.class.getMethod("length"));
assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
}
private static final class FinalClass {
@SuppressWarnings("unused") // used by reflection
void notFinalMethod() {}
}
public void testNonFinalMethodInFinalClass_isOverridable() throws Exception {
Invokable<?, ?> delegate = Invokable.from(FinalClass.class.getDeclaredMethod("notFinalMethod"));
assertFalse(delegate.isOverridable());
assertFalse(delegate.isVarArgs());
}
private class InnerWithDefaultConstructor {
class NestedInner {}
}
public void testInnerClassDefaultConstructor() {
Constructor<?> constructor = InnerWithDefaultConstructor.class.getDeclaredConstructors()[0];
assertEquals(0, Invokable.from(constructor).getParameters().size());
}
public void testNestedInnerClassDefaultConstructor() {
Constructor<?> constructor =
InnerWithDefaultConstructor.NestedInner.class.getDeclaredConstructors()[0];
assertEquals(0, Invokable.from(constructor).getParameters().size());
}
private class InnerWithOneParameterConstructor {
@SuppressWarnings("unused") // called by reflection
public InnerWithOneParameterConstructor(String s) {}
}
public void testInnerClassWithOneParameterConstructor() {
Constructor<?> constructor =
InnerWithOneParameterConstructor.class.getDeclaredConstructors()[0];
Invokable<?, ?> invokable = Invokable.from(constructor);
assertEquals(1, invokable.getParameters().size());
assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
}
private class InnerWithAnnotatedConstructorParameter {
@SuppressWarnings("unused") // called by reflection
InnerWithAnnotatedConstructorParameter(@Nullable String s) {}
}
public void testInnerClassWithAnnotatedConstructorParameter() {
Constructor<?> constructor =
InnerWithAnnotatedConstructorParameter.class.getDeclaredConstructors()[0];
Invokable<?, ?> invokable = Invokable.from(constructor);
assertEquals(1, invokable.getParameters().size());
assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
}
private class InnerWithGenericConstructorParameter {
@SuppressWarnings("unused") // called by reflection
InnerWithGenericConstructorParameter(Iterable<String> it, String s) {}
}
public void testInnerClassWithGenericConstructorParameter() {
Constructor<?> constructor =
InnerWithGenericConstructorParameter.class.getDeclaredConstructors()[0];
Invokable<?, ?> invokable = Invokable.from(constructor);
assertEquals(2, invokable.getParameters().size());
assertEquals(new TypeToken<Iterable<String>>() {}, invokable.getParameters().get(0).getType());
assertEquals(TypeToken.of(String.class), invokable.getParameters().get(1).getType());
}
public void testAnonymousClassDefaultConstructor() {
final int i = 1;
final String s = "hello world";
Class<?> anonymous =
new Runnable() {
@Override
public void run() {
System.out.println(s + i);
}
}.getClass();
Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
assertEquals(0, Invokable.from(constructor).getParameters().size());
}
public void testAnonymousClassWithTwoParametersConstructor() {
abstract class Base {
@SuppressWarnings("unused") // called by reflection
Base(String s, int i) {}
}
Class<?> anonymous = new Base("test", 0) {}.getClass();
Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
assertEquals(2, Invokable.from(constructor).getParameters().size());
}
public void testLocalClassDefaultConstructor() {
final int i = 1;
final String s = "hello world";
class LocalWithDefaultConstructor implements Runnable {
@Override
public void run() {
System.out.println(s + i);
}
}
Constructor<?> constructor = LocalWithDefaultConstructor.class.getDeclaredConstructors()[0];
assertEquals(0, Invokable.from(constructor).getParameters().size());
}
public void testStaticAnonymousClassDefaultConstructor() throws Exception {
doTestStaticAnonymousClassDefaultConstructor();
}
private static void doTestStaticAnonymousClassDefaultConstructor() {
final int i = 1;
final String s = "hello world";
Class<?> anonymous =
new Runnable() {
@Override
public void run() {
System.out.println(s + i);
}
}.getClass();
Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
assertEquals(0, Invokable.from(constructor).getParameters().size());
}
public void testAnonymousClassInConstructor() {
new AnonymousClassInConstructor();
}
private static class AnonymousClassInConstructor {
AnonymousClassInConstructor() {
final int i = 1;
final String s = "hello world";
Class<?> anonymous =
new Runnable() {
@Override
public void run() {
System.out.println(s + i);
}
}.getClass();
Constructor<?> constructor = anonymous.getDeclaredConstructors()[0];
assertEquals(0, Invokable.from(constructor).getParameters().size());
}
}
public void testLocalClassInInstanceInitializer() {
new LocalClassInInstanceInitializer();
}
private static class LocalClassInInstanceInitializer {
{
class Local {}
Constructor<?> constructor = Local.class.getDeclaredConstructors()[0];
assertEquals(0, Invokable.from(constructor).getParameters().size());
}
}
public void testLocalClassInStaticInitializer() {
new LocalClassInStaticInitializer();
}
private static class LocalClassInStaticInitializer {
static {
class Local {}
Constructor<?> constructor = Local.class.getDeclaredConstructors()[0];
assertEquals(0, Invokable.from(constructor).getParameters().size());
}
}
public void testLocalClassWithSeeminglyHiddenThisInStaticInitializer_BUG() {
new LocalClassWithSeeminglyHiddenThisInStaticInitializer();
}
/**
* This class demonstrates a bug in getParameters() when the local class is inside static
* initializer.
*/
private static class LocalClassWithSeeminglyHiddenThisInStaticInitializer {
static {
class Local {
@SuppressWarnings("unused") // through reflection
Local(LocalClassWithSeeminglyHiddenThisInStaticInitializer outer) {}
}
Constructor<?> constructor = Local.class.getDeclaredConstructors()[0];
int miscalculated = 0;
assertEquals(miscalculated, Invokable.from(constructor).getParameters().size());
}
}
public void testLocalClassWithOneParameterConstructor() throws Exception {
final int i = 1;
final String s = "hello world";
class LocalWithOneParameterConstructor {
@SuppressWarnings("unused") // called by reflection
public LocalWithOneParameterConstructor(String x) {
System.out.println(s + i);
}
}
Constructor<?> constructor =
LocalWithOneParameterConstructor.class.getDeclaredConstructors()[0];
Invokable<?, ?> invokable = Invokable.from(constructor);
assertEquals(1, invokable.getParameters().size());
assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
}
public void testLocalClassWithAnnotatedConstructorParameter() throws Exception {
class LocalWithAnnotatedConstructorParameter {
@SuppressWarnings("unused") // called by reflection
LocalWithAnnotatedConstructorParameter(@Nullable String s) {}
}
Constructor<?> constructor =
LocalWithAnnotatedConstructorParameter.class.getDeclaredConstructors()[0];
Invokable<?, ?> invokable = Invokable.from(constructor);
assertEquals(1, invokable.getParameters().size());
assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
}
public void testLocalClassWithGenericConstructorParameter() throws Exception {
class LocalWithGenericConstructorParameter {
@SuppressWarnings("unused") // called by reflection
LocalWithGenericConstructorParameter(Iterable<String> it, String s) {}
}
Constructor<?> constructor =
LocalWithGenericConstructorParameter.class.getDeclaredConstructors()[0];
Invokable<?, ?> invokable = Invokable.from(constructor);
assertEquals(2, invokable.getParameters().size());
assertEquals(new TypeToken<Iterable<String>>() {}, invokable.getParameters().get(0).getType());
assertEquals(TypeToken.of(String.class), invokable.getParameters().get(1).getType());
}
public void testEquals() throws Exception {
new EqualsTester()
.addEqualityGroup(Prepender.constructor(), Prepender.constructor())
.addEqualityGroup(Prepender.constructor(String.class, int.class))
.addEqualityGroup(Prepender.method("privateMethod"), Prepender.method("privateMethod"))
.addEqualityGroup(Prepender.method("privateFinalMethod"))
.testEquals();
}
public void testNulls() {
new NullPointerTester().testAllPublicStaticMethods(Invokable.class);
new NullPointerTester().testAllPublicInstanceMethods(Prepender.method("staticMethod"));
}
@Retention(RetentionPolicy.RUNTIME)
private @interface NotBlank {}
/** Class for testing constructor, static method and instance method. */
@SuppressWarnings("unused") // most are called by reflection
private static class Prepender {
private final String prefix;
private final int times;
Prepender(@NotBlank String prefix, int times) throws NullPointerException {
this.prefix = prefix;
this.times = times;
}
Prepender(String... varargs) {
this(null, 0);
}
// just for testing
private <A> Prepender() {
this(null, 0);
}
static <T> Iterable<String> prepend(@NotBlank String first, Iterable<String> tail) {
return Iterables.concat(ImmutableList.of(first), tail);
}
Iterable<String> prepend(Iterable<String> tail)
throws IllegalArgumentException, NullPointerException {
return Iterables.concat(Collections.nCopies(times, prefix), tail);
}
static Invokable<?, Prepender> constructor(Class<?>... parameterTypes) throws Exception {
Constructor<Prepender> constructor = Prepender.class.getDeclaredConstructor(parameterTypes);
return Invokable.from(constructor);
}
static Invokable<Prepender, Object> method(String name, Class<?>... parameterTypes) {
try {
Method method = Prepender.class.getDeclaredMethod(name, parameterTypes);
@SuppressWarnings("unchecked") // The method is from Prepender.
Invokable<Prepender, Object> invokable =
(Invokable<Prepender, Object>) Invokable.from(method);
return invokable;
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException(e);
}
}
private void privateMethod() {}
private final void privateFinalMethod() {}
static void staticMethod() {}
static final void staticFinalMethod() {}
private void privateVarArgsMethod(String... varargs) {}
}
private static class SubPrepender extends Prepender {
@SuppressWarnings("unused") // needed to satisfy compiler, never called
public SubPrepender() throws NullPointerException {
throw new AssertionError();
}
}
}