diff options
author | Jesse Wilson <jessewilson@google.com> | 2011-12-13 12:51:28 -0500 |
---|---|---|
committer | Jesse Wilson <jessewilson@google.com> | 2011-12-13 12:51:28 -0500 |
commit | a0ee76b0850774edeb0c67204070b89d117573bc (patch) | |
tree | 4fc399b5bc4bc27b703cfa0cb74292095259527a /luni | |
parent | 56de8e10ca290d46c26831d298d5cc7673a168ff (diff) | |
download | libcore-a0ee76b0850774edeb0c67204070b89d117573bc.zip libcore-a0ee76b0850774edeb0c67204070b89d117573bc.tar.gz libcore-a0ee76b0850774edeb0c67204070b89d117573bc.tar.bz2 |
Migrate some reflection tests from frameworks/base/tests/CoreTests
This found a problem where Method.toString() was returning "[C" for
char arrays rather than "char[]". Sadly the RI doesn't use either
getName() or getCanonicalName(). That problem was introduced with the
fix for http://b/3073292
Bug: http://b/3073226
Change-Id: I7c2b0ebfc1718f7f0e8da55bdefd13a8e4032a45
Diffstat (limited to 'luni')
7 files changed, 375 insertions, 19 deletions
diff --git a/luni/src/main/java/java/lang/reflect/AccessibleObject.java b/luni/src/main/java/java/lang/reflect/AccessibleObject.java index 6dde0c2..9c6b8c7 100644 --- a/luni/src/main/java/java/lang/reflect/AccessibleObject.java +++ b/luni/src/main/java/java/lang/reflect/AccessibleObject.java @@ -183,10 +183,10 @@ public class AccessibleObject implements AnnotatedElement { StringBuilder result = new StringBuilder(); if (types.length != 0) { - result.append(types[0].getName()); + appendTypeName(result, types[0]); for (int i = 1; i < types.length; i++) { result.append(','); - result.append(types[i].getName()); + appendTypeName(result, types[i]); } } @@ -228,23 +228,21 @@ public class AccessibleObject implements AnnotatedElement { private static native Object[] getClassSignatureAnnotation(Class clazz); /** - * Appends the specified class name to the buffer. The class may represent - * a simple type, a reference type or an array type. - * - * @param sb buffer - * @param obj the class which name should be appended to the buffer - * - * @throws NullPointerException if any of the arguments is null + * Appends the best {@link #toString} name for {@code c} to {@code out}. + * This works around the fact that {@link Class#getName} is lousy for + * primitive arrays (it writes "[C" instead of "char[]") and {@link + * Class#getCanonicalName()} is lousy for nested classes (it uses a "." + * separator rather than a "$" separator). */ - void appendArrayType(StringBuilder sb, Class<?> obj) { + void appendTypeName(StringBuilder out, Class<?> c) { int dimensions = 0; - while (obj.isArray()) { - obj = obj.getComponentType(); + while (c.isArray()) { + c = c.getComponentType(); dimensions++; } - sb.append(obj.getName()); + out.append(c.getName()); for (int d = 0; d < dimensions; d++) { - sb.append("[]"); + out.append("[]"); } } diff --git a/luni/src/main/java/java/lang/reflect/Constructor.java b/luni/src/main/java/java/lang/reflect/Constructor.java index b03e28b..9a0e03c 100644 --- a/luni/src/main/java/java/lang/reflect/Constructor.java +++ b/luni/src/main/java/java/lang/reflect/Constructor.java @@ -142,7 +142,7 @@ public final class Constructor<T> extends AccessibleObject implements GenericDec sb.append("> "); } // append constructor name - appendArrayType(sb, getDeclaringClass()); + appendTypeName(sb, getDeclaringClass()); // append parameters sb.append('('); appendArrayGenericType(sb, diff --git a/luni/src/main/java/java/lang/reflect/Field.java b/luni/src/main/java/java/lang/reflect/Field.java index 87c955a..0aacb11 100644 --- a/luni/src/main/java/java/lang/reflect/Field.java +++ b/luni/src/main/java/java/lang/reflect/Field.java @@ -869,9 +869,9 @@ public final class Field extends AccessibleObject implements Member { if (result.length() != 0) { result.append(' '); } - appendArrayType(result, type); + appendTypeName(result, type); result.append(' '); - result.append(declaringClass.getName()); + appendTypeName(result, declaringClass); result.append('.'); result.append(name); return result.toString(); diff --git a/luni/src/main/java/java/lang/reflect/Method.java b/luni/src/main/java/java/lang/reflect/Method.java index 8a9c0f1..044dbff 100644 --- a/luni/src/main/java/java/lang/reflect/Method.java +++ b/luni/src/main/java/java/lang/reflect/Method.java @@ -188,7 +188,7 @@ public final class Method extends AccessibleObject implements GenericDeclaration appendGenericType(sb, Types.getType(genericReturnType)); sb.append(' '); // append method name - appendArrayType(sb, getDeclaringClass()); + appendTypeName(sb, getDeclaringClass()); sb.append(".").append(getName()); // append parameters sb.append('('); diff --git a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java index a059d96..ef30eb8 100644 --- a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java +++ b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java @@ -197,6 +197,24 @@ public final class MethodTest extends TestCase { assertEquals(anonymous.getClass(), method.getDeclaringClass()); } + // http://b/1045939 + public void testMethodToString() throws Exception { + assertEquals("public final native void java.lang.Object.notify()", + Object.class.getMethod("notify", new Class[] { }).toString()); + assertEquals("public java.lang.String java.lang.Object.toString()", + Object.class.getMethod("toString", new Class[] { }).toString()); + assertEquals("public final native void java.lang.Object.wait(long,int)" + + " throws java.lang.InterruptedException", + Object.class.getMethod("wait", new Class[] { long.class, int.class }).toString()); + assertEquals("public boolean java.lang.Object.equals(java.lang.Object)", + Object.class.getMethod("equals", new Class[] { Object.class }).toString()); + assertEquals("public static java.lang.String java.lang.String.valueOf(char[])", + String.class.getMethod("valueOf", new Class[] { char[].class }).toString()); + assertEquals( "public java.lang.Process java.lang.Runtime.exec(java.lang.String[])" + + " throws java.io.IOException", + Runtime.class.getMethod("exec", new Class[] { String[].class }).toString()); + } + public static class MethodTestHelper { public void m1() throws IndexOutOfBoundsException { } public void m2(Object o) { } diff --git a/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java b/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java new file mode 100644 index 0000000..0a95bbf --- /dev/null +++ b/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * 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 libcore.java.lang.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import junit.framework.TestCase; + +public final class OldAndroidClassTest extends TestCase { + private static final String packageName = "libcore.java.lang.reflect"; + + public void testNewInstance() throws Exception { + Class helloClass = Class.forName(OldAndroidClassTest.class.getName()); + Object instance = helloClass.newInstance(); + assertNotNull(instance); + } + + public void testForName() throws Exception { + try { + Class.forName("this.class.DoesNotExist"); + fail(); + } catch (ClassNotFoundException expected) { + } + } + + public void testNewInstancePrivateConstructor() throws Exception { + try { + Class.forName(packageName + ".ClassWithPrivateConstructor").newInstance(); + fail(); + } catch (IllegalAccessException expected) { + } + } + + public void testGetDeclaredMethod() throws Exception { + Class helloClass = Class.forName(OldAndroidClassTest.class.getName()); + Method method = helloClass.getDeclaredMethod("method", (Class[]) null); + method.invoke(new OldAndroidClassTest(), (Object[]) null); + } + + public void testGetDeclaredMethodWithArgs() throws Exception { + Class helloClass = Class.forName(OldAndroidClassTest.class.getName()); + Method method = helloClass.getDeclaredMethod("methodWithArgs", Object.class); + + Object invokeArgs[] = new Object[1]; + invokeArgs[0] = "Hello"; + Object ret = method.invoke(new OldAndroidClassTest(), invokeArgs); + assertEquals(ret, invokeArgs[0]); + } + + public void testGetDeclaredMethodPrivate() throws Exception { + Class helloClass = Class.forName(OldAndroidClassTest.class.getName()); + Method method = helloClass.getDeclaredMethod("privateMethod", (Class[]) null); + method.invoke(new OldAndroidClassTest(), (Object[]) null); + } + + public void testGetSuperclass() throws Exception { + Class helloClass = Class.forName(OldAndroidClassTest.class.getName()); + Class objectClass = Class.forName("java.lang.Object"); + assertEquals(helloClass.getSuperclass().getSuperclass().getSuperclass(), objectClass); + } + + public void testIsAssignableFrom() throws Exception { + Class helloClass = Class.forName(OldAndroidClassTest.class.getName()); + Class objectClass = Class.forName("java.lang.Object"); + assertTrue(objectClass.isAssignableFrom(helloClass)); + assertFalse(helloClass.isAssignableFrom(objectClass)); + } + + public void testGetConstructor() throws Exception { + Class helloClass = Class.forName(OldAndroidClassTest.class.getName()); + Constructor constructor = helloClass.getConstructor((Class[]) null); + assertNotNull(constructor); + } + + public void testGetModifiers() throws Exception { + Class helloClass = Class.forName(OldAndroidClassTest.class.getName()); + assertTrue(Modifier.isPublic(helloClass.getModifiers())); + } + + public void testGetMethod() throws Exception { + Class helloClass = Class.forName(OldAndroidClassTest.class.getName()); + helloClass.getMethod("method", (Class[]) null); + try { + Class[] argTypes = new Class[1]; + argTypes[0] = helloClass; + helloClass.getMethod("method", argTypes); + fail(); + } catch (NoSuchMethodException expected) { + } + } + + // http://code.google.com/p/android/issues/detail?id=14 + public void testFieldSet() throws Exception { + OldAndroidClassTest.SimpleClass obj = new OldAndroidClassTest.SimpleClass(); + Field field = obj.getClass().getDeclaredField("str"); + field.set(obj, null); + } + + public class SimpleClass { + public String str; + } + + public Object methodWithArgs(Object o) { + return o; + } + + boolean methodInvoked; + + public void method() { + methodInvoked = true; + } + + boolean privateMethodInvoked; + + public void privateMethod() { + privateMethodInvoked = true; + } + + // Regression for 1018067: Class.getMethods() returns the same method over + // and over again from all base classes + public void testClassGetMethodsNoDupes() { + Method[] methods = ArrayList.class.getMethods(); + Set<String> set = new HashSet<String>(); + + for (Method method : methods) { + String signature = method.toString(); + + int par = signature.indexOf('('); + int dot = signature.lastIndexOf('.', par); + + signature = signature.substring(dot + 1); + + assertFalse("Duplicate " + signature, set.contains(signature)); + set.add(signature); + } + } + + interface MyInterface { + void foo(); + } + + interface MyOtherInterface extends MyInterface { + void bar(); + } + + abstract class MyClass implements MyOtherInterface { + public void gabba() { + } + + public void hey() { + } + } + + // Check if we also reflect methods from interfaces + public void testGetMethodsInterfaces() { + Method[] methods = MyInterface.class.getMethods(); + assertTrue(hasMethod(methods, ".foo(")); + + methods = MyOtherInterface.class.getMethods(); + assertTrue(hasMethod(methods, ".foo(")); + assertTrue(hasMethod(methods, ".bar(")); + + methods = MyClass.class.getMethods(); + assertTrue(hasMethod(methods, ".foo(")); + assertTrue(hasMethod(methods, ".bar(")); + + assertTrue(hasMethod(methods, ".gabba(")); + assertTrue(hasMethod(methods, ".hey(")); + + assertTrue(hasMethod(methods, ".toString(")); + } + + private boolean hasMethod(Method[] methods, String signature) { + for (Method method : methods) { + if (method.toString().contains(signature)) { + return true; + } + } + return false; + } + + // Test for Class.getPackage(); + public void testClassGetPackage() { + assertNotNull(getClass().getPackage()); + assertEquals(packageName, getClass().getPackage().getName()); + assertEquals("Unknown", getClass().getPackage().getSpecificationTitle()); + + Package p = Object.class.getPackage(); + assertNotNull(p); + assertEquals("java.lang", p.getName()); + assertSame(p, Object.class.getPackage()); + } + + // Regression test for #1123708: Problem with getCanonicalName(), + // getSimpleName(), and getPackage(). + // + // A couple of interesting cases need to be checked: Top-level classes, + // member classes, local classes, and anonymous classes. Also, boundary + // cases with '$' in the class names are checked, since the '$' is used + // as the separator between outer and inner class, so this might lead + // to problems (it did in the previous implementation). + // + // Caution: Adding local or anonymous classes elsewhere in this + // file might affect the test. + private class MemberClass { + } + + private class Mi$o$oup { + } + + public void testVariousClassNames() { + Class<?> clazz = this.getClass(); + String pkg = (clazz.getPackage() == null ? "" : clazz.getPackage().getName() + "."); + + // Simple, top-level class + + assertEquals(pkg + "OldAndroidClassTest", clazz.getName()); + assertEquals("OldAndroidClassTest", clazz.getSimpleName()); + assertEquals(pkg + "OldAndroidClassTest", clazz.getCanonicalName()); + + clazz = MemberClass.class; + + assertEquals(pkg + "OldAndroidClassTest$MemberClass", clazz.getName()); + assertEquals("MemberClass", clazz.getSimpleName()); + assertEquals(pkg + "OldAndroidClassTest.MemberClass", clazz.getCanonicalName()); + + class LocalClass { + // This space intentionally left blank. + } + + clazz = LocalClass.class; + + assertEquals(pkg + "OldAndroidClassTest$1LocalClass", clazz.getName()); + assertEquals("LocalClass", clazz.getSimpleName()); + assertNull(clazz.getCanonicalName()); + + clazz = new Object() { }.getClass(); + + assertEquals(pkg + "OldAndroidClassTest$1", clazz.getName()); + assertEquals("", clazz.getSimpleName()); + assertNull(clazz.getCanonicalName()); + + // Weird special cases with dollar in name. + + clazz = Mou$$aka.class; + + assertEquals(pkg + "Mou$$aka", clazz.getName()); + assertEquals("Mou$$aka", clazz.getSimpleName()); + assertEquals(pkg + "Mou$$aka", clazz.getCanonicalName()); + + clazz = Mi$o$oup.class; + + assertEquals(pkg + "OldAndroidClassTest$Mi$o$oup", clazz.getName()); + assertEquals("Mi$o$oup", clazz.getSimpleName()); + assertEquals(pkg + "OldAndroidClassTest.Mi$o$oup", clazz.getCanonicalName()); + + class Ma$hedPotatoe$ { + } + + clazz = Ma$hedPotatoe$.class; + + assertEquals(pkg + "OldAndroidClassTest$1Ma$hedPotatoe$", clazz.getName()); + assertEquals("Ma$hedPotatoe$", clazz.getSimpleName()); + assertNull(clazz.getCanonicalName()); + } + + public void testLocalMemberClass() { + Class<?> clazz = this.getClass(); + + assertFalse(clazz.isMemberClass()); + assertFalse(clazz.isLocalClass()); + + clazz = MemberClass.class; + + assertTrue(clazz.isMemberClass()); + assertFalse(clazz.isLocalClass()); + + class OtherLocalClass { + } + + clazz = OtherLocalClass.class; + + assertFalse(clazz.isMemberClass()); + assertTrue(clazz.isLocalClass()); + + clazz = new Object() { }.getClass(); + + assertFalse(clazz.isMemberClass()); + assertFalse(clazz.isLocalClass()); + } +} + +class ClassWithPrivateConstructor { + private ClassWithPrivateConstructor() { + } +} + +class Mou$$aka { +} diff --git a/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java b/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java index 3b092e5..8d7546a 100644 --- a/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java +++ b/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java @@ -32,7 +32,6 @@ import java.util.Set; import junit.framework.TestCase; public final class ReflectionTest extends TestCase { - String classA = "libcore.java.lang.reflect.ReflectionTest$A"; String classB = "libcore.java.lang.reflect.ReflectionTest$B"; String classC = "libcore.java.lang.reflect.ReflectionTest$C"; @@ -45,6 +44,29 @@ public final class ReflectionTest extends TestCase { AList.class.getGenericSuperclass().toString()); } + public void testClassGetName() { + assertEquals("int", int.class.getName()); + assertEquals("[I", int[].class.getName()); + assertEquals("java.lang.String", String.class.getName()); + assertEquals("[Ljava.lang.String;", String[].class.getName()); + assertEquals("libcore.java.lang.reflect.ReflectionTest", getClass().getName()); + assertEquals(getClass().getName() + "$A", A.class.getName()); + assertEquals(getClass().getName() + "$B", B.class.getName()); + assertEquals(getClass().getName() + "$DefinesMember", DefinesMember.class.getName()); + } + + public void testClassGetCanonicalName() { + assertEquals("int", int.class.getCanonicalName()); + assertEquals("int[]", int[].class.getCanonicalName()); + assertEquals("java.lang.String", String.class.getCanonicalName()); + assertEquals("java.lang.String[]", String[].class.getCanonicalName()); + assertEquals("libcore.java.lang.reflect.ReflectionTest", getClass().getCanonicalName()); + assertEquals(getClass().getName() + ".A", A.class.getCanonicalName()); + assertEquals(getClass().getName() + ".B", B.class.getCanonicalName()); + assertEquals(getClass().getName() + ".DefinesMember", + DefinesMember.class.getCanonicalName()); + } + public void testFieldToString() throws Exception { Field fieldOne = C.class.getDeclaredField("fieldOne"); String fieldOneRaw = "public static " + classA + " " + classC + ".fieldOne"; |