// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.collections; import org.junit.Test; import org.mockito.Mockito; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * @author Simon Thoresen Hult */ public class LazyMapTest { @Test public void requireThatInitialDelegateIsEmpty() { LazyMap map = newLazyMap(new HashMap()); assertEquals(LazyMap.EmptyMap.class, map.getDelegate().getClass()); } @Test public void requireThatEmptyMapPutUpgradesToSingletonMap() { LazyMap map = newLazyMap(new HashMap()); assertNull(map.put("foo", "bar")); assertEquals(LazyMap.SingletonMap.class, map.getDelegate().getClass()); map = newLazyMap(new HashMap()); map.putAll(Collections.singletonMap("foo", "bar")); assertEquals(LazyMap.SingletonMap.class, map.getDelegate().getClass()); } @Test public void requireThatEmptyMapPutAllEmptyMapDoesNotUpgradeToSingletonMap() { LazyMap map = newLazyMap(new HashMap()); map.putAll(Collections.emptyMap()); assertEquals(LazyMap.EmptyMap.class, map.getDelegate().getClass()); } @Test public void requireThatEmptyMapPutAllUpgradesToFinalMap() { Map delegate = new HashMap<>(); LazyMap map = newLazyMap(delegate); map.putAll(new HashMapBuilder() .put("foo", "bar") .put("baz", "cox").map); assertSame(delegate, map.getDelegate()); assertEquals(2, delegate.size()); assertEquals("bar", delegate.get("foo")); assertEquals("cox", delegate.get("baz")); } @Test public void requireThatSingletonMapRemoveEntryDowngradesToEmptyMap() { LazyMap map = newSingletonMap("foo", "bar"); assertEquals("bar", map.remove("foo")); assertEquals(LazyMap.EmptyMap.class, map.getDelegate().getClass()); } @Test public void requireThatSingletonMapRemoveUnknownDoesNotDowngradesToEmptyMap() { LazyMap map = newSingletonMap("foo", "bar"); assertNull(map.remove("baz")); assertEquals(LazyMap.SingletonMap.class, map.getDelegate().getClass()); } @Test public void requireThatSingletonMapValueMayBeChangedInPlace() { LazyMap map = newSingletonMap("foo", "bar"); Map delegate = map.getDelegate(); assertEquals("bar", map.put("foo", "baz")); assertEquals("baz", map.get("foo")); assertSame(delegate, map.getDelegate()); map.putAll(Collections.singletonMap("foo", "cox")); assertSame(delegate, map.getDelegate()); assertEquals("cox", map.get("foo")); } @Test public void requireThatSingletonMapPutAllEmptyMapDoesNotUpgradeToFinalMap() { LazyMap map = newSingletonMap("foo", "bar"); map.putAll(Collections.emptyMap()); assertEquals(LazyMap.SingletonMap.class, map.getDelegate().getClass()); } @Test public void requireThatSingletonMapPutUpgradesToFinalMap() { Map delegate = new HashMap<>(); LazyMap map = newSingletonMap(delegate, "fooKey", "fooVal"); map.put("barKey", "barVal"); assertSame(delegate, map.getDelegate()); assertEquals(2, delegate.size()); assertEquals("fooVal", delegate.get("fooKey")); assertEquals("barVal", delegate.get("barKey")); } @Test public void requireThatSingletonMapPutAllUpgradesToFinalMap() { Map delegate = new HashMap<>(); LazyMap map = newSingletonMap(delegate, "fooKey", "fooVal"); map.putAll(new HashMapBuilder() .put("barKey", "barVal") .put("bazKey", "bazVal").map); assertSame(delegate, map.getDelegate()); assertEquals(3, delegate.size()); assertEquals("fooVal", delegate.get("fooKey")); assertEquals("barVal", delegate.get("barKey")); assertEquals("bazVal", delegate.get("bazKey")); } @Test public void requireThatSingletonEntryIsMutable() { LazyMap map = newSingletonMap("foo", "bar"); Map.Entry entry = map.entrySet().iterator().next(); entry.setValue("baz"); assertEquals("baz", map.get("foo")); } @Test public void requireThatSingletonEntryImplementsHashCode() { assertEquals(newSingletonMap("foo", "bar").entrySet().iterator().next().hashCode(), newSingletonMap("foo", "bar").entrySet().iterator().next().hashCode()); } @Test public void requireThatSingletonEntryImplementsEquals() { Map.Entry map = newSingletonMap("foo", "bar").entrySet().iterator().next(); assertNotEquals(map, null); assertNotEquals(map, new Object()); assertEquals(map, map); assertNotEquals(map, newSingletonMap("baz", "cox").entrySet().iterator().next()); assertNotEquals(map, newSingletonMap("foo", "cox").entrySet().iterator().next()); assertEquals(map, newSingletonMap("foo", "bar").entrySet().iterator().next()); } @Test public void requireThatSingletonEntrySetIteratorNextThrowsIfInvokedMoreThanOnce() { LazyMap map = newSingletonMap("foo", "bar"); Iterator> it = map.entrySet().iterator(); it.next(); try { it.next(); fail(); } catch (NoSuchElementException e) { } try { it.next(); fail(); } catch (NoSuchElementException e) { } } @Test public void requireThatSingletonEntrySetIteratorRemoveThrowsIfInvokedBeforeNext() { LazyMap map = newSingletonMap("foo", "bar"); Iterator> it = map.entrySet().iterator(); try { it.remove(); fail(); } catch (IllegalStateException e) { } } @SuppressWarnings("unchecked") private static Map makeMockMap() { return Mockito.mock(Map.class); } @Test public void requireThatMapDelegates() { Map delegate = makeMockMap(); Map map = newLazyMap(delegate); map.put("foo", "bar"); map.put("baz", "cox"); // trigger the assignment of the delegate Mockito.verify(delegate).put("foo", "bar"); Mockito.verify(delegate).put("baz", "cox"); Map arg = Collections.singletonMap("baz", "cox"); map.putAll(arg); Mockito.verify(delegate).putAll(arg); assertEquals(0, map.size()); Mockito.verify(delegate).size(); assertFalse(map.isEmpty()); Mockito.verify(delegate).isEmpty(); assertFalse(map.containsKey("foo")); Mockito.verify(delegate).containsKey("foo"); assertFalse(map.containsValue("bar")); Mockito.verify(delegate).containsValue("bar"); assertNull(map.get("foo")); Mockito.verify(delegate).get("foo"); assertNull(map.remove("foo")); Mockito.verify(delegate).remove("foo"); map.clear(); Mockito.verify(delegate).clear(); assertTrue(map.keySet().isEmpty()); Mockito.verify(delegate).keySet(); assertTrue(map.values().isEmpty()); Mockito.verify(delegate).values(); assertTrue(map.entrySet().isEmpty()); Mockito.verify(delegate).entrySet(); } @Test public void requireThatHashCodeIsImplemented() { assertEquals(newLazyMap(null).hashCode(), newLazyMap(null).hashCode()); } @Test public void requireThatEqualsIsImplemented() { Map lhs = newLazyMap(new HashMap<>()); Map rhs = newLazyMap(new HashMap<>()); assertEquals(lhs, lhs); assertEquals(lhs, rhs); Object key = new Object(); Object val = new Object(); lhs.put(key, val); assertEquals(lhs, lhs); assertFalse(lhs.equals(rhs)); rhs.put(key, val); assertEquals(lhs, rhs); } @Test public void requireThatHashMapFactoryDelegatesToAHashMap() { LazyMap map = LazyMap.newHashMap(); map.put("foo", "bar"); map.put("baz", "cox"); assertEquals(HashMap.class, map.getDelegate().getClass()); } private static LazyMap newSingletonMap(K key, V value) { return newSingletonMap(new HashMap(), key, value); } private static LazyMap newSingletonMap(Map delegate, K key, V value) { LazyMap map = newLazyMap(delegate); map.put(key, value); return map; } private static LazyMap newLazyMap(final Map delegate) { return new LazyMap() { @Override protected Map newDelegate() { return delegate; } }; } private static class HashMapBuilder { final Map map = new HashMap<>(); public HashMapBuilder put(K key, V value) { map.put(key, value); return this; } } }