/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * 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.alibaba.citrus.generictype; import static com.alibaba.citrus.generictype.TypeInfo.*; import static java.lang.reflect.Modifier.*; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.Method; import java.util.List; import java.util.Map; import com.alibaba.citrus.test.runner.Prototyped; import com.alibaba.citrus.test.runner.Prototyped.Prototypes; import com.alibaba.citrus.test.runner.Prototyped.TestData; import com.alibaba.citrus.test.runner.Prototyped.TestName; import com.alibaba.citrus.util.ClassUtil; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; /** * 测试{@link MethodInfo}。 * * @author Michael Zhou */ @RunWith(Prototyped.class) public class MethodTests extends BaseTypeTests implements Cloneable { private transient MethodInfo method; private transient boolean isConstructor; // 是否为构造函数? private transient String name; // 方法名,如果是构造函数,则为<init> private Class<?> ownerType; // 方法所在类的子类 private Class<?> declaringType; // 方法所在类 private String methodName; // 方法名,如果是构造函数,则为null private Class<?>[] paramTypes; // 参数类型表 private boolean isGeneric; // 是否包含类型参数? private String typeParams; // 类型参数toString private String signature; // 方法签名 private int modifiers; // 限定符 private String returnType; // 返回类型 private String exceptionTypes; // 异常类型 private String effectiveExceptionTypes; // 有效异常类型 private String toString; // toString结果 private String resolved; // resolve的结果 private boolean resolveChanged; // 如果为false,代表resolve返回this private String getMethodFromClassTypeInfo; // ClassTypeInfo.getMethod的结果 @Before public void init() throws Exception { this.declaringType = declaringType == null ? ownerType : declaringType; this.paramTypes = paramTypes == null ? new Class<?>[0] : paramTypes; this.method = (MethodInfo) factory.getGenericDeclaration(getMethodOrConstructor(ownerType, methodName, paramTypes)); this.isConstructor = methodName == null; this.name = isConstructor ? "<init>" : methodName; this.effectiveExceptionTypes = effectiveExceptionTypes == null ? exceptionTypes : effectiveExceptionTypes; this.resolved = resolved == null ? toString : resolved; this.getMethodFromClassTypeInfo = getMethodFromClassTypeInfo == null ? resolved : getMethodFromClassTypeInfo; } @TestName public String testName() throws Exception { GenericDeclaration decl = getMethodOrConstructor(ownerType, methodName, paramTypes); if (decl instanceof Method) { return String.format("%s.%s", ClassUtil.getSimpleClassName(ownerType), ((Method) decl).getName()); } else { return String.format("%s[%d params]", ClassUtil.getSimpleClassName(ownerType), ((Constructor<?>) decl).getParameterTypes().length); } } @SuppressWarnings("unused") private static class TestClass<X extends Number> { // constructors public TestClass(int i) { } protected TestClass(int i, int j) throws FileNotFoundException, IOException, RuntimeException { } TestClass(int i, int j, int k) throws IOException, FileNotFoundException, RuntimeException { } private TestClass(int i, int j, int k, int l) throws Exception { } public <A, B extends Number> TestClass(A i, List<B> j, int k, int l, int m) { } // methods public void a(int i) { } protected String b(String i, int j) throws FileNotFoundException, IOException, RuntimeException { return null; } List<Integer> c(String i, int j, int k) throws IOException, FileNotFoundException, RuntimeException { return null; } private int d(String i, int j, int k, int l) throws Exception { return 0; } public final static <A, B extends Number> Map<A, ? extends B> e(A a, List<B> b) { return null; } public List<X> f() { return null; } } @SuppressWarnings("unused") private static class TestClass2<Y extends Number> extends TestClass<Y> { public <A, B extends Number> TestClass2(A i, List<B> j, int k, int l, int m) { super(i, j, k, l, m); } public void g(List<Y> list) { } } @Prototypes public static TestData<MethodTests> data() { TestData<MethodTests> data = TestData.getInstance(MethodTests.class); MethodTests prototype; // ========================= // constructor // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass.class; prototype.methodName = null; prototype.paramTypes = new Class<?>[] { int.class }; prototype.isGeneric = false; prototype.typeParams = "[]"; prototype.signature = "<init>(I)V"; prototype.modifiers = PUBLIC; prototype.returnType = "void"; prototype.exceptionTypes = "[]"; prototype.effectiveExceptionTypes = "[]"; prototype.toString = "public MethodTests$TestClass(int)"; prototype.resolved = null; // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass.class; prototype.methodName = null; prototype.paramTypes = new Class<?>[] { int.class, int.class }; prototype.isGeneric = false; prototype.typeParams = "[]"; prototype.signature = "<init>(II)V"; prototype.modifiers = PROTECTED; prototype.returnType = "void"; prototype.exceptionTypes = "[FileNotFoundException, IOException, RuntimeException]"; prototype.effectiveExceptionTypes = "[IOException]"; prototype.toString = "protected MethodTests$TestClass(int, int) throws IOException"; prototype.resolved = null; // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass.class; prototype.methodName = null; prototype.paramTypes = new Class<?>[] { int.class, int.class, int.class }; prototype.isGeneric = false; prototype.typeParams = "[]"; prototype.signature = "<init>(III)V"; prototype.modifiers = 0; prototype.returnType = "void"; prototype.exceptionTypes = "[IOException, FileNotFoundException, RuntimeException]"; prototype.effectiveExceptionTypes = "[IOException]"; prototype.toString = "MethodTests$TestClass(int, int, int) throws IOException"; prototype.resolved = null; // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass.class; prototype.methodName = null; prototype.paramTypes = new Class<?>[] { int.class, int.class, int.class, int.class }; prototype.isGeneric = false; prototype.typeParams = "[]"; prototype.signature = "<init>(IIII)V"; prototype.modifiers = PRIVATE; prototype.returnType = "void"; prototype.exceptionTypes = "[Exception]"; prototype.effectiveExceptionTypes = "[Exception]"; prototype.toString = "private MethodTests$TestClass(int, int, int, int) throws Exception"; prototype.resolved = null; // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass.class; prototype.methodName = null; prototype.paramTypes = new Class<?>[] { Object.class, List.class, int.class, int.class, int.class }; prototype.isGeneric = true; prototype.typeParams = "[A, B]"; prototype.signature = "<init>(Ljava/lang/Object;Ljava/util/List;III)V"; prototype.modifiers = PUBLIC; prototype.returnType = "void"; prototype.exceptionTypes = "[]"; prototype.effectiveExceptionTypes = "[]"; prototype.toString = "public <A, B> MethodTests$TestClass(A, List<E=B>, int, int, int)"; prototype.resolved = "public <A, B> MethodTests$TestClass(Object, List<E=Number>, int, int, int)"; prototype.resolveChanged = true; prototype.getMethodFromClassTypeInfo = "public <A, B> MethodTests$TestClass(A, List<E=B>, int, int, int)"; // ========================= // method // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass.class; prototype.methodName = "a"; prototype.paramTypes = new Class<?>[] { int.class }; prototype.isGeneric = false; prototype.typeParams = "[]"; prototype.signature = "a(I)V"; prototype.modifiers = PUBLIC; prototype.returnType = "void"; prototype.exceptionTypes = "[]"; prototype.effectiveExceptionTypes = "[]"; prototype.toString = "public void MethodTests$TestClass.a(int)"; prototype.resolved = null; // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass.class; prototype.methodName = "b"; prototype.paramTypes = new Class<?>[] { String.class, int.class }; prototype.isGeneric = false; prototype.typeParams = "[]"; prototype.signature = "b(Ljava/lang/String;I)Ljava/lang/String;"; prototype.modifiers = PROTECTED; prototype.returnType = "String"; prototype.exceptionTypes = "[FileNotFoundException, IOException, RuntimeException]"; prototype.effectiveExceptionTypes = "[IOException]"; prototype.toString = "protected String MethodTests$TestClass.b(String, int) throws IOException"; prototype.resolved = null; // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass.class; prototype.methodName = "c"; prototype.paramTypes = new Class<?>[] { String.class, int.class, int.class }; prototype.isGeneric = false; prototype.typeParams = "[]"; prototype.signature = "c(Ljava/lang/String;II)Ljava/util/List;"; prototype.modifiers = 0; prototype.returnType = "List<E=Integer>"; prototype.exceptionTypes = "[IOException, FileNotFoundException, RuntimeException]"; prototype.effectiveExceptionTypes = "[IOException]"; prototype.toString = "List<E=Integer> MethodTests$TestClass.c(String, int, int) throws IOException"; prototype.resolved = null; // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass.class; prototype.methodName = "d"; prototype.paramTypes = new Class<?>[] { String.class, int.class, int.class, int.class }; prototype.isGeneric = false; prototype.typeParams = "[]"; prototype.signature = "d(Ljava/lang/String;III)I"; prototype.modifiers = PRIVATE; prototype.returnType = "int"; prototype.exceptionTypes = "[Exception]"; prototype.effectiveExceptionTypes = "[Exception]"; prototype.toString = "private int MethodTests$TestClass.d(String, int, int, int) throws Exception"; prototype.resolved = null; // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass.class; prototype.methodName = "e"; prototype.paramTypes = new Class<?>[] { Object.class, List.class }; prototype.isGeneric = true; prototype.typeParams = "[A, B]"; prototype.signature = "e(Ljava/lang/Object;Ljava/util/List;)Ljava/util/Map;"; prototype.modifiers = PUBLIC | STATIC; prototype.returnType = "Map<K=A, V=? extends B>"; prototype.exceptionTypes = "[]"; prototype.effectiveExceptionTypes = "[]"; prototype.toString = "public static <A, B> Map<K=A, V=? extends B> MethodTests$TestClass.e(A, List<E=B>)"; prototype.resolved = "public static <A, B> Map<K=Object, V=Number> MethodTests$TestClass.e(Object, List<E=Number>)"; prototype.resolveChanged = true; prototype.getMethodFromClassTypeInfo = "public static <A, B> Map<K=A, V=B> MethodTests$TestClass.e(A, List<E=B>)"; // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass2.class; prototype.declaringType = TestClass.class; prototype.methodName = "f"; prototype.paramTypes = new Class<?>[] { }; prototype.isGeneric = false; prototype.typeParams = "[]"; prototype.signature = "f()Ljava/util/List;"; prototype.modifiers = PUBLIC; prototype.returnType = "List<E=X>"; prototype.exceptionTypes = "[]"; prototype.effectiveExceptionTypes = "[]"; prototype.toString = "public List<E=X> MethodTests$TestClass.f()"; prototype.resolved = "public List<E=Number> MethodTests$TestClass.f()"; prototype.resolveChanged = true; prototype.getMethodFromClassTypeInfo = "public List<E=Y> MethodTests$TestClass.f()"; // ----------------- prototype = data.newPrototype(); prototype.ownerType = TestClass2.class; prototype.declaringType = TestClass2.class; prototype.methodName = "g"; prototype.paramTypes = new Class<?>[] { List.class }; prototype.isGeneric = false; prototype.typeParams = "[]"; prototype.signature = "g(Ljava/util/List;)V"; prototype.modifiers = PUBLIC; prototype.returnType = "void"; prototype.exceptionTypes = "[]"; prototype.effectiveExceptionTypes = "[]"; prototype.toString = "public void MethodTests$TestClass2.g(List<E=Y>)"; prototype.resolved = "public void MethodTests$TestClass2.g(List<E=Number>)"; prototype.resolveChanged = true; prototype.getMethodFromClassTypeInfo = "public void MethodTests$TestClass2.g(List<E=Y>)"; return data; } @Test public void isGeneric() { assertEquals(isGeneric, method.isGeneric()); } @Test public void getTypeParameters() { assertEquals(typeParams, method.getTypeParameters().toString()); } @Test public void isConstructor() { assertEquals(isConstructor, method.isConstructor()); } @Test public void getConstructorOrMethod() { if (isConstructor) { assertNotNull(method.getConstructor()); assertNull(method.getMethod()); } else { assertNull(method.getConstructor()); assertNotNull(method.getMethod()); } } @Test public void getDeclaringType() { assertSame(factory.getType(declaringType), method.getDeclaringType()); } @Test public void getSignature() { assertEquals(signature, method.getSignature().toString()); } @Test public void getModifiers() { assertEquals(modifiers, method.getModifiers()); } @Test public void getReturnType() { assertEquals(returnType, method.getReturnType().toString()); } @Test public void getName() { assertEquals(name, method.getName()); } @Test public void getParamTypes() { List<TypeInfo> types = method.getParameterTypes(); assertEquals(paramTypes.length, types.size()); int i = 0; for (TypeInfo type : types) { assertEquals(paramTypes[i++], type.getRawType()); } } @Test public void getExceptionTypes() { assertEquals(exceptionTypes, method.getExceptionTypes().toString()); } @Test public void getEffectiveExceptionTypes() { assertEquals(effectiveExceptionTypes, method.getEffectiveExceptionTypes().toString()); } @Test public void getActualTypeParameters() { List<TypeVariableInfo> vars = method.getTypeParameters(); List<TypeInfo> actualArgs = method.getActualTypeArguments(); for (int i = 0; i < vars.size(); i++) { assertEquals(vars.get(i).getBaseType(), actualArgs.get(i)); assertEquals(vars.get(i).getBaseType(), vars.get(i).resolve(method)); assertEquals(vars.get(i).getBaseType(), vars.get(i).resolve(factory.getGenericDeclaration(List.class))); } } @Test public void resolve() { assertEquals(resolved, method.resolve(null).toString()); assertEquals(resolved, method.resolve(null, true).toString()); assertEquals(resolveChanged, method != method.resolve(null)); } @Test public void getMethod_from_ClassTypeInfo() { ClassTypeInfo ownerType = TypeInfo.factory.getClassType(this.ownerType); if (isConstructor) { assertEquals(getMethodFromClassTypeInfo, ownerType.getConstructor(paramTypes).toString()); } else { assertEquals(getMethodFromClassTypeInfo, ownerType.getMethod(methodName, paramTypes).toString()); } } @Test public void equalsHashCode() throws Exception { MethodInfo newMethod = (MethodInfo) factory.getGenericDeclaration(getMethodOrConstructor(ownerType, methodName, paramTypes)); assertEquals(newMethod, method); assertNotSame(newMethod, method); assertEquals(newMethod.hashCode(), method.hashCode()); newMethod = (MethodInfo) factory.getGenericDeclaration(getMethodOrConstructor(String.class, null, new Class<?>[] { String.class })); assertThat(method, not(equalTo(newMethod))); assertNotSame(newMethod, method); assertThat(method.hashCode(), not(equalTo(newMethod.hashCode()))); } @Test public void toString_() { assertEquals(toString, method.toString()); } }