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