1/*
2 * Copyright (C) 2008 The Android Open Source Project
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
17package android.core;
18
19import android.test.suitebuilder.annotation.MediumTest;
20import android.test.suitebuilder.annotation.SmallTest;
21import android.widget.Button;
22import junit.framework.TestCase;
23
24import java.lang.reflect.Constructor;
25import java.lang.reflect.Field;
26import java.lang.reflect.Method;
27import java.lang.reflect.Modifier;
28import java.util.HashSet;
29import java.util.Set;
30
31
32class ClassWithPrivateConstructor {
33    private ClassWithPrivateConstructor() {
34    }
35}
36
37public class ClassTest extends TestCase {
38
39    @SmallTest
40    public void testClass() throws Exception {
41        // Now, never mind the fact that most of this stuff has to work
42        // for the test harness to get this far....
43
44        //System.out.println("Class.forName()");
45        Class helloClass = Class.forName(ClassTest.class.getName());
46
47        //System.out.println("Class.newInstance()");
48        Object instance = helloClass.newInstance();
49        assertNotNull(instance);
50
51        //System.out.println("Class.forName() nonexisting class");
52        try {
53            Class.forName("this.class.DoesNotExist");
54            fail("unexpected success");
55        } catch (ClassNotFoundException ex) {
56            // expected
57        }
58
59        //System.out.println("Class.newInstance() private constructor");
60        try {
61            Class.forName("android.core.ClassWithPrivateConstructor").newInstance();
62            fail("unexpected success");
63        } catch (IllegalAccessException ex) {
64            // this is expected
65        }
66
67        //System.out.println("Class.getDeclaredMethod()");
68
69        Method method = helloClass.getDeclaredMethod("method", (Class[]) null);
70
71        method.invoke(new ClassTest(), (Object[]) null);
72
73        //System.out.println("Class.getDeclaredMethod() w/ args");
74
75        method = helloClass.getDeclaredMethod("methodWithArgs", Object.class);
76
77        Object invokeArgs[] = new Object[1];
78        invokeArgs[0] = "Hello";
79        Object ret = method.invoke(new ClassTest(), invokeArgs);
80        assertEquals(ret, invokeArgs[0]);
81
82        //System.out.println("Class.getDeclaredMethod() -- private");
83
84        method = helloClass.getDeclaredMethod("privateMethod", (Class[]) null);
85
86        method.invoke(new ClassTest(), (Object[]) null);
87        //fail("unexpected success");
88        // TODO: I think this actually *should* succeed, because the
89        // call to the private method is being made from the same class.
90        // This needs to be replaced with a private call to a different
91        // class.
92
93        //System.out.println("Class.getSuperclass");
94        Class objectClass = Class.forName("java.lang.Object");
95        assertEquals(helloClass.getSuperclass().getSuperclass().getSuperclass(), objectClass);
96
97        //System.out.println("Class.isAssignableFrom");
98        assertTrue(objectClass.isAssignableFrom(helloClass));
99        assertFalse(helloClass.isAssignableFrom(objectClass));
100
101        //System.out.println("Class.getConstructor");
102
103        Constructor constructor = helloClass.getConstructor((Class[]) null);
104        assertNotNull(constructor);
105
106        //System.out.println("Class.getModifiers");
107
108        assertTrue(Modifier.isPublic(helloClass.getModifiers()));
109        //System.out.println("Modifiers: " + Modifier.toString(helloClass.getModifiers()));
110
111        //System.out.println("Class.getMethod");
112
113        helloClass.getMethod("method", (Class[]) null);
114
115        try {
116            Class[] argTypes = new Class[1];
117            argTypes[0] = helloClass;
118            helloClass.getMethod("method", argTypes);
119            fail("unexpected success");
120        } catch (NoSuchMethodException ex) {
121            // exception expected
122        }
123
124        // Test for public tracker issue 14
125        SimpleClass obj = new SimpleClass();
126        Field field = obj.getClass().getDeclaredField("str");
127        field.set(obj, null);
128    }
129
130    public class SimpleClass {
131        public String str;
132    }
133
134    public Object methodWithArgs(Object o) {
135        return o;
136    }
137
138    boolean methodInvoked;
139
140    public void method() {
141        methodInvoked = true;
142    }
143
144    boolean privateMethodInvoked;
145
146    public void privateMethod() {
147        privateMethodInvoked = true;
148    }
149
150    // Regression for 1018067: Class.getMethods() returns the same method over
151    // and over again from all base classes
152    @MediumTest
153    public void testClassGetMethodsNoDupes() {
154        Method[] methods = Button.class.getMethods();
155        Set<String> set = new HashSet<String>();
156
157        for (int i = 0; i < methods.length; i++) {
158            String signature = methods[i].toString();
159
160            int par = signature.indexOf('(');
161            int dot = signature.lastIndexOf('.', par);
162
163            signature = signature.substring(dot + 1);
164
165            assertFalse("Duplicate " + signature, set.contains(signature));
166            set.add(signature);
167        }
168    }
169
170    interface MyInterface {
171        void foo();
172    }
173
174    interface MyOtherInterface extends MyInterface {
175        void bar();
176    }
177
178    abstract class MyClass implements MyOtherInterface {
179        public void gabba() {
180        }
181
182        public void hey() {
183        }
184    }
185
186    // Check if we also reflect methods from interfaces
187    @SmallTest
188    public void testGetMethodsInterfaces() {
189        Method[] methods = MyInterface.class.getMethods();
190        assertTrue("Interface method must be there", hasMethod(methods, ".foo("));
191
192        methods = MyOtherInterface.class.getMethods();
193        assertTrue("Interface method must be there", hasMethod(methods, ".foo("));
194        assertTrue("Interface method must be there", hasMethod(methods, ".bar("));
195
196        methods = MyClass.class.getMethods();
197        assertTrue("Interface method must be there", hasMethod(methods, ".foo("));
198        assertTrue("Interface method must be there", hasMethod(methods, ".bar("));
199
200        assertTrue("Declared method must be there", hasMethod(methods, ".gabba("));
201        assertTrue("Declared method must be there", hasMethod(methods, ".hey("));
202
203        assertTrue("Inherited method must be there", hasMethod(methods, ".toString("));
204    }
205
206    private boolean hasMethod(Method[] methods, String signature) {
207        for (int i = 0; i < methods.length; i++) {
208            if (methods[i].toString().contains(signature)) {
209                return true;
210            }
211        }
212
213        return false;
214    }
215
216    // Test for Class.getPackage();
217    @SmallTest
218    public void testClassGetPackage() {
219        assertNotNull("Package must be non-null", getClass().getPackage());
220        assertEquals("Package must have expected name", "android.core", getClass().getPackage().getName());
221        assertEquals("Package must have expected title", "Unknown", getClass().getPackage().getSpecificationTitle());
222
223        Package p = java.lang.Object.class.getPackage();
224        assertNotNull("Package must be non-null", p);
225        assertEquals("Package must have expected name", "java.lang", p.getName());
226        assertSame("Package object must be same for each call", p, java.lang.Object.class.getPackage());
227    }
228
229    // Regression test for #1123708: Problem with getCanonicalName(),
230    // getSimpleName(), and getPackage().
231    //
232    // A couple of interesting cases need to be checked: Top-level classes,
233    // member classes, local classes, and anonymous classes. Also, boundary
234    // cases with '$' in the class names are checked, since the '$' is used
235    // as the separator between outer and inner class, so this might lead
236    // to problems (it did in the previous implementation).
237    //
238    // Caution: Adding local or anonymous classes elsewhere in this
239    // file might affect the test.
240    private class MemberClass {
241        // This space intentionally left blank.
242    }
243
244    private class Mi$o$oup {
245        // This space intentionally left blank.
246    }
247
248    @SmallTest
249    public void testVariousClassNames() {
250        Class<?> clazz = this.getClass();
251        String pkg = (clazz.getPackage() == null ? "" : clazz.getPackage().getName() + ".");
252
253        // Simple, top-level class
254
255        assertEquals("Top-level class name must be correct", pkg + "ClassTest", clazz.getName());
256        assertEquals("Top-level class simple name must be correct", "ClassTest", clazz.getSimpleName());
257        assertEquals("Top-level class canonical name must be correct", pkg + "ClassTest", clazz.getCanonicalName());
258
259        clazz = MemberClass.class;
260
261        assertEquals("Member class name must be correct", pkg + "ClassTest$MemberClass", clazz.getName());
262        assertEquals("Member class simple name must be correct", "MemberClass", clazz.getSimpleName());
263        assertEquals("Member class canonical name must be correct", pkg + "ClassTest.MemberClass", clazz.getCanonicalName());
264
265        class LocalClass {
266            // This space intentionally left blank.
267        }
268
269        clazz = LocalClass.class;
270
271        assertEquals("Local class name must be correct", pkg + "ClassTest$1LocalClass", clazz.getName());
272        assertEquals("Local class simple name must be correct", "LocalClass", clazz.getSimpleName());
273        assertNull("Local class canonical name must be null", clazz.getCanonicalName());
274
275        clazz = new Object() { }.getClass();
276
277        assertEquals("Anonymous class name must be correct", pkg + "ClassTest$1", clazz.getName());
278        assertEquals("Anonymous class simple name must be empty", "", clazz.getSimpleName());
279        assertNull("Anonymous class canonical name must be null", clazz.getCanonicalName());
280
281        // Weird special cases with dollar in name.
282
283        clazz = Mou$$aka.class;
284
285        assertEquals("Top-level class name must be correct", pkg + "Mou$$aka", clazz.getName());
286        assertEquals("Top-level class simple name must be correct", "Mou$$aka", clazz.getSimpleName());
287        assertEquals("Top-level class canonical name must be correct", pkg + "Mou$$aka", clazz.getCanonicalName());
288
289        clazz = Mi$o$oup.class;
290
291        assertEquals("Member class name must be correct", pkg + "ClassTest$Mi$o$oup", clazz.getName());
292        assertEquals("Member class simple name must be correct", "Mi$o$oup", clazz.getSimpleName());
293        assertEquals("Member class canonical name must be correct", pkg + "ClassTest.Mi$o$oup", clazz.getCanonicalName());
294
295        class Ma$hedPotatoe$ {
296            // This space intentionally left blank.
297        }
298
299        clazz = Ma$hedPotatoe$.class;
300
301        assertEquals("Member class name must be correct", pkg + "ClassTest$1Ma$hedPotatoe$", clazz.getName());
302        assertEquals("Member class simple name must be correct", "Ma$hedPotatoe$", clazz.getSimpleName());
303        assertNull("Member class canonical name must be null", clazz.getCanonicalName());
304    }
305
306    @SmallTest
307    public void testLocalMemberClass() {
308        Class<?> clazz = this.getClass();
309
310        assertFalse("Class must not be member", clazz.isMemberClass());
311        assertFalse("Class must not be local", clazz.isLocalClass());
312
313        clazz = MemberClass.class;
314
315        assertTrue("Class must be member", clazz.isMemberClass());
316        assertFalse("Class must not be local", clazz.isLocalClass());
317
318        class OtherLocalClass {
319            // This space intentionally left blank.
320        }
321
322        clazz = OtherLocalClass.class;
323
324        assertFalse("Class must not be member", clazz.isMemberClass());
325        assertTrue("Class must be local", clazz.isLocalClass());
326
327        clazz = new Object() { }.getClass();
328
329        assertFalse("Class must not be member", clazz.isMemberClass());
330        assertFalse("Class must not be local", clazz.isLocalClass());
331    }
332
333}
334
335class Mou$$aka {
336    // This space intentionally left blank.
337}
338