/* * Copyright (C) 2011 The Guava Authors * * 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.common.reflect; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; import com.google.common.collect.Lists; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import com.google.common.testing.NullPointerTester.Visibility; import com.google.common.testing.SerializableTester; import java.lang.reflect.GenericArrayType; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import junit.framework.TestCase; /** * Tests for {@link Types}. * * @author Ben Yu */ @AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations? public class TypesTest extends TestCase { public void testNewParameterizedType_ownerTypeImplied() throws Exception { ParameterizedType jvmType = (ParameterizedType) new TypeCapture<Map.Entry<String, Integer>>() {}.capture(); ParameterizedType ourType = Types.newParameterizedType( Map.Entry.class, String.class, Integer.class); assertEquals(jvmType, ourType); assertEquals(Map.class, ourType.getOwnerType()); } public void testNewParameterizedType() { ParameterizedType jvmType = (ParameterizedType) new TypeCapture<HashMap<String, int[][]>>() {}.capture(); ParameterizedType ourType = Types.newParameterizedType( HashMap.class, String.class, int[][].class); new EqualsTester() .addEqualityGroup(jvmType, ourType) .testEquals(); assertEquals(jvmType.toString(), ourType.toString()); assertEquals(jvmType.hashCode(), ourType.hashCode()); assertEquals(HashMap.class, ourType.getRawType()); assertThat(ourType.getActualTypeArguments()) .asList() .containsExactlyElementsIn(asList(jvmType.getActualTypeArguments())) .inOrder(); assertEquals(Arrays.asList(String.class, Types.newArrayType(Types.newArrayType(int.class))), Arrays.asList(ourType.getActualTypeArguments())); assertEquals(null, ourType.getOwnerType()); } public void testNewParameterizedType_nonStaticLocalClass() { class LocalClass<T> {} Type jvmType = new LocalClass<String>() {}.getClass().getGenericSuperclass(); Type ourType = Types.newParameterizedType(LocalClass.class, String.class); assertEquals(jvmType, ourType); } public void testNewParameterizedType_staticLocalClass() { doTestNewParameterizedType_staticLocalClass(); } private static void doTestNewParameterizedType_staticLocalClass() { class LocalClass<T> {} Type jvmType = new LocalClass<String>() {}.getClass().getGenericSuperclass(); Type ourType = Types.newParameterizedType(LocalClass.class, String.class); assertEquals(jvmType, ourType); } public void testNewParameterizedTypeWithOwner() { ParameterizedType jvmType = (ParameterizedType) new TypeCapture<Map.Entry<String, int[][]>>() {}.capture(); ParameterizedType ourType = Types.newParameterizedTypeWithOwner( Map.class, Map.Entry.class, String.class, int[][].class); new EqualsTester() .addEqualityGroup(jvmType, ourType) .addEqualityGroup(new TypeCapture<Map.Entry<String, String>>() {}.capture()) .addEqualityGroup(new TypeCapture<Map<String, Integer>>() {}.capture()) .testEquals(); assertEquals(jvmType.toString(), ourType.toString()); assertEquals(Map.class, ourType.getOwnerType()); assertEquals(Map.Entry.class, ourType.getRawType()); assertThat(ourType.getActualTypeArguments()) .asList() .containsExactlyElementsIn(asList(jvmType.getActualTypeArguments())) .inOrder(); } public void testNewParameterizedType_serializable() { SerializableTester.reserializeAndAssert(Types.newParameterizedType( Map.Entry.class, String.class, Integer.class)); } public void testNewParameterizedType_ownerMismatch() { try { Types.newParameterizedTypeWithOwner( Number.class, List.class, String.class); fail(); } catch (IllegalArgumentException expected) {} } public void testNewParameterizedType_ownerMissing() { assertEquals( Types.newParameterizedType(Map.Entry.class, String.class, Integer.class), Types.newParameterizedTypeWithOwner( null, Map.Entry.class, String.class, Integer.class)); } public void testNewParameterizedType_invalidTypeParameters() { try { Types.newParameterizedTypeWithOwner( Map.class, Map.Entry.class, String.class); fail(); } catch (IllegalArgumentException expected) {} } public void testNewParameterizedType_primitiveTypeParameters() { try { Types.newParameterizedTypeWithOwner( Map.class, Map.Entry.class, int.class, int.class); fail(); } catch (IllegalArgumentException expected) {} } public void testNewArrayType() { Type jvmType1 = new TypeCapture<List<String>[]>() {}.capture(); GenericArrayType ourType1 = (GenericArrayType) Types.newArrayType( Types.newParameterizedType(List.class, String.class)); Type jvmType2 = new TypeCapture<List[]>() {}.capture(); Type ourType2 = Types.newArrayType(List.class); new EqualsTester() .addEqualityGroup(jvmType1, ourType1) .addEqualityGroup(jvmType2, ourType2) .testEquals(); assertEquals(new TypeCapture<List<String>>() {}.capture(), ourType1.getGenericComponentType()); assertEquals(jvmType1.toString(), ourType1.toString()); assertEquals(jvmType2.toString(), ourType2.toString()); } public void testNewArrayTypeOfArray() { Type jvmType = new TypeCapture<int[][]>() {}.capture(); Type ourType = Types.newArrayType(int[].class); assertEquals(jvmType.toString(), ourType.toString()); new EqualsTester() .addEqualityGroup(jvmType, ourType) .testEquals(); } public void testNewArrayType_primitive() { Type jvmType = new TypeCapture<int[]>() {}.capture(); Type ourType = Types.newArrayType(int.class); assertEquals(jvmType.toString(), ourType.toString()); new EqualsTester() .addEqualityGroup(jvmType, ourType) .testEquals(); } public void testNewArrayType_upperBoundedWildcard() { Type wildcard = Types.subtypeOf(Number.class); assertEquals(Types.subtypeOf(Number[].class), Types.newArrayType(wildcard)); } public void testNewArrayType_lowerBoundedWildcard() { Type wildcard = Types.supertypeOf(Number.class); assertEquals(Types.supertypeOf(Number[].class), Types.newArrayType(wildcard)); } public void testNewArrayType_serializable() { SerializableTester.reserializeAndAssert( Types.newArrayType(int[].class)); } private static class WithWildcardType { @SuppressWarnings("unused") void withoutBound(List<?> list) {} @SuppressWarnings("unused") void withObjectBound(List<? extends Object> list) {} @SuppressWarnings("unused") void withUpperBound(List<? extends int[][]> list) {} @SuppressWarnings("unused") void withLowerBound(List<? super String[][]> list) {} static WildcardType getWildcardType(String methodName) throws Exception { ParameterizedType parameterType = (ParameterizedType) WithWildcardType.class .getDeclaredMethod(methodName, List.class) .getGenericParameterTypes()[0]; return (WildcardType) parameterType.getActualTypeArguments()[0]; } } public void testNewWildcardType() throws Exception { WildcardType noBoundJvmType = WithWildcardType.getWildcardType("withoutBound"); WildcardType objectBoundJvmType = WithWildcardType.getWildcardType("withObjectBound"); WildcardType upperBoundJvmType = WithWildcardType.getWildcardType("withUpperBound"); WildcardType lowerBoundJvmType = WithWildcardType.getWildcardType("withLowerBound"); WildcardType objectBound = Types.subtypeOf(Object.class); WildcardType upperBound = Types.subtypeOf(int[][].class); WildcardType lowerBound = Types.supertypeOf(String[][].class); assertEqualWildcardType(noBoundJvmType, objectBound); assertEqualWildcardType(objectBoundJvmType, objectBound); assertEqualWildcardType(upperBoundJvmType, upperBound); assertEqualWildcardType(lowerBoundJvmType, lowerBound); new EqualsTester() .addEqualityGroup( noBoundJvmType, objectBoundJvmType, objectBound) .addEqualityGroup(upperBoundJvmType, upperBound) .addEqualityGroup(lowerBoundJvmType, lowerBound) .testEquals(); } public void testNewWildcardType_primitiveTypeBound() { try { Types.subtypeOf(int.class); fail(); } catch (IllegalArgumentException expected) {} } public void testNewWildcardType_serializable() { SerializableTester.reserializeAndAssert( Types.supertypeOf(String.class)); SerializableTester.reserializeAndAssert( Types.subtypeOf(String.class)); SerializableTester.reserializeAndAssert( Types.subtypeOf(Object.class)); } private static void assertEqualWildcardType( WildcardType expected, WildcardType actual) { assertEquals(expected.toString(), actual.toString()); assertEquals(actual.toString(), expected.hashCode(), actual.hashCode()); assertThat(actual.getLowerBounds()) .asList() .containsExactlyElementsIn(asList(expected.getLowerBounds())) .inOrder(); assertThat(actual.getUpperBounds()) .asList() .containsExactlyElementsIn(asList(expected.getUpperBounds())) .inOrder(); } private static class WithTypeVariable { @SuppressWarnings("unused") <T> void withoutBound(List<T> list) {} @SuppressWarnings("unused") <T extends Object> void withObjectBound(List<T> list) {} @SuppressWarnings("unused") <T extends Number & CharSequence> void withUpperBound(List<T> list) {} static TypeVariable<?> getTypeVariable(String methodName) throws Exception { ParameterizedType parameterType = (ParameterizedType) WithTypeVariable.class .getDeclaredMethod(methodName, List.class) .getGenericParameterTypes()[0]; return (TypeVariable<?>) parameterType.getActualTypeArguments()[0]; } } public void testNewTypeVariable() throws Exception { TypeVariable<?> noBoundJvmType = WithTypeVariable.getTypeVariable("withoutBound"); TypeVariable<?> objectBoundJvmType = WithTypeVariable.getTypeVariable("withObjectBound"); TypeVariable<?> upperBoundJvmType = WithTypeVariable.getTypeVariable("withUpperBound"); TypeVariable<?> noBound = withBounds(noBoundJvmType); TypeVariable<?> objectBound = withBounds(objectBoundJvmType, Object.class); TypeVariable<?> upperBound = withBounds( upperBoundJvmType, Number.class, CharSequence.class); assertEqualTypeVariable(noBoundJvmType, noBound); assertEqualTypeVariable(noBoundJvmType, withBounds(noBoundJvmType, Object.class)); assertEqualTypeVariable(objectBoundJvmType, objectBound); assertEqualTypeVariable(upperBoundJvmType, upperBound); new TypeVariableEqualsTester() .addEqualityGroup(noBoundJvmType, noBound) .addEqualityGroup(objectBoundJvmType, objectBound) .addEqualityGroup(upperBoundJvmType, upperBound) .testEquals(); } public void testNewTypeVariable_primitiveTypeBound() { try { Types.newArtificialTypeVariable(List.class, "E", int.class); fail(); } catch (IllegalArgumentException expected) {} } public void testNewTypeVariable_serializable() throws Exception { try { SerializableTester.reserialize(Types.newArtificialTypeVariable(List.class, "E")); fail(); } catch (RuntimeException expected) {} } private static <D extends GenericDeclaration> TypeVariable<D> withBounds( TypeVariable<D> typeVariable, Type... bounds) { return Types.newArtificialTypeVariable( typeVariable.getGenericDeclaration(), typeVariable.getName(), bounds); } private static class TypeVariableEqualsTester { private final EqualsTester tester = new EqualsTester(); TypeVariableEqualsTester addEqualityGroup(Type jvmType, Type... types) { if (Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) { tester.addEqualityGroup(jvmType); tester.addEqualityGroup((Object[]) types); } else { tester.addEqualityGroup(Lists.asList(jvmType, types).toArray()); } return this; } void testEquals() { tester.testEquals(); } } private static void assertEqualTypeVariable( TypeVariable<?> expected, TypeVariable<?> actual) { assertEquals(expected.toString(), actual.toString()); assertEquals(expected.getName(), actual.getName()); assertEquals( expected.getGenericDeclaration(), actual.getGenericDeclaration()); if (!Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) { assertEquals(actual.toString(), expected.hashCode(), actual.hashCode()); } assertThat(actual.getBounds()) .asList() .containsExactlyElementsIn(asList(expected.getBounds())) .inOrder(); } /** * Working with arrays requires defensive code. Verify that we clone the * type array for both input and output. */ public void testNewParameterizedTypeImmutability() { Type[] typesIn = { String.class, Integer.class }; ParameterizedType parameterizedType = Types.newParameterizedType(Map.class, typesIn); typesIn[0] = null; typesIn[1] = null; Type[] typesOut = parameterizedType.getActualTypeArguments(); typesOut[0] = null; typesOut[1] = null; assertEquals(String.class, parameterizedType.getActualTypeArguments()[0]); assertEquals(Integer.class, parameterizedType.getActualTypeArguments()[1]); } public void testNewParameterizedTypeWithWrongNumberOfTypeArguments() { try { Types.newParameterizedType( Map.class, String.class, Integer.class, Long.class); fail(); } catch (IllegalArgumentException expected) {} } public void testToString() { assertEquals(int[].class.getName(), Types.toString(int[].class)); assertEquals(int[][].class.getName(), Types.toString(int[][].class)); assertEquals(String[].class.getName(), Types.toString(String[].class)); Type elementType = List.class.getTypeParameters()[0]; assertEquals(elementType.toString(), Types.toString(elementType)); } public void testNullPointers() { new NullPointerTester().testStaticMethods(Types.class, Visibility.PACKAGE); } }