1/*
2 * Copyright (C) 2009 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.collect;
18import static org.easymock.EasyMock.anyObject;
19import static org.easymock.EasyMock.createMock;
20import static org.easymock.EasyMock.expect;
21import static org.easymock.EasyMock.expectLastCall;
22import static org.easymock.EasyMock.replay;
23import static org.easymock.EasyMock.verify;
24
25import com.google.common.collect.testing.MapTestSuiteBuilder;
26import com.google.common.collect.testing.TestStringMapGenerator;
27import com.google.common.collect.testing.features.CollectionFeature;
28import com.google.common.collect.testing.features.CollectionSize;
29import com.google.common.collect.testing.features.MapFeature;
30
31import junit.framework.Test;
32import junit.framework.TestSuite;
33
34import java.lang.reflect.InvocationTargetException;
35import java.util.Collection;
36import java.util.HashMap;
37import java.util.Iterator;
38import java.util.Map;
39import java.util.Map.Entry;
40import java.util.Set;
41
42/**
43 * Unit test for {@link ForwardingMap}.
44 *
45 * @author Hayward Chan
46 * @author Louis Wasserman
47 */
48public class ForwardingMapTest extends ForwardingTestCase {
49  static class StandardImplForwardingMap<K, V> extends ForwardingMap<K, V> {
50    private final Map<K, V> backingMap;
51
52    StandardImplForwardingMap(Map<K, V> backingMap) {
53      this.backingMap = backingMap;
54    }
55
56    @Override protected Map<K, V> delegate() {
57      return backingMap;
58    }
59
60    @Override public boolean containsKey(Object key) {
61      return standardContainsKey(key);
62    }
63
64    @Override public boolean containsValue(Object value) {
65      return standardContainsValue(value);
66    }
67
68    @Override public void putAll(Map<? extends K, ? extends V> map) {
69      standardPutAll(map);
70    }
71
72    @Override public V remove(Object object) {
73      return standardRemove(object);
74    }
75
76    @Override public boolean equals(Object object) {
77      return standardEquals(object);
78    }
79
80    @Override public int hashCode() {
81      return standardHashCode();
82    }
83
84    @Override public Set<K> keySet() {
85      return new StandardKeySet();
86    }
87
88    @Override public Collection<V> values() {
89      return new StandardValues();
90    }
91
92    @Override public String toString() {
93      return standardToString();
94    }
95
96    @Override public Set<Entry<K, V>> entrySet() {
97      return new StandardEntrySet() {
98        @Override
99        public Iterator<Entry<K, V>> iterator() {
100          return delegate()
101              .entrySet()
102              .iterator();
103        }
104      };
105    }
106
107    @Override public void clear() {
108      standardClear();
109    }
110
111    @Override public boolean isEmpty() {
112      return standardIsEmpty();
113    }
114  }
115
116  Map<String, Boolean> forward;
117
118  public static Test suite() {
119    TestSuite suite = new TestSuite();
120
121    suite.addTestSuite(ForwardingMapTest.class);
122    suite.addTest(MapTestSuiteBuilder.using(new TestStringMapGenerator() {
123
124      @Override protected Map<String, String> create(
125          Entry<String, String>[] entries) {
126        Map<String, String> map = Maps.newLinkedHashMap();
127        for (Entry<String, String> entry : entries) {
128          map.put(entry.getKey(), entry.getValue());
129        }
130        return new StandardImplForwardingMap<String, String>(map);
131      }
132
133    }).named("ForwardingMap[LinkedHashMap] with standard implementations")
134        .withFeatures(CollectionSize.ANY, MapFeature.ALLOWS_NULL_VALUES,
135            MapFeature.ALLOWS_NULL_KEYS, MapFeature.ALLOWS_ANY_NULL_QUERIES,
136            MapFeature.GENERAL_PURPOSE,
137            CollectionFeature.SUPPORTS_ITERATOR_REMOVE, CollectionFeature.KNOWN_ORDER)
138        .createTestSuite());
139    suite.addTest(MapTestSuiteBuilder.using(new TestStringMapGenerator() {
140
141      @Override protected Map<String, String> create(
142          Entry<String, String>[] entries) {
143        ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
144        for (Entry<String, String> entry : entries) {
145          builder.put(entry.getKey(), entry.getValue());
146        }
147        return new StandardImplForwardingMap<String, String>(builder.build());
148      }
149
150    }).named("ForwardingMap[ImmutableMap] with standard implementations")
151        .withFeatures(
152            CollectionSize.ANY, MapFeature.REJECTS_DUPLICATES_AT_CREATION,
153            MapFeature.ALLOWS_ANY_NULL_QUERIES,
154            CollectionFeature.KNOWN_ORDER)
155        .createTestSuite());
156
157    return suite;
158  }
159
160  @Override public void setUp() throws Exception {
161    super.setUp();
162    /*
163     * Class parameters must be raw, so we can't create a proxy with generic
164     * type arguments. The created proxy only records calls and returns null, so
165     * the type is irrelevant at runtime.
166     */
167    @SuppressWarnings("unchecked")
168    final Map<String, Boolean> map = createProxyInstance(Map.class);
169    forward = new ForwardingMap<String, Boolean>() {
170      @Override protected Map<String, Boolean> delegate() {
171        return map;
172      }
173    };
174  }
175
176  public void testSize() {
177    forward().size();
178    assertEquals("[size]", getCalls());
179  }
180
181  public void testIsEmpty() {
182    forward().isEmpty();
183    assertEquals("[isEmpty]", getCalls());
184  }
185
186  public void testRemove() {
187    forward().remove(null);
188    assertEquals("[remove(Object)]", getCalls());
189  }
190
191  public void testClear() {
192    forward().clear();
193    assertEquals("[clear]", getCalls());
194  }
195
196  public void testContainsKey() {
197    forward().containsKey("asdf");
198    assertEquals("[containsKey(Object)]", getCalls());
199  }
200
201  public void testContainsValue() {
202    forward().containsValue(false);
203    assertEquals("[containsValue(Object)]", getCalls());
204  }
205
206  public void testGet_Object() {
207    forward().get("asdf");
208    assertEquals("[get(Object)]", getCalls());
209  }
210
211  public void testPut_Key_Value() {
212    forward().put("key", false);
213    assertEquals("[put(Object,Object)]", getCalls());
214  }
215
216  public void testPutAll_Map() {
217    forward().putAll(new HashMap<String, Boolean>());
218    assertEquals("[putAll(Map)]", getCalls());
219  }
220
221  public void testKeySet() {
222    forward().keySet();
223    assertEquals("[keySet]", getCalls());
224  }
225
226  public void testValues() {
227    forward().values();
228    assertEquals("[values]", getCalls());
229  }
230
231  public void testEntrySet() {
232    forward().entrySet();
233    assertEquals("[entrySet]", getCalls());
234  }
235
236  public void testToString() {
237    forward().toString();
238    assertEquals("[toString]", getCalls());
239  }
240
241  public void testEquals_Object() {
242    forward().equals("asdf");
243    assertEquals("[equals(Object)]", getCalls());
244  }
245
246  public void testHashCode() {
247    forward().hashCode();
248    assertEquals("[hashCode]", getCalls());
249  }
250
251  public void testStandardEntrySet() throws InvocationTargetException {
252    @SuppressWarnings("unchecked")
253    final Map<String, Boolean> map = createMock(Map.class);
254    @SuppressWarnings("unchecked")
255    final Set<Map.Entry<String, Boolean>> entrySet = createMock(Set.class);
256    expect(map.containsKey(anyObject())).andReturn(false).anyTimes();
257    expect(map.get(anyObject())).andReturn(null).anyTimes();
258    expect(map.isEmpty()).andReturn(true).anyTimes();
259    expect(map.remove(anyObject())).andReturn(null).anyTimes();
260    expect(map.size()).andReturn(0).anyTimes();
261    expect(entrySet.iterator())
262        .andReturn(Iterators.<Entry<String, Boolean>>emptyIterator())
263        .anyTimes();
264    map.clear();
265    expectLastCall().anyTimes();
266
267    replay(map, entrySet);
268
269    Map<String, Boolean> forward = new ForwardingMap<String, Boolean>() {
270      @Override protected Map<String, Boolean> delegate() {
271        return map;
272      }
273
274      @Override public Set<Entry<String, Boolean>> entrySet() {
275        return new StandardEntrySet() {
276          @Override
277          public Iterator<Entry<String, Boolean>> iterator() {
278            return entrySet.iterator();
279          }
280        };
281      }
282    };
283    callAllPublicMethods(Set.class, forward.entrySet());
284
285    verify(map, entrySet);
286  }
287
288  public void testStandardKeySet() throws InvocationTargetException {
289    @SuppressWarnings("unchecked")
290    Set<Entry<String, Boolean>> entrySet = createMock(Set.class);
291    expect(entrySet.iterator()).andReturn(
292        Iterators.<Entry<String, Boolean>>emptyIterator()).anyTimes();
293
294    @SuppressWarnings("unchecked")
295    final Map<String, Boolean> map = createMock(Map.class);
296    expect(map.containsKey(anyObject())).andReturn(false).anyTimes();
297    expect(map.isEmpty()).andReturn(true).anyTimes();
298    expect(map.remove(anyObject())).andReturn(null).anyTimes();
299    expect(map.size()).andReturn(0).anyTimes();
300    expect(map.entrySet()).andReturn(entrySet).anyTimes();
301    map.clear();
302    expectLastCall().anyTimes();
303
304    replay(entrySet, map);
305
306    Map<String, Boolean> forward = new ForwardingMap<String, Boolean>() {
307      @Override protected Map<String, Boolean> delegate() {
308        return map;
309      }
310
311      @Override public Set<String> keySet() {
312        return new StandardKeySet();
313      }
314    };
315    callAllPublicMethods(Set.class, forward.keySet());
316
317    verify(entrySet, map);
318  }
319
320  public void testStandardValues() throws InvocationTargetException {
321    @SuppressWarnings("unchecked")
322    Set<Entry<String, Boolean>> entrySet = createMock(Set.class);
323    expect(entrySet.iterator()).andReturn(
324        Iterators.<Entry<String, Boolean>>emptyIterator()).anyTimes();
325
326    @SuppressWarnings("unchecked")
327    final Map<String, Boolean> map = createMock(Map.class);
328    expect(map.containsValue(anyObject())).andReturn(false).anyTimes();
329    expect(map.isEmpty()).andReturn(true).anyTimes();
330    expect(map.size()).andReturn(0).anyTimes();
331    expect(map.entrySet()).andReturn(entrySet).anyTimes();
332    map.clear();
333    expectLastCall().anyTimes();
334
335    replay(entrySet, map);
336
337    Map<String, Boolean> forward = new ForwardingMap<String, Boolean>() {
338      @Override protected Map<String, Boolean> delegate() {
339        return map;
340      }
341
342      @Override public Collection<Boolean> values() {
343        return new StandardValues();
344      }
345    };
346    callAllPublicMethods(Collection.class, forward.values());
347
348    verify(entrySet, map);
349  }
350
351  public void testToStringWithNullKeys() throws Exception {
352    Map<String, String> hashmap = Maps.newHashMap();
353    hashmap.put("foo", "bar");
354    hashmap.put(null, "baz");
355
356    StandardImplForwardingMap<String, String> forwardingMap =
357        new StandardImplForwardingMap<String, String>(
358            Maps.<String, String>newHashMap());
359    forwardingMap.put("foo", "bar");
360    forwardingMap.put(null, "baz");
361
362    assertEquals(hashmap.toString(), forwardingMap.toString());
363  }
364
365  public void testToStringWithNullValues() throws Exception {
366    Map<String, String> hashmap = Maps.newHashMap();
367    hashmap.put("foo", "bar");
368    hashmap.put("baz", null);
369
370    StandardImplForwardingMap<String, String> forwardingMap =
371        new StandardImplForwardingMap<String, String>(
372            Maps.<String, String>newHashMap());
373    forwardingMap.put("foo", "bar");
374    forwardingMap.put("baz", null);
375
376    assertEquals(hashmap.toString(), forwardingMap.toString());
377  }
378
379  Map<String, Boolean> forward() {
380    return forward;
381  }
382}
383