/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ /* * 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 com.google.j2objc; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; 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.util.Arrays; import java.util.List; import junit.framework.TestCase; /** * Miscellaneous tests for J2ObjC's reflection support. */ public class ReflectionTest extends TestCase { @Target({ElementType.CONSTRUCTOR}) @Retention(RetentionPolicy.RUNTIME) @interface Mumble {} static class NoEquals { public NoEquals() {} @Mumble public NoEquals(String s) {} } static interface Defaults { public boolean noDefault(); public default boolean withDefault() { return true; } } static class HasDefault implements Defaults { @Override public boolean noDefault() { return false; } public void unrelatedMethod() {} } static class ParameterizedReturnTest { // Method that returns a parameterized type. public List<String> getStringList() { return null; } } static class TestFields { public long longField; } static class TestStaticInit { public static TestStaticInit instance = new TestStaticInit(); } // Assert equals method can be found using reflection. Because it's a mapped // method with a parameter, reflection was trying to find "equalsWithId:" // instead of "isEqual:". public void testEqualsMethodLookup() throws Exception { Method m = Integer.class.getMethod("equals", new Class<?>[] { Object.class }); assertNotNull(m); Integer uno = new Integer(1); Integer dos = new Integer(2); Boolean b = (Boolean) m.invoke(uno, new Object[] { dos }); assertFalse(b); b = (Boolean) m.invoke(uno, new Object[] { uno }); assertTrue(b); NoEquals obj1 = new NoEquals(); NoEquals obj2 = new NoEquals(); m = NoEquals.class.getMethod("equals", new Class<?>[] { Object.class }); assertNotNull(m); assertFalse ((Boolean) m.invoke(obj1, obj2)); assertTrue ((Boolean) m.invoke(obj1, obj1)); } // Verify non-default constructor annotations are returned. Issue 473 // reported that the annotations for the default constructor are always // returned, regardless of whether the constructor had parameters. public void testNonDefaultConstructorAnnotations() { Constructor<?>[] constructors = NoEquals.class.getDeclaredConstructors(); for (Constructor<?> c : constructors) { if (c.getParameterTypes().length == 0) { // Default constructor should not have a Mumble annotation. assertNull(c.getAnnotation(Mumble.class)); } else { assertNotNull(c.getAnnotation(Mumble.class)); } } } public void testParameterizedTypeMethodReturn() throws Exception { Method method = ParameterizedReturnTest.class.getMethod("getStringList"); Type returnType = method.getGenericReturnType(); assertTrue(returnType instanceof ParameterizedType); } // Regression for Issue 705. public void testAssignLong() throws Exception { TestFields o = new TestFields(); Field field = TestFields.class.getField("longField"); field.set(o, 3000000000L); assertEquals(3000000000L, o.longField); } // Regression for issue 767. public void testStaticInitialization() throws Exception { Class<?> unsafeClass = Class.forName("com.google.j2objc.ReflectionTest$TestStaticInit"); Field field = unsafeClass.getDeclaredField("instance"); // Verify instance isn't null, indicating the class's +initialize method ran. assertNotNull(field.get(null)); } public void testIsDefaultMethod() throws Exception { // Test interface with a default method. Class<?> defaultsInterface = Class.forName("com.google.j2objc.ReflectionTest$Defaults"); Method m = defaultsInterface.getMethod("withDefault"); assertTrue("isDefault false for default method", m.isDefault()); m = defaultsInterface.getMethod("noDefault"); assertFalse("isDefault true for non-default method", m.isDefault()); // Test implementing class. Class<?> defaultClass = Class.forName("com.google.j2objc.ReflectionTest$HasDefault"); m = defaultClass.getMethod("withDefault"); assertTrue("isDefault false for default method", m.isDefault()); m = defaultClass.getMethod("noDefault"); assertFalse("isDefault true for non-default method", m.isDefault()); m = defaultClass.getMethod("unrelatedMethod"); assertFalse("isDefault true for unrelated method", m.isDefault()); } enum Color { RED, GREEN, BLUE } @Retention(RetentionPolicy.RUNTIME) @interface OtherAnnotation { String value() default "hello"; } @Retention(RetentionPolicy.RUNTIME) @interface AnnotationWithDefaults { int intValue() default 123; String stringValue() default "abc"; Color enumValue() default Color.RED; String[] stringArrayValue() default { "foo", "bar" }; int[] intArrayValue() default { 4, 5, 6 }; OtherAnnotation annotationValue() default @OtherAnnotation; Class<?> classValue() default Iterable.class; } @AnnotationWithDefaults static class AnnotatedClass {} public void testAnnotationInitializedWithDefaults() { AnnotationWithDefaults a = AnnotatedClass.class.getAnnotation(AnnotationWithDefaults.class); assertEquals(123, a.intValue()); assertEquals("abc", a.stringValue()); assertEquals(Color.RED, a.enumValue()); assertTrue(Arrays.equals(new String[] { "foo", "bar" }, a.stringArrayValue())); assertTrue(Arrays.equals(new int[] { 4, 5, 6 }, a.intArrayValue())); assertEquals("hello", a.annotationValue().value()); assertEquals(Iterable.class, a.classValue()); } }