/* * Copyright 2002-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.core; import java.util.ArrayList; import java.util.Collection; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import org.junit.Test; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.Matchers.empty; import static org.junit.Assert.*; import static org.springframework.core.CollectionFactory.*; /** * Unit tests for {@link CollectionFactory}. * * @author Oliver Gierke * @author Sam Brannen * @since 4.1.4 */ public class CollectionFactoryTests { /** * The test demonstrates that the generics-based API for * {@link CollectionFactory#createApproximateCollection(Object, int)} * is not type-safe. * <p>Specifically, the parameterized type {@code E} is not bound to * the type of elements contained in the {@code collection} argument * passed to {@code createApproximateCollection()}. Thus casting the * value returned by {@link EnumSet#copyOf(EnumSet)} to * {@code (Collection<E>)} cannot guarantee that the returned collection * actually contains elements of type {@code E}. */ @Test public void createApproximateCollectionIsNotTypeSafeForEnumSet() { Collection<Integer> ints = createApproximateCollection(EnumSet.of(Color.BLUE), 3); // Use a try-catch block to ensure that the exception is thrown as a result of the // next line and not as a result of the previous line. try { // Note that ints is of type Collection<Integer>, but the collection returned // by createApproximateCollection() is of type Collection<Color>. Thus, 42 // cannot be cast to a Color. ints.add(42); fail("Should have thrown a ClassCastException"); } catch (ClassCastException e) { /* expected */ } } @Test public void createCollectionIsNotTypeSafeForEnumSet() { Collection<Integer> ints = createCollection(EnumSet.class, Color.class, 3); // Use a try-catch block to ensure that the exception is thrown as a result of the // next line and not as a result of the previous line. try { // Note that ints is of type Collection<Integer>, but the collection returned // by createCollection() is of type Collection<Color>. Thus, 42 cannot be cast // to a Color. ints.add(42); fail("Should have thrown a ClassCastException"); } catch (ClassCastException e) { /* expected */ } } /** * The test demonstrates that the generics-based API for * {@link CollectionFactory#createApproximateMap(Object, int)} * is not type-safe. * <p>The reasoning is similar that described in * {@link #createApproximateCollectionIsNotTypeSafe()}. */ @Test public void createApproximateMapIsNotTypeSafeForEnumMap() { EnumMap<Color, Integer> enumMap = new EnumMap<>(Color.class); enumMap.put(Color.RED, 1); enumMap.put(Color.BLUE, 2); Map<String, Integer> map = createApproximateMap(enumMap, 3); // Use a try-catch block to ensure that the exception is thrown as a result of the // next line and not as a result of the previous line. try { // Note that the 'map' key must be of type String, but the keys in the map // returned by createApproximateMap() are of type Color. Thus "foo" cannot be // cast to a Color. map.put("foo", 1); fail("Should have thrown a ClassCastException"); } catch (ClassCastException e) { /* expected */ } } @Test public void createMapIsNotTypeSafeForEnumMap() { Map<String, Integer> map = createMap(EnumMap.class, Color.class, 3); // Use a try-catch block to ensure that the exception is thrown as a result of the // next line and not as a result of the previous line. try { // Note that the 'map' key must be of type String, but the keys in the map // returned by createMap() are of type Color. Thus "foo" cannot be cast to a // Color. map.put("foo", 1); fail("Should have thrown a ClassCastException"); } catch (ClassCastException e) { /* expected */ } } @Test public void createMapIsNotTypeSafeForLinkedMultiValueMap() { Map<String, Integer> map = createMap(MultiValueMap.class, null, 3); // Use a try-catch block to ensure that the exception is thrown as a result of the // next line and not as a result of the previous line. try { // Note: 'map' values must be of type Integer, but the values in the map // returned by createMap() are of type java.util.List. Thus 1 cannot be // cast to a List. map.put("foo", 1); fail("Should have thrown a ClassCastException"); } catch (ClassCastException e) { /* expected */ } } @Test public void createApproximateCollectionFromEmptyHashSet() { Collection<String> set = createApproximateCollection(new HashSet<String>(), 2); assertThat(set, is(empty())); } @Test public void createApproximateCollectionFromNonEmptyHashSet() { HashSet<String> hashSet = new HashSet<>(); hashSet.add("foo"); Collection<String> set = createApproximateCollection(hashSet, 2); assertThat(set, is(empty())); } @Test public void createApproximateCollectionFromEmptyEnumSet() { Collection<Color> colors = createApproximateCollection(EnumSet.noneOf(Color.class), 2); assertThat(colors, is(empty())); } @Test public void createApproximateCollectionFromNonEmptyEnumSet() { Collection<Color> colors = createApproximateCollection(EnumSet.of(Color.BLUE), 2); assertThat(colors, is(empty())); } @Test public void createApproximateMapFromEmptyHashMap() { Map<String, String> map = createApproximateMap(new HashMap<String, String>(), 2); assertThat(map.size(), is(0)); } @Test public void createApproximateMapFromNonEmptyHashMap() { Map<String, String> hashMap = new HashMap<>(); hashMap.put("foo", "bar"); Map<String, String> map = createApproximateMap(hashMap, 2); assertThat(map.size(), is(0)); } @Test public void createApproximateMapFromEmptyEnumMap() { Map<Color, String> colors = createApproximateMap(new EnumMap<Color, String>(Color.class), 2); assertThat(colors.size(), is(0)); } @Test public void createApproximateMapFromNonEmptyEnumMap() { EnumMap<Color, String> enumMap = new EnumMap<>(Color.class); enumMap.put(Color.BLUE, "blue"); Map<Color, String> colors = createApproximateMap(enumMap, 2); assertThat(colors.size(), is(0)); } @Test public void createsCollectionsCorrectly() { // interfaces assertThat(createCollection(List.class, 0), is(instanceOf(ArrayList.class))); assertThat(createCollection(Set.class, 0), is(instanceOf(LinkedHashSet.class))); assertThat(createCollection(Collection.class, 0), is(instanceOf(LinkedHashSet.class))); assertThat(createCollection(SortedSet.class, 0), is(instanceOf(TreeSet.class))); assertThat(createCollection(NavigableSet.class, 0), is(instanceOf(TreeSet.class))); assertThat(createCollection(List.class, String.class, 0), is(instanceOf(ArrayList.class))); assertThat(createCollection(Set.class, String.class, 0), is(instanceOf(LinkedHashSet.class))); assertThat(createCollection(Collection.class, String.class, 0), is(instanceOf(LinkedHashSet.class))); assertThat(createCollection(SortedSet.class, String.class, 0), is(instanceOf(TreeSet.class))); assertThat(createCollection(NavigableSet.class, String.class, 0), is(instanceOf(TreeSet.class))); // concrete types assertThat(createCollection(HashSet.class, 0), is(instanceOf(HashSet.class))); assertThat(createCollection(HashSet.class, String.class, 0), is(instanceOf(HashSet.class))); } @Test public void createsEnumSet() { assertThat(createCollection(EnumSet.class, Color.class, 0), is(instanceOf(EnumSet.class))); } @Test(expected = IllegalArgumentException.class) public void rejectsInvalidElementTypeForEnumSet() { createCollection(EnumSet.class, Object.class, 0); } @Test(expected = IllegalArgumentException.class) public void rejectsNullElementTypeForEnumSet() { createCollection(EnumSet.class, null, 0); } @Test(expected = IllegalArgumentException.class) public void rejectsNullCollectionType() { createCollection(null, Object.class, 0); } @Test public void createsMapsCorrectly() { // interfaces assertThat(createMap(Map.class, 0), is(instanceOf(LinkedHashMap.class))); assertThat(createMap(SortedMap.class, 0), is(instanceOf(TreeMap.class))); assertThat(createMap(NavigableMap.class, 0), is(instanceOf(TreeMap.class))); assertThat(createMap(MultiValueMap.class, 0), is(instanceOf(LinkedMultiValueMap.class))); assertThat(createMap(Map.class, String.class, 0), is(instanceOf(LinkedHashMap.class))); assertThat(createMap(SortedMap.class, String.class, 0), is(instanceOf(TreeMap.class))); assertThat(createMap(NavigableMap.class, String.class, 0), is(instanceOf(TreeMap.class))); assertThat(createMap(MultiValueMap.class, String.class, 0), is(instanceOf(LinkedMultiValueMap.class))); // concrete types assertThat(createMap(HashMap.class, 0), is(instanceOf(HashMap.class))); assertThat(createMap(HashMap.class, String.class, 0), is(instanceOf(HashMap.class))); } @Test public void createsEnumMap() { assertThat(createMap(EnumMap.class, Color.class, 0), is(instanceOf(EnumMap.class))); } @Test(expected = IllegalArgumentException.class) public void rejectsInvalidKeyTypeForEnumMap() { createMap(EnumMap.class, Object.class, 0); } @Test(expected = IllegalArgumentException.class) public void rejectsNullKeyTypeForEnumMap() { createMap(EnumMap.class, null, 0); } @Test(expected = IllegalArgumentException.class) public void rejectsNullMapType() { createMap(null, Object.class, 0); } static enum Color { RED, BLUE; } }