/* * Copyright 2011 Google Inc. 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.google.j2objc; import junit.framework.TestCase; import javax.annotation.Resource; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * Command-line tests for java.lang.Class support (IOSClass) * * @author Tom Ball */ public class ClassTest extends TestCase { public ClassTest() {} public ClassTest(Double d) { super(); } public int answerToLife() { return 42; } public void testForName() throws Exception { Class<?> thisClass = Class.forName("com.google.j2objc.ClassTest"); assertNotNull(thisClass); assertEquals("com.google.j2objc.ClassTest", thisClass.getName()); Method answerToLife = thisClass.getMethod("answerToLife"); Integer answer = (Integer) answerToLife.invoke(this); assertEquals(42, answer.intValue()); } public void testArrayForName() throws Exception { Class<?> arrayClass = Class.forName("[Ljava.lang.String;"); assertNotNull(arrayClass); assertEquals("[Ljava.lang.String;", arrayClass.getName()); String[] array = new String[0]; assertEquals(array.getClass(), arrayClass); // Test that array types not referenced in source can be loaded. arrayClass = Class.forName("[[[Ljava.lang.Integer;"); assertNotNull(arrayClass); } public void testGetDefaultConstructor() throws Exception { Class<?> foo = Class.forName("com.google.j2objc.ClassTest"); Constructor<?> c = foo.getConstructor(); Class<?>[] paramTypes = c.getParameterTypes(); assertEquals(0, paramTypes.length); } public void testGetConstructor() throws Exception { Class<?> foo = Class.forName("com.google.j2objc.ClassTest"); Constructor<?> c = foo.getConstructor(Double.class); Class<?>[] paramTypes = c.getParameterTypes(); assertEquals(1, paramTypes.length); } public void testGetDeclaredConstructor() throws Exception { Class<?> foo = Class.forName("com.google.j2objc.ClassTest"); Constructor<?> c = foo.getConstructor(); Class<?>[] paramTypes = c.getParameterTypes(); assertEquals(0, paramTypes.length); } public void testGetInterfaceMethods() throws Exception { Class<?> runnableClass = Class.forName("java.lang.Runnable"); Method[] methods = runnableClass.getMethods(); assertEquals(1, methods.length); Method runMethod = methods[0]; assertEquals("run", runMethod.getName()); assertEquals(0, runMethod.getParameterTypes().length); } public void testGetDeclaredInterfaceMethods() throws Exception { Class<?> runnableClass = Class.forName("java.lang.Runnable"); Method[] methods = runnableClass.getDeclaredMethods(); assertEquals(1, methods.length); Method runMethod = methods[0]; assertEquals("run", runMethod.getName()); assertEquals(0, runMethod.getParameterTypes().length); } public void testGetInterfaceMethod() throws Exception { Class<?> runnableClass = Class.forName("java.lang.Runnable"); Method runMethod = runnableClass.getMethod("run", new Class<?>[0]); assertEquals("run", runMethod.getName()); assertEquals(0, runMethod.getParameterTypes().length); } public void testGetArrayMethods() throws Exception { Method[] methods = int[].class.getDeclaredMethods(); assertEquals(0, methods.length); methods = int[].class.getMethods(); Set<String> methodNames = new HashSet<>(); for (Method m : methods) { methodNames.add(m.getName()); } assertTrue(methodNames.contains("equals")); assertTrue(methodNames.contains("toString")); } public void testInterfaceMethodInvocation() throws Exception { Class<?> runnableClass = Class.forName("java.lang.Runnable"); Method runMethod = runnableClass.getMethod("run", new Class<?>[0]); Runnable r = new Runnable() { public void run() { System.out.println("run, run"); } }; ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos); PrintStream oldOut = System.out; System.setOut(ps); try { runMethod.invoke(r, new Object[0]); System.out.flush(); assertEquals("run, run\n", baos.toString()); } finally { System.setOut(oldOut); } } public void testArrayClassObjects() throws Exception { Class<?> c1 = int[].class; Class<?> c2 = int[][].class; assertFalse(c1 == c2); assertTrue(c1 == c2.getComponentType()); c1 = String[].class; c2 = String[][].class; assertFalse(c1 == c2); assertTrue(c1 == c2.getComponentType()); } public void testGetPackage() throws Exception { // Test package name for a class. Class<?> listClass = Class.forName("java.util.ArrayList"); Package pkg = listClass.getPackage(); assertNotNull(pkg); assertEquals("java.util", pkg.getName()); // Test package name for an interface. Class<?> readerClass = Class.forName("java.io.Reader"); pkg = readerClass.getPackage(); assertNotNull(pkg); assertEquals("java.io", pkg.getName()); // Test no package for arrays and primitive types. assertNull(boolean.class.getPackage()); assertNull(int[].class.getPackage()); } public void testInnerClass() throws Exception { Class<?> innerClass = Class.forName("com.google.j2objc.ClassTest$InnerClass"); assertEquals(InnerClass.class, innerClass); assertEquals("com.google.j2objc.ClassTest$InnerClass", innerClass.getName()); assertEquals("InnerClass", innerClass.getSimpleName()); assertTrue(innerClass.isMemberClass()); assertEquals(ClassTest.class, innerClass.getEnclosingClass()); } public void testInnerInterface() throws Exception { Class<?> innerInterface = Class.forName("com.google.j2objc.ClassTest$InnerInterface"); assertEquals(InnerInterface.class, innerInterface); assertEquals("com.google.j2objc.ClassTest$InnerInterface", innerInterface.getName()); assertEquals("InnerInterface", innerInterface.getSimpleName()); assertTrue(innerInterface.isMemberClass()); assertEquals(ClassTest.class, innerInterface.getEnclosingClass()); } public void testAnonymousClass() throws Exception { Object o = new Object() {}; Class<?> cls = o.getClass(); assertTrue(cls.isAnonymousClass()); assertFalse(cls.isMemberClass()); assertEquals(ClassTest.class, cls.getEnclosingClass()); } public void testGetGenericSuperclass() throws Exception { Class<?> cls = SubParameterizedClass.class; Type genericSuperclass = cls.getGenericSuperclass(); assertTrue(genericSuperclass instanceof ParameterizedType); ParameterizedType pType = (ParameterizedType) genericSuperclass; assertEquals(ParameterizedClass.class, pType.getRawType()); Type[] typeArgs = pType.getActualTypeArguments(); assertEquals(2, typeArgs.length); assertEquals(String.class, typeArgs[0]); assertTrue(typeArgs[1] instanceof TypeVariable); assertEquals("C", ((TypeVariable) typeArgs[1]).getName()); } public void testArrayIsAssignableToObject() throws Exception { assertTrue(Object.class.isAssignableFrom(Object[].class)); assertTrue(Object.class.isAssignableFrom(Object[][].class)); assertTrue(Object.class.isAssignableFrom(String[].class)); assertTrue(Object.class.isAssignableFrom(int[].class)); assertFalse(String.class.isAssignableFrom(String[].class)); } public void testGetMappedMethods() throws Exception { Class<?> objectClass = Class.forName("java.lang.Object"); Method[] methods = objectClass.getDeclaredMethods(); assertTrue("not all Object methods returned, only: " + methods.length + " " + Arrays.toString(methods), methods.length >= 7); Class<?> classClass = Class.forName("java.lang.Class"); methods = classClass.getDeclaredMethods(); assertTrue("not all Class methods returned, only: " + methods.length + " " + Arrays.toString(methods), methods.length >= 5); Class<?> stringClass = Class.forName("java.lang.String"); methods = stringClass.getDeclaredMethods(); assertTrue("not all String methods returned, only: " + methods.length + " " + Arrays.toString(methods), methods.length >= 35); Class<?> numberClass = Class.forName("java.lang.Number"); methods = numberClass.getDeclaredMethods(); assertTrue("not all Number methods returned, only: " + methods.length + " " + Arrays.toString(methods), methods.length >= 6); } public void testGetEnum() throws Exception { Class<?> innerEnum = Class.forName("com.google.j2objc.ClassTest$InnerEnum"); assertNotNull(innerEnum); } public void testLookupClassWith$() throws Exception { Class<?> dollarClass = Class.forName("com.google.j2objc.Test$$With$$Dollar$$Signs"); assertNotNull(dollarClass); } // Verify that lookup of a method in an interface throws NoSuchMethod. public void testNoSuchInterfaceMethod() { try { Method m = java.io.Serializable.class.getMethod("foo"); fail("method shouldn't have been returned"); } catch (NoSuchMethodException e) { // Successful. } catch (Throwable t) { fail("wrong exception thrown"); } } // Verify that we can access the class literals for classes that are // hand-coded. public void testCertainClassLiterals() { assertEquals("java.lang.Object", Object.class.getName()); assertEquals("java.lang.String", String.class.getName()); assertEquals("java.lang.Cloneable", Cloneable.class.getName()); assertEquals("java.lang.Number", Number.class.getName()); assertEquals("java.lang.Iterable", Iterable.class.getName()); assertEquals("java.lang.Throwable", Throwable.class.getName()); assertEquals("java.lang.reflect.AccessibleObject", AccessibleObject.class.getName()); assertEquals("java.lang.reflect.Constructor", Constructor.class.getName()); assertEquals("java.lang.reflect.Field", Field.class.getName()); assertEquals("java.lang.reflect.Method", Method.class.getName()); assertEquals("javax.annotation.Resource", Resource.class.getName()); } // Verify that mapped classes return the correct superclass from a Java view. public void testCertainSuperclasses() { assertNull("Non-null Object superclass", Object.class.getSuperclass()); assertNull("Non-null Cloneable superclass", Cloneable.class.getSuperclass()); assertNull("Non-null Iterable superclass", Iterable.class.getSuperclass()); assertNull("Non-null Resource superclass", Resource.class.getSuperclass()); assertEquals("Bad String superclass", Object.class, String.class.getSuperclass()); assertEquals("Bad Number superclass", Object.class, Number.class.getSuperclass()); assertEquals("Bad Throwable superclass", Object.class, Throwable.class.getSuperclass()); assertEquals("Bad AccessibleObject superclass", Object.class, AccessibleObject.class.getSuperclass()); // Check a String subclass. assertEquals("Bad String superclass", Object.class, "foo".getClass().getSuperclass()); } /** * Verify that a class with a package that has been renamed using an * ObjectiveCName annotation can be reflexively loaded. */ public void testPackagePrefixAnnotation() throws Exception { // Lookup class by its Java name. Class<?> cls = Class.forName("java.lang.test.Example"); Object instance = cls.newInstance(); Method m = cls.getMethod("nativeClassName"); String nativeName = (String) m.invoke(instance); // Native name should have an OK prefix, instead of a camel-cased package name. assertEquals("OKExample", nativeName); } public void testDeclaringClass() throws Exception { Class<?> thisClass = Class.forName("com.google.j2objc.ClassTest"); assertNotNull(thisClass); Class<?> innerClass = Class.forName("com.google.j2objc.ClassTest$InnerClass"); assertNotNull(innerClass); assertEquals(thisClass, innerClass.getDeclaringClass()); Class<?> innerInterface = Class.forName("com.google.j2objc.ClassTest$InnerInterface"); assertNotNull(innerInterface); assertEquals(thisClass, innerInterface.getDeclaringClass()); Class<?> innerEnum = Class.forName("com.google.j2objc.ClassTest$InnerEnum"); assertNotNull(innerEnum); assertEquals(thisClass, innerEnum.getDeclaringClass()); } public void testClassIsAssignableToItself() throws Exception { Class<?> cls = Class.forName("java.util.HashMap"); assertNotNull(cls); assertTrue(cls.isAssignableFrom(cls)); Class<?> protocol = Class.forName("java.util.Map"); assertNotNull(protocol); assertTrue(protocol.isAssignableFrom(protocol)); } public void testCharSequenceClass() throws Exception { Class<?> cls = CharSequence.class; assertEquals("interface java.lang.CharSequence", cls.toString()); assertEquals(CharSequence.class, Class.forName("java.lang.CharSequence")); } public void testFindPrefixMappedClass() throws Exception { Class<?> cls = Class.forName("com.google.j2objc.mappedpkg.TestClass"); assertNotNull(cls); cls = Class.forName("com.google.j2objc.mappedpkg.TestClass$Inner"); assertNotNull(cls); } public void testDefaultEnumMethods() throws Exception { Method[] methods = InnerEnum.class.getDeclaredMethods(); assertEquals(2, methods.length); Method valuesMethod = InnerEnum.class.getDeclaredMethod("values"); assertNotNull(valuesMethod); assertEquals("[Lcom.google.j2objc.ClassTest$InnerEnum;", valuesMethod.getReturnType().getName()); Method valueOfMethod = InnerEnum.class.getDeclaredMethod("valueOf", String.class); assertNotNull(valueOfMethod); assertEquals(InnerEnum.class, valueOfMethod.getReturnType()); } boolean canCallAsSubclass(Class<?> x, Class<?> y) { boolean callSuccessful; try { Class<?> z = x.asSubclass(y); callSuccessful = true; } catch (ClassCastException e) { callSuccessful = false; } boolean assignable = y.isAssignableFrom(x); assertEquals(assignable, callSuccessful); return callSuccessful; } public void testAsSubclassCalls() throws Exception { assertTrue(canCallAsSubclass(Integer.class, Object.class)); assertFalse(canCallAsSubclass(Long.class, Integer.class)); assertTrue(canCallAsSubclass(Integer[].class, Object[].class)); assertTrue(canCallAsSubclass(Integer[].class, Object.class)); assertFalse(canCallAsSubclass(int.class, Object.class)); assertFalse(canCallAsSubclass(int.class, long.class)); assertTrue(canCallAsSubclass(int[].class, Object.class)); assertFalse(canCallAsSubclass(int[].class, Object[].class)); assertTrue(canCallAsSubclass(InterfaceP.class, Object.class)); assertFalse(canCallAsSubclass(InterfaceP.class, InterfaceQ.class)); assertTrue(canCallAsSubclass(InterfaceP[].class, Object.class)); assertTrue(canCallAsSubclass(InterfaceP[].class, Object[].class)); assertTrue(canCallAsSubclass(InterfaceR.class, InterfaceP.class)); assertFalse(canCallAsSubclass(InterfaceR.class, InterfaceQ.class)); assertTrue(canCallAsSubclass(InterfaceR.class, InterfaceR.class)); assertTrue(canCallAsSubclass(AbstractClassX.class, Object.class)); assertFalse(canCallAsSubclass(AbstractClassX.class, AbstractClassY.class)); assertTrue(canCallAsSubclass(AbstractClassX[].class, Object.class)); assertTrue(canCallAsSubclass(AbstractClassX[].class, Object[].class)); assertTrue(canCallAsSubclass(AbstractClassY.class, Object.class)); assertTrue(canCallAsSubclass(AbstractClassY.class, InterfaceP.class)); assertTrue(canCallAsSubclass(AbstractClassY[].class, Object.class)); assertTrue(canCallAsSubclass(AbstractClassY[].class, Object[].class)); assertFalse(canCallAsSubclass(AbstractClassY[].class, InterfaceP.class)); assertTrue(canCallAsSubclass(AbstractClassY[].class, InterfaceP[].class)); assertTrue(canCallAsSubclass(ConcreteClassA.class, AbstractClassX.class)); assertFalse(canCallAsSubclass(ConcreteClassA.class, AbstractClassY.class)); assertTrue(canCallAsSubclass(ConcreteClassA[].class, AbstractClassX[].class)); assertTrue(canCallAsSubclass(ConcreteClassB.class, AbstractClassY.class)); assertTrue(canCallAsSubclass(ConcreteClassB.class, InterfaceP.class)); assertFalse(canCallAsSubclass(ConcreteClassB.class, InterfaceQ.class)); assertTrue(canCallAsSubclass(ConcreteClassB[].class, AbstractClassY[].class)); assertTrue(canCallAsSubclass(ConcreteClassB[].class, InterfaceP[].class)); assertFalse(canCallAsSubclass(ConcreteClassC.class, ConcreteClassA.class)); assertTrue(canCallAsSubclass(ConcreteClassC.class, ConcreteClassB.class)); assertTrue(canCallAsSubclass(ConcreteClassC.class, InterfaceQ.class)); assertTrue(canCallAsSubclass(ConcreteClassC[].class, ConcreteClassB[].class)); assertTrue(canCallAsSubclass(ConcreteClassC[].class, InterfaceQ[].class)); assertTrue(canCallAsSubclass(ConcreteClassD.class, InterfaceP.class)); assertTrue(canCallAsSubclass(ConcreteClassD.class, InterfaceQ.class)); assertTrue(canCallAsSubclass(ConcreteClassE.class, AbstractClassX.class)); assertFalse(canCallAsSubclass(ConcreteClassE.class, AbstractClassY.class)); assertTrue(canCallAsSubclass(ConcreteClassE.class, InterfaceP.class)); assertTrue(canCallAsSubclass(ConcreteClassE.class, InterfaceQ.class)); } static class InnerClass { } interface InnerInterface { } static class ParameterizedClass<A, B> { } static class SubParameterizedClass<C> extends ParameterizedClass<String, C> { } static enum InnerEnum { A, B, C; } interface InterfaceP { } interface InterfaceQ { } interface InterfaceR extends InterfaceP { } static abstract class AbstractClassX { } static abstract class AbstractClassY implements InterfaceP { } class ConcreteClassA extends AbstractClassX { } class ConcreteClassB extends AbstractClassY { } class ConcreteClassC extends ConcreteClassB implements InterfaceQ { } class ConcreteClassD implements InterfaceP, InterfaceQ { } class ConcreteClassE extends AbstractClassX implements InterfaceP, InterfaceQ { } } class Test$$With$$Dollar$$Signs { }