Repository: logging-log4j2 Updated Branches: refs/heads/master e1902e97f -> d16716a58
LOG4J2-1611 productionize JdkMapAdapterStringMap (null checks, toString/equals/hashCode); allow mutation; add unit tests Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/d16716a5 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/d16716a5 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/d16716a5 Branch: refs/heads/master Commit: d16716a58e6bba87f7874f23dfc43db41edbf1b6 Parents: e1902e9 Author: rpopma <[email protected]> Authored: Sat Sep 24 22:37:53 2016 +0900 Committer: rpopma <[email protected]> Committed: Sat Sep 24 22:37:53 2016 +0900 ---------------------------------------------------------------------- .../log4j/core/impl/JdkMapAdapterStringMap.java | 69 +- .../core/impl/JdkMapAdapterStringMapTest.java | 882 +++++++++++++++++++ 2 files changed, 942 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d16716a5/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMap.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMap.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMap.java index b9387b9a..504b194 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMap.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMap.java @@ -16,7 +16,9 @@ */ package org.apache.logging.log4j.core.impl; +import java.util.HashMap; import java.util.Map; +import java.util.Objects; import org.apache.logging.log4j.util.BiConsumer; import org.apache.logging.log4j.util.ReadOnlyStringMap; @@ -28,10 +30,17 @@ import org.apache.logging.log4j.util.TriConsumer; */ class JdkMapAdapterStringMap implements StringMap { private static final long serialVersionUID = -7348247784983193612L; - private Map<String, String> map; + private static final String FROZEN = "Frozen collection cannot be modified"; + + private final Map<String, String> map; + private boolean immutable = false; + + public JdkMapAdapterStringMap() { + this(new HashMap<String, String>()); + } public JdkMapAdapterStringMap(final Map<String, String> map) { - this.map = map; + this.map = Objects.requireNonNull(map, "map"); } @Override @@ -39,6 +48,12 @@ class JdkMapAdapterStringMap implements StringMap { return map; } + private void assertNotFrozen() { + if (immutable) { + throw new UnsupportedOperationException(FROZEN); + } + } + @Override public boolean containsKey(final String key) { return map.containsKey(key); @@ -78,34 +93,70 @@ class JdkMapAdapterStringMap implements StringMap { @Override public void clear() { - fail(); + if (map.isEmpty()) { + return; + } + assertNotFrozen(); + map.clear(); } @Override public void freeze() { + immutable = true; } @Override public boolean isFrozen() { - return true; + return immutable; } @Override public void putAll(final ReadOnlyStringMap source) { - fail(); + assertNotFrozen(); + source.forEach(PUT_ALL, map); } + private static TriConsumer<String, String, Map<String, String>> PUT_ALL = new TriConsumer<String, String, Map<String, String>>() { + @Override + public void accept(final String key, final String value, final Map<String, String> stringStringMap) { + stringStringMap.put(key, value); + } + }; + @Override public void putValue(final String key, final Object value) { - fail(); + assertNotFrozen(); + map.put(key, value == null ? null : String.valueOf(value)); } @Override public void remove(final String key) { - fail(); + if (!map.containsKey(key)) { + return; + } + assertNotFrozen(); + map.remove(key); } - private void fail() { - throw new UnsupportedOperationException("This is a read-only data structure"); + @Override + public String toString() { + return map.toString(); + } + + @Override + public boolean equals(final Object object) { + if (object == this) { + return true; + } + if (!(object instanceof JdkMapAdapterStringMap)) { + return false; + } + JdkMapAdapterStringMap other = (JdkMapAdapterStringMap) object; + return map.equals(other.map) && immutable == other.immutable; + } + + @Override + public int hashCode() { + return map.hashCode() + (immutable ? 31 : 0); } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d16716a5/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMapTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMapTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMapTest.java new file mode 100644 index 0000000..482d399 --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/JdkMapAdapterStringMapTest.java @@ -0,0 +1,882 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.logging.log4j.core.impl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ConcurrentModificationException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.logging.log4j.util.BiConsumer; +import org.apache.logging.log4j.util.TriConsumer; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Tests the JdkMapAdapterStringMap class. + */ +public class JdkMapAdapterStringMapTest { + + @Test(expected = NullPointerException.class) + public void testConstructorDisallowsNull() throws Exception { + new JdkMapAdapterStringMap(null); + } + + @Test + public void testToString() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + assertEquals("{3=3value, B=Bvalue, a=avalue}", original.toString()); + } + + @Test + public void testSerialization() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + + final byte[] binary = serialize(original); + final JdkMapAdapterStringMap copy = deserialize(binary); + assertEquals(original, copy); + } + + private byte[] serialize(final JdkMapAdapterStringMap data) throws IOException { + final ByteArrayOutputStream arr = new ByteArrayOutputStream(); + final ObjectOutputStream out = new ObjectOutputStream(arr); + out.writeObject(data); + return arr.toByteArray(); + } + + private JdkMapAdapterStringMap deserialize(final byte[] binary) throws IOException, ClassNotFoundException { + final ByteArrayInputStream inArr = new ByteArrayInputStream(binary); + final ObjectInputStream in = new ObjectInputStream(inArr); + final JdkMapAdapterStringMap result = (JdkMapAdapterStringMap) in.readObject(); + return result; + } + + @Test + public void testPutAll() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putAll(original); + assertEquals(original, other); + + other.putValue("3", "otherValue"); + assertNotEquals(original, other); + + other.putValue("3", null); + assertNotEquals(original, other); + + other.putValue("3", "3value"); + assertEquals(original, other); + } + + @Test + public void testPutAll_overwritesSameKeys2() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aORIG"); + original.putValue("b", "bORIG"); + original.putValue("c", "cORIG"); + original.putValue("d", "dORIG"); + original.putValue("e", "eORIG"); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue("1", "11"); + other.putValue("2", "22"); + other.putValue("a", "aa"); + other.putValue("c", "cc"); + original.putAll(other); + + assertEquals("size after put other", 7, original.size()); + assertEquals("aa", original.getValue("a")); + assertEquals("bORIG", original.getValue("b")); + assertEquals("cc", original.getValue("c")); + assertEquals("dORIG", original.getValue("d")); + assertEquals("eORIG", original.getValue("e")); + assertEquals("11", original.getValue("1")); + assertEquals("22", original.getValue("2")); + } + + @Test + public void testPutAll_nullKeyInLargeOriginal() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue(null, "nullORIG"); + original.putValue("a", "aORIG"); + original.putValue("b", "bORIG"); + original.putValue("c", "cORIG"); + original.putValue("d", "dORIG"); + original.putValue("e", "eORIG"); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue("1", "11"); + other.putValue("a", "aa"); + original.putAll(other); + + assertEquals("size after put other", 7, original.size()); + assertEquals("aa", original.getValue("a")); + assertEquals("bORIG", original.getValue("b")); + assertEquals("cORIG", original.getValue("c")); + assertEquals("dORIG", original.getValue("d")); + assertEquals("eORIG", original.getValue("e")); + assertEquals("11", original.getValue("1")); + assertEquals("nullORIG", original.getValue(null)); + } + + @Test + public void testPutAll_nullKeyInSmallOriginal() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue(null, "nullORIG"); + original.putValue("a", "aORIG"); + original.putValue("b", "bORIG"); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue("1", "11"); + other.putValue("2", "22"); + other.putValue("3", "33"); + other.putValue("a", "aa"); + original.putAll(other); + + assertEquals("size after put other", 6, original.size()); + assertEquals("aa", original.getValue("a")); + assertEquals("bORIG", original.getValue("b")); + assertEquals("11", original.getValue("1")); + assertEquals("22", original.getValue("2")); + assertEquals("33", original.getValue("3")); + assertEquals("nullORIG", original.getValue(null)); + } + + @Test + public void testPutAll_nullKeyInSmallAdditional() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aORIG"); + original.putValue("b", "bORIG"); + original.putValue("c", "cORIG"); + original.putValue("d", "dORIG"); + original.putValue("e", "eORIG"); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue(null, "nullNEW"); + other.putValue("1", "11"); + other.putValue("a", "aa"); + original.putAll(other); + + assertEquals("size after put other", 7, original.size()); + assertEquals("aa", original.getValue("a")); + assertEquals("bORIG", original.getValue("b")); + assertEquals("cORIG", original.getValue("c")); + assertEquals("dORIG", original.getValue("d")); + assertEquals("eORIG", original.getValue("e")); + assertEquals("11", original.getValue("1")); + assertEquals("nullNEW", original.getValue(null)); + } + + @Test + public void testPutAll_nullKeyInLargeAdditional() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aORIG"); + original.putValue("b", "bORIG"); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue(null, "nullNEW"); + other.putValue("1", "11"); + other.putValue("2", "22"); + other.putValue("3", "33"); + other.putValue("a", "aa"); + original.putAll(other); + + assertEquals("size after put other", 6, original.size()); + assertEquals("aa", original.getValue("a")); + assertEquals("bORIG", original.getValue("b")); + assertEquals("11", original.getValue("1")); + assertEquals("22", original.getValue("2")); + assertEquals("33", original.getValue("3")); + assertEquals("nullNEW", original.getValue(null)); + } + + @Test + public void testPutAll_nullKeyInBoth_LargeOriginal() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue(null, "nullORIG"); + original.putValue("a", "aORIG"); + original.putValue("b", "bORIG"); + original.putValue("c", "cORIG"); + original.putValue("d", "dORIG"); + original.putValue("e", "eORIG"); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue(null, "nullNEW"); + other.putValue("1", "11"); + other.putValue("a", "aa"); + original.putAll(other); + + assertEquals("size after put other", 7, original.size()); + assertEquals("aa", original.getValue("a")); + assertEquals("bORIG", original.getValue("b")); + assertEquals("cORIG", original.getValue("c")); + assertEquals("dORIG", original.getValue("d")); + assertEquals("eORIG", original.getValue("e")); + assertEquals("11", original.getValue("1")); + assertEquals("nullNEW", original.getValue(null)); + } + + @Test + public void testPutAll_nullKeyInBoth_SmallOriginal() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue(null, "nullORIG"); + original.putValue("a", "aORIG"); + original.putValue("b", "bORIG"); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue(null, "nullNEW"); + other.putValue("1", "11"); + other.putValue("2", "22"); + other.putValue("3", "33"); + other.putValue("a", "aa"); + original.putAll(other); + + assertEquals("size after put other", 6, original.size()); + assertEquals("aa", original.getValue("a")); + assertEquals("bORIG", original.getValue("b")); + assertEquals("11", original.getValue("1")); + assertEquals("22", original.getValue("2")); + assertEquals("33", original.getValue("3")); + assertEquals("nullNEW", original.getValue(null)); + } + + @Test + public void testPutAll_overwritesSameKeys1() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aORIG"); + original.putValue("b", "bORIG"); + original.putValue("c", "cORIG"); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue("1", "11"); + other.putValue("2", "22"); + other.putValue("a", "aa"); + other.putValue("c", "cc"); + original.putAll(other); + + assertEquals("size after put other", 5, original.size()); + assertEquals("aa", original.getValue("a")); + assertEquals("bORIG", original.getValue("b")); + assertEquals("cc", original.getValue("c")); + assertEquals("11", original.getValue("1")); + assertEquals("22", original.getValue("2")); + } + + @Test + public void testEquals() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + assertEquals(original, original); // equal to itself + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue("a", "avalue"); + assertNotEquals(original, other); + + other.putValue("B", "Bvalue"); + assertNotEquals(original, other); + + other.putValue("3", "3value"); + assertEquals(original, other); + + other.putValue("3", "otherValue"); + assertNotEquals(original, other); + + other.putValue("3", null); + assertNotEquals(original, other); + + other.putValue("3", "3value"); + assertEquals(original, other); + } + + @Test + public void testToMap() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + + final Map<String, Object> expected = new HashMap<>(); + expected.put("a", "avalue"); + expected.put("B", "Bvalue"); + expected.put("3", "3value"); + + assertEquals(expected, original.toMap()); + + try { + original.toMap().put("abc", "xyz"); + } catch (final UnsupportedOperationException ex) { + fail("Expected map to be mutable, but " + ex); + } + } + + @Test + public void testPutAll_KeepsExistingValues() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "bbb"); + original.putValue("c", "ccc"); + assertEquals("size", 3, original.size()); + + // add empty context data + original.putAll(new JdkMapAdapterStringMap()); + assertEquals("size after put empty", 3, original.size()); + assertEquals("aaa", original.getValue("a")); + assertEquals("bbb", original.getValue("b")); + assertEquals("ccc", original.getValue("c")); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue("1", "111"); + other.putValue("2", "222"); + other.putValue("3", "333"); + original.putAll(other); + + assertEquals("size after put other", 6, original.size()); + assertEquals("aaa", original.getValue("a")); + assertEquals("bbb", original.getValue("b")); + assertEquals("ccc", original.getValue("c")); + assertEquals("111", original.getValue("1")); + assertEquals("222", original.getValue("2")); + assertEquals("333", original.getValue("3")); + } + + @Test + public void testPutAll_sizePowerOfTwo() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "bbb"); + original.putValue("c", "ccc"); + original.putValue("d", "ddd"); + assertEquals("size", 4, original.size()); + + // add empty context data + original.putAll(new JdkMapAdapterStringMap()); + assertEquals("size after put empty", 4, original.size()); + assertEquals("aaa", original.getValue("a")); + assertEquals("bbb", original.getValue("b")); + assertEquals("ccc", original.getValue("c")); + assertEquals("ddd", original.getValue("d")); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + other.putValue("1", "111"); + other.putValue("2", "222"); + other.putValue("3", "333"); + other.putValue("4", "444"); + original.putAll(other); + + assertEquals("size after put other", 8, original.size()); + assertEquals("aaa", original.getValue("a")); + assertEquals("bbb", original.getValue("b")); + assertEquals("ccc", original.getValue("c")); + assertEquals("ddd", original.getValue("d")); + assertEquals("111", original.getValue("1")); + assertEquals("222", original.getValue("2")); + assertEquals("333", original.getValue("3")); + assertEquals("444", original.getValue("4")); + } + + @Test + public void testPutAll_largeAddition() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue(null, "nullVal"); + original.putValue("a", "aaa"); + original.putValue("b", "bbb"); + original.putValue("c", "ccc"); + original.putValue("d", "ddd"); + assertEquals("size", 5, original.size()); + + final JdkMapAdapterStringMap other = new JdkMapAdapterStringMap(); + for (int i = 0 ; i < 500; i++) { + other.putValue(String.valueOf(i), String.valueOf(i)); + } + other.putValue(null, "otherVal"); + original.putAll(other); + + assertEquals("size after put other", 505, original.size()); + assertEquals("otherVal", original.getValue(null)); + assertEquals("aaa", original.getValue("a")); + assertEquals("bbb", original.getValue("b")); + assertEquals("ccc", original.getValue("c")); + assertEquals("ddd", original.getValue("d")); + for (int i = 0 ; i < 500; i++) { + assertEquals(String.valueOf(i), original.getValue(String.valueOf(i))); + } + } + + @Test + public void testPutAllSelfDoesNotModify() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "bbb"); + original.putValue("c", "ccc"); + assertEquals("size", 3, original.size()); + + // putAll with self + original.putAll(original); + assertEquals("size after put empty", 3, original.size()); + assertEquals("aaa", original.getValue("a")); + assertEquals("bbb", original.getValue("b")); + assertEquals("ccc", original.getValue("c")); + } + + @Test(expected = ConcurrentModificationException.class) + public void testConcurrentModificationBiConsumerPut() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "aaa"); + original.putValue("c", "aaa"); + original.putValue("d", "aaa"); + original.putValue("e", "aaa"); + original.forEach(new BiConsumer<String, Object>() { + @Override + public void accept(final String s, final Object o) { + original.putValue("c" + s, "other"); + } + }); + } + + @Test(expected = ConcurrentModificationException.class) + public void testConcurrentModificationBiConsumerPutValue() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "aaa"); + original.putValue("c", "aaa"); + original.putValue("d", "aaa"); + original.putValue("e", "aaa"); + original.forEach(new BiConsumer<String, Object>() { + @Override + public void accept(final String s, final Object o) { + original.putValue("c" + s, "other"); + } + }); + } + + @Test(expected = ConcurrentModificationException.class) + public void testConcurrentModificationBiConsumerRemove() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "aaa"); + original.putValue("c", "aaa"); + original.forEach(new BiConsumer<String, Object>() { + @Override + public void accept(final String s, final Object o) { + original.remove("a"); + } + }); + } + + @Test(expected = ConcurrentModificationException.class) + public void testConcurrentModificationBiConsumerClear() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "aaa"); + original.putValue("c", "aaa"); + original.putValue("d", "aaa"); + original.putValue("e", "aaa"); + original.forEach(new BiConsumer<String, Object>() { + @Override + public void accept(final String s, final Object o) { + original.clear(); + } + }); + } + + @Test(expected = ConcurrentModificationException.class) + public void testConcurrentModificationTriConsumerPut() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "aaa"); + original.putValue("d", "aaa"); + original.putValue("e", "aaa"); + original.forEach(new TriConsumer<String, Object, Object>() { + @Override + public void accept(final String s, final Object o, final Object o2) { + original.putValue("c", "other"); + } + }, null); + } + + @Test(expected = ConcurrentModificationException.class) + public void testConcurrentModificationTriConsumerPutValue() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "aaa"); + original.putValue("c", "aaa"); + original.putValue("d", "aaa"); + original.putValue("e", "aaa"); + original.forEach(new TriConsumer<String, Object, Object>() { + @Override + public void accept(final String s, final Object o, final Object o2) { + original.putValue("c" + s, "other"); + } + }, null); + } + + @Test(expected = ConcurrentModificationException.class) + public void testConcurrentModificationTriConsumerRemove() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "aaa"); + original.putValue("c", "aaa"); + original.forEach(new TriConsumer<String, Object, Object>() { + @Override + public void accept(final String s, final Object o, final Object o2) { + original.remove("a"); + } + }, null); + } + + @Test(expected = ConcurrentModificationException.class) + public void testConcurrentModificationTriConsumerClear() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.putValue("b", "aaa"); + original.putValue("c", "aaa"); + original.putValue("d", "aaa"); + original.forEach(new TriConsumer<String, Object, Object>() { + @Override + public void accept(final String s, final Object o, final Object o2) { + original.clear(); + } + }, null); + } + + @Test + public void testInitiallyNotFrozen() { + assertFalse(new JdkMapAdapterStringMap().isFrozen()); + } + + @Test + public void testIsFrozenAfterCallingFreeze() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + assertFalse("before freeze", original.isFrozen()); + original.freeze(); + assertTrue("after freeze", original.isFrozen()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testFreezeProhibitsPutValue() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.freeze(); + original.putValue("a", "aaa"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testFreezeProhibitsRemove() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("b", "bbb"); + original.freeze(); + original.remove("b"); // existing key: modifies the collection + } + + @Test + public void testFreezeAllowsRemoveOfNonExistingKey() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("b", "bbb"); + original.freeze(); + original.remove("a"); // no actual modification + } + + @Test + public void testFreezeAllowsRemoveIfEmpty() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.freeze(); + original.remove("a"); // no exception + } + + @Test(expected = UnsupportedOperationException.class) + public void testFreezeProhibitsClear() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "aaa"); + original.freeze(); + original.clear(); + } + + @Test + public void testFreezeAllowsClearIfEmpty() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.freeze(); + original.clear(); + } + + @Test + public void testNullKeysAllowed() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + original.putValue("c", "cvalue"); + original.putValue("d", "dvalue"); + assertEquals(5, original.size()); + + original.putValue(null, "nullvalue"); + assertEquals(6, original.size()); + assertEquals("nullvalue", original.getValue(null)); + + original.putValue(null, "otherNullvalue"); + assertEquals("otherNullvalue", original.getValue(null)); + assertEquals(6, original.size()); + + original.putValue(null, "nullvalue"); + assertEquals(6, original.size()); + assertEquals("nullvalue", original.getValue(null)); + + original.putValue(null, "abc"); + assertEquals(6, original.size()); + assertEquals("abc", original.getValue(null)); + + } + + @Test + public void testNullKeysCopiedToAsMap() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + original.putValue("c", "cvalue"); + original.putValue("d", "dvalue"); + assertEquals(5, original.size()); + + final HashMap<String, String> expected = new HashMap<>(); + expected.put("a", "avalue"); + expected.put("B", "Bvalue"); + expected.put("3", "3value"); + expected.put("c", "cvalue"); + expected.put("d", "dvalue"); + assertEquals("initial", expected, original.toMap()); + + original.putValue(null, "nullvalue"); + expected.put(null, "nullvalue"); + assertEquals(6, original.size()); + assertEquals("with null key", expected, original.toMap()); + + original.putValue(null, "otherNullvalue"); + expected.put(null, "otherNullvalue"); + assertEquals(6, original.size()); + assertEquals("with null key value2", expected, original.toMap()); + + original.putValue(null, "nullvalue"); + expected.put(null, "nullvalue"); + assertEquals(6, original.size()); + assertEquals("with null key value1 again", expected, original.toMap()); + + original.putValue(null, "abc"); + expected.put(null, "abc"); + assertEquals(6, original.size()); + assertEquals("with null key value3", expected, original.toMap()); + } + + @Test + public void testRemove() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + assertEquals(1, original.size()); + assertEquals("avalue", original.getValue("a")); + + original.remove("a"); + assertEquals(0, original.size()); + assertNull("no a val", original.getValue("a")); + + original.remove("B"); + assertEquals(0, original.size()); + assertNull("no B val", original.getValue("B")); + } + + @Test + public void testRemoveWhenFull() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("b", "bvalue"); + original.putValue("c", "cvalue"); + original.putValue("d", "dvalue"); // default capacity = 4 + original.remove("d"); + } + + @Test + public void testNullValuesArePreserved() { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + assertEquals(1, original.size()); + assertEquals("avalue", original.getValue("a")); + + original.putValue("a", null); + assertEquals(1, original.size()); + assertNull("no a val", original.getValue("a")); + + original.putValue("B", null); + assertEquals(2, original.size()); + assertNull("no B val", original.getValue("B")); + } + + @Test + public void testGet() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + + assertEquals("avalue", original.getValue("a")); + assertEquals("Bvalue", original.getValue("B")); + assertEquals("3value", original.getValue("3")); + + original.putValue("0", "0value"); + assertEquals("0value", original.getValue("0")); + assertEquals("3value", original.getValue("3")); + assertEquals("Bvalue", original.getValue("B")); + assertEquals("avalue", original.getValue("a")); + } + + @Test + public void testClear() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + assertEquals(3, original.size()); + + original.clear(); + assertEquals(0, original.size()); + } + + @Test + public void testContainsKey() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + assertFalse("a", original.containsKey("a")); + assertFalse("B", original.containsKey("B")); + assertFalse("3", original.containsKey("3")); + assertFalse("A", original.containsKey("A")); + + original.putValue("a", "avalue"); + assertTrue("a", original.containsKey("a")); + assertFalse("B", original.containsKey("B")); + assertFalse("3", original.containsKey("3")); + assertFalse("A", original.containsKey("A")); + + original.putValue("B", "Bvalue"); + assertTrue("a", original.containsKey("a")); + assertTrue("B", original.containsKey("B")); + assertFalse("3", original.containsKey("3")); + assertFalse("A", original.containsKey("A")); + + original.putValue("3", "3value"); + assertTrue("a", original.containsKey("a")); + assertTrue("B", original.containsKey("B")); + assertTrue("3", original.containsKey("3")); + assertFalse("A", original.containsKey("A")); + + original.putValue("A", "AAA"); + assertTrue("a", original.containsKey("a")); + assertTrue("B", original.containsKey("B")); + assertTrue("3", original.containsKey("3")); + assertTrue("A", original.containsKey("A")); + } + + @Test + public void testSizeAndIsEmpty() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + assertEquals(0, original.size()); + assertTrue("initial", original.isEmpty()); + + original.putValue("a", "avalue"); + assertEquals(1, original.size()); + assertFalse("size=" + original.size(), original.isEmpty()); + + original.putValue("B", "Bvalue"); + assertEquals(2, original.size()); + assertFalse("size=" + original.size(), original.isEmpty()); + + original.putValue("3", "3value"); + assertEquals(3, original.size()); + assertFalse("size=" + original.size(), original.isEmpty()); + + original.remove("B"); + assertEquals(2, original.size()); + assertFalse("size=" + original.size(), original.isEmpty()); + + original.remove("3"); + assertEquals(1, original.size()); + assertFalse("size=" + original.size(), original.isEmpty()); + + original.remove("a"); + assertEquals(0, original.size()); + assertTrue("size=" + original.size(), original.isEmpty()); + } + + @Test + public void testForEachBiConsumer() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + + original.forEach(new BiConsumer<String, String>() { + int count = 0; + @Override + public void accept(final String key, final String value) { +// assertEquals("key", key, original.getKeyAt(count)); +// assertEquals("val", value, original.getValueAt(count)); + count++; + assertTrue("count should not exceed size but was " + count, count <= original.size()); + } + }); + } + + static class State { + JdkMapAdapterStringMap data; + int count; + } + static TriConsumer<String, String, JdkMapAdapterStringMapTest.State> COUNTER = new TriConsumer<String, String, JdkMapAdapterStringMapTest.State>() { + @Override + public void accept(final String key, final String value, final JdkMapAdapterStringMapTest.State state) { +// assertEquals("key", key, state.data.getKeyAt(state.count)); +// assertEquals("val", value, state.data.getValueAt(state.count)); + state.count++; + assertTrue("count should not exceed size but was " + state.count, + state.count <= state.data.size()); + } + }; + + @Test + public void testForEachTriConsumer() throws Exception { + final JdkMapAdapterStringMap original = new JdkMapAdapterStringMap(); + original.putValue("a", "avalue"); + original.putValue("B", "Bvalue"); + original.putValue("3", "3value"); + + final JdkMapAdapterStringMapTest.State state = new JdkMapAdapterStringMapTest.State(); + state.data = original; + original.forEach(COUNTER, state); + assertEquals(state.count, original.size()); + } + +} \ No newline at end of file
