/* * Copyright 2011-2017 the original author or 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 org.springframework.data.util; import static org.assertj.core.api.Assertions.*; import static org.springframework.data.util.ClassTypeInformation.*; import java.lang.reflect.Constructor; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * Unit tests for {@link TypeDiscoverer}. * * @author Oliver Gierke */ @RunWith(MockitoJUnitRunner.class) public class TypeDiscovererUnitTests { static final Map<TypeVariable<?>, Type> EMPTY_MAP = Collections.emptyMap(); @Mock Map<TypeVariable<?>, Type> firstMap; @Mock Map<TypeVariable<?>, Type> secondMap; @Test(expected = IllegalArgumentException.class) public void rejectsNullType() { new TypeDiscoverer<>(null, null); } @Test public void isNotEqualIfTypesDiffer() { TypeDiscoverer<Object> objectTypeInfo = new TypeDiscoverer<>(Object.class, EMPTY_MAP); TypeDiscoverer<String> stringTypeInfo = new TypeDiscoverer<>(String.class, EMPTY_MAP); assertThat(objectTypeInfo.equals(stringTypeInfo)).isFalse(); } @Test public void isNotEqualIfTypeVariableMapsDiffer() { assertThat(firstMap.equals(secondMap)).isFalse(); TypeDiscoverer<Object> first = new TypeDiscoverer<>(Object.class, firstMap); TypeDiscoverer<Object> second = new TypeDiscoverer<>(Object.class, secondMap); assertThat(first.equals(second)).isFalse(); } @Test public void dealsWithTypesReferencingThemselves() { TypeInformation<SelfReferencing> information = from(SelfReferencing.class); Optional<TypeInformation<?>> first = information.getProperty("parent").flatMap(TypeInformation::getMapValueType); Optional<TypeInformation<?>> second = first.flatMap(it -> it.getProperty("map")) .flatMap(TypeInformation::getMapValueType); assertThat(second).isEqualTo(first); } @Test public void dealsWithTypesReferencingThemselvesInAMap() { TypeInformation<SelfReferencingMap> information = from(SelfReferencingMap.class); Optional<TypeInformation<?>> property = information.getProperty("map"); assertThat(property).hasValueSatisfying(it -> assertThat(it.getMapValueType()).hasValue(information)); } @Test public void returnsComponentAndValueTypesForMapExtensions() { TypeInformation<?> discoverer = new TypeDiscoverer<>(CustomMap.class, EMPTY_MAP); assertThat(discoverer.getMapValueType()).hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Locale.class)); assertThat(discoverer.getComponentType()) .hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class)); } @Test public void returnsComponentTypeForCollectionExtension() { TypeDiscoverer<CustomCollection> discoverer = new TypeDiscoverer<>(CustomCollection.class, firstMap); assertThat(discoverer.getComponentType()) .hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class)); } @Test public void returnsComponentTypeForArrays() { TypeDiscoverer<String[]> discoverer = new TypeDiscoverer<>(String[].class, EMPTY_MAP); assertThat(discoverer.getComponentType()) .hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class)); } @Test // DATACMNS-57 public void discoveresConstructorParameterTypesCorrectly() throws NoSuchMethodException, SecurityException { TypeDiscoverer<GenericConstructors> discoverer = new TypeDiscoverer<>(GenericConstructors.class, firstMap); Constructor<GenericConstructors> constructor = GenericConstructors.class.getConstructor(List.class, Locale.class); List<TypeInformation<?>> types = discoverer.getParameterTypes(constructor); assertThat(types).hasSize(2); assertThat(types.get(0).getType()).isEqualTo(List.class); assertThat(types.get(0).getComponentType()) .hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class)); } @Test @SuppressWarnings("rawtypes") public void returnsNullForComponentAndValueTypesForRawMaps() { TypeDiscoverer<Map> discoverer = new TypeDiscoverer<>(Map.class, EMPTY_MAP); assertThat(discoverer.getComponentType()).isEmpty(); assertThat(discoverer.getMapValueType()).isEmpty(); } @Test // DATACMNS-167 @SuppressWarnings("rawtypes") public void doesNotConsiderTypeImplementingIterableACollection() { TypeDiscoverer<Person> discoverer = new TypeDiscoverer<>(Person.class, EMPTY_MAP); TypeInformation reference = from(Address.class); Optional<TypeInformation<?>> addresses = discoverer.getProperty("addresses"); assertThat(addresses).hasValueSatisfying(it -> { assertThat(it.isCollectionLike()).isFalse(); assertThat(it.getComponentType()).hasValue(reference); }); Optional<TypeInformation<?>> adressIterable = discoverer.getProperty("addressIterable"); assertThat(adressIterable).hasValueSatisfying(it -> { assertThat(it.isCollectionLike()).isTrue(); assertThat(it.getComponentType()).hasValue(reference); }); } class Person { Addresses addresses; Iterable<Address> addressIterable; } abstract class Addresses implements Iterable<Address> { } class Address { } class SelfReferencing { Map<String, SelfReferencingMap> parent; } class SelfReferencingMap { Map<String, SelfReferencingMap> map; } interface CustomMap extends Map<String, Locale> { } interface CustomCollection extends Collection<String> { } public static class GenericConstructors { public GenericConstructors(List<String> first, Locale second) { } } }