1/*
2 * Copyright (C) 2012 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.reflect;
18
19import static com.google.common.truth.Truth.assertThat;
20
21import com.google.common.collect.ImmutableList;
22import com.google.common.collect.ImmutableMap;
23import com.google.common.collect.testing.MapTestSuiteBuilder;
24import com.google.common.collect.testing.features.CollectionFeature;
25import com.google.common.collect.testing.features.CollectionSize;
26import com.google.common.collect.testing.features.MapFeature;
27import com.google.common.collect.testing.testers.MapPutTester;
28import com.google.common.reflect.ImmutableTypeToInstanceMapTest.TestTypeToInstanceMapGenerator;
29
30import junit.framework.Test;
31import junit.framework.TestCase;
32import junit.framework.TestSuite;
33
34import java.lang.reflect.Method;
35import java.util.Map;
36import java.util.Map.Entry;
37
38/**
39 * Unit test of {@link MutableTypeToInstanceMap}.
40 *
41 * @author Ben Yu
42 */
43public class MutableTypeToInstanceMapTest extends TestCase {
44
45  public static Test suite() {
46    TestSuite suite = new TestSuite();
47    suite.addTestSuite(MutableTypeToInstanceMapTest.class);
48
49    // Suppress this one because the tester framework doesn't understand that
50    // *some* remappings will be allowed and others not.
51    Method remapTest = null;
52    try {
53      remapTest = MapPutTester.class.getMethod(
54          "testPut_replaceNullValueWithNonNullSupported");
55    } catch (NoSuchMethodException e) {
56      throw new AssertionError();
57    }
58
59    suite.addTest(MapTestSuiteBuilder
60        .using(new TestTypeToInstanceMapGenerator() {
61          // Other tests will verify what real, warning-free usage looks like
62          // but here we have to do some serious fudging
63          @Override
64          @SuppressWarnings("unchecked")
65          public Map<TypeToken, Object> create(Object... elements) {
66            MutableTypeToInstanceMap<Object> map
67                = new MutableTypeToInstanceMap<Object>();
68            for (Object object : elements) {
69              Entry<TypeToken, Object> entry = (Entry<TypeToken, Object>) object;
70              map.putInstance(entry.getKey(), entry.getValue());
71            }
72            return (Map) map;
73          }
74        })
75        .named("MutableTypeToInstanceMap")
76        .withFeatures(
77            MapFeature.SUPPORTS_REMOVE,
78            MapFeature.RESTRICTS_KEYS,
79            MapFeature.ALLOWS_NULL_VALUES,
80            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
81            CollectionSize.ANY,
82            MapFeature.ALLOWS_ANY_NULL_QUERIES)
83        .suppressing(remapTest)
84        .createTestSuite());
85
86    return suite;
87  }
88
89  private TypeToInstanceMap<Object> map;
90
91  @Override protected void setUp() throws Exception {
92    map = new MutableTypeToInstanceMap<Object>();
93  }
94
95  public void testPutThrows() {
96    try {
97      map.put(TypeToken.of(Integer.class), new Integer(5));
98      fail();
99    } catch (UnsupportedOperationException expected) {}
100  }
101
102  public void testPutAllThrows() {
103    try {
104      map.putAll(ImmutableMap.of(TypeToken.of(Integer.class), new Integer(5)));
105      fail();
106    } catch (UnsupportedOperationException expected) {}
107  }
108
109  public void testEntrySetMutationThrows() {
110    map.putInstance(String.class, "test");
111    assertEquals(TypeToken.of(String.class), map.entrySet().iterator().next().getKey());
112    assertEquals("test", map.entrySet().iterator().next().getValue());
113    try {
114      map.entrySet().iterator().next().setValue(1);
115      fail();
116    } catch (UnsupportedOperationException expected) {}
117  }
118
119  public void testEntrySetToArrayMutationThrows() {
120    map.putInstance(String.class, "test");
121    @SuppressWarnings("unchecked") // Should get a CCE later if cast is wrong
122    Entry<Object, Object> entry = (Entry<Object, Object>) map.entrySet().toArray()[0];
123    assertEquals(TypeToken.of(String.class), entry.getKey());
124    assertEquals("test", entry.getValue());
125    try {
126      entry.setValue(1);
127      fail();
128    } catch (UnsupportedOperationException expected) {}
129  }
130
131  public void testEntrySetToTypedArrayMutationThrows() {
132    map.putInstance(String.class, "test");
133    @SuppressWarnings("unchecked") // Should get a CCE later if cast is wrong
134    Entry<Object, Object> entry = map.entrySet().toArray(new Entry[0])[0];
135    assertEquals(TypeToken.of(String.class), entry.getKey());
136    assertEquals("test", entry.getValue());
137    try {
138      entry.setValue(1);
139      fail();
140    } catch (UnsupportedOperationException expected) {}
141  }
142
143  public void testPutAndGetInstance() {
144    assertNull(map.putInstance(Integer.class, new Integer(5)));
145
146    Integer oldValue = map.putInstance(Integer.class, new Integer(7));
147    assertEquals(5, (int) oldValue);
148
149    Integer newValue = map.getInstance(Integer.class);
150    assertEquals(7, (int) newValue);
151    assertEquals(7, (int) map.getInstance(TypeToken.of(Integer.class)));
152
153    // Won't compile: map.putInstance(Double.class, new Long(42));
154  }
155
156  public void testNull() {
157    try {
158      map.putInstance((TypeToken) null, new Integer(1));
159      fail();
160    } catch (NullPointerException expected) {
161    }
162    map.putInstance(Integer.class, null);
163    assertNull(map.get(Integer.class));
164    assertNull(map.getInstance(Integer.class));
165
166    map.putInstance(Long.class, null);
167    assertNull(map.get(Long.class));
168    assertNull(map.getInstance(Long.class));
169  }
170
171  public void testPrimitiveAndWrapper() {
172    assertNull(map.getInstance(int.class));
173    assertNull(map.getInstance(Integer.class));
174
175    assertNull(map.putInstance(int.class, 0));
176    assertNull(map.putInstance(Integer.class, 1));
177    assertEquals(2, map.size());
178
179    assertEquals(0, (int) map.getInstance(int.class));
180    assertEquals(1, (int) map.getInstance(Integer.class));
181
182    assertEquals(0, (int) map.putInstance(int.class, null));
183    assertEquals(1, (int) map.putInstance(Integer.class, null));
184
185    assertNull(map.getInstance(int.class));
186    assertNull(map.getInstance(Integer.class));
187    assertEquals(2, map.size());
188  }
189
190  public void testParameterizedType() {
191    TypeToken<ImmutableList<Integer>> type = new TypeToken<ImmutableList<Integer>>() {};
192    map.putInstance(type, ImmutableList.of(1));
193    assertEquals(1, map.size());
194    assertEquals(ImmutableList.of(1), map.getInstance(type));
195  }
196
197  public void testGenericArrayType() {
198    @SuppressWarnings("unchecked") // Trying to test generic array
199    ImmutableList<Integer>[] array = new ImmutableList[] {ImmutableList.of(1)};
200    TypeToken<ImmutableList<Integer>[]> type = new TypeToken<ImmutableList<Integer>[]>() {};
201    map.putInstance(type, array);
202    assertEquals(1, map.size());
203    assertThat(map.getInstance(type)).asList().has().exactly(array[0]).inOrder();
204  }
205
206  public void testWildcardType() {
207    TypeToken<ImmutableList<?>> type = new TypeToken<ImmutableList<?>>() {};
208    map.putInstance(type, ImmutableList.of(1));
209    assertEquals(1, map.size());
210    assertEquals(ImmutableList.of(1), map.getInstance(type));
211  }
212
213  public void testGetInstance_withTypeVariable() {
214    try {
215      map.getInstance(this.<Number>anyIterableType());
216      fail();
217    } catch (IllegalArgumentException expected) {}
218  }
219
220  public void testPutInstance_withTypeVariable() {
221    try {
222      map.putInstance(this.<Integer>anyIterableType(), ImmutableList.of(1));
223      fail();
224    } catch (IllegalArgumentException expected) {}
225  }
226
227  private <T> TypeToken<Iterable<T>> anyIterableType() {
228    return new TypeToken<Iterable<T>>() {};
229  }
230}
231