test/java/util/Map/Defaults.java

Print this page
rev 8126 : 8024688: j.u.Map.merge doesn't work as specified if contains key:null pair
Reviewed-by: duke

*** 39,48 **** --- 39,49 ---- import java.util.HashSet; import java.util.Hashtable; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedHashMap; + import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentMap;
*** 286,308 **** assertFalse(map.containsKey(EXTRA_KEY)); assertSame(map.computeIfAbsent(EXTRA_KEY, (k) -> EXTRA_VALUE), EXTRA_VALUE); assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); } ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testComputeIfAbsentNPEHashMap() { ! Object value = new HashMap().computeIfAbsent(KEYS[1], null); ! } ! ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testComputeIfAbsentNPEHashtable() { ! Object value = new Hashtable().computeIfAbsent(KEYS[1], null); ! } ! ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testComputeIfAbsentNPETreeMap() { ! Object value = new TreeMap().computeIfAbsent(KEYS[1], null); } @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") public void testComputeIfPresentNulls(String description, Map<IntegerEnum, String> map) { assertTrue(map.containsKey(null), description + ": null key absent"); --- 287,301 ---- assertFalse(map.containsKey(EXTRA_KEY)); assertSame(map.computeIfAbsent(EXTRA_KEY, (k) -> EXTRA_VALUE), EXTRA_VALUE); assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); } ! @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") ! public void testComputeIfAbsentNullFunction(String description, Map<IntegerEnum, String> map) { ! assertThrows( () -> { map.computeIfAbsent(KEYS[1], null);}, ! NullPointerException.class, ! "Should throw NPE"); } @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") public void testComputeIfPresentNulls(String description, Map<IntegerEnum, String> map) { assertTrue(map.containsKey(null), description + ": null key absent");
*** 341,363 **** }), null); assertFalse(map.containsKey(EXTRA_KEY)); assertSame(map.get(EXTRA_KEY), null); } ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testComputeIfPresentNPEHashMap() { ! Object value = new HashMap().computeIfPresent(KEYS[1], null); ! } ! ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testComputeIfPresentNPEHashtable() { ! Object value = new Hashtable().computeIfPresent(KEYS[1], null); ! } ! ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testComputeIfPresentNPETreeMap() { ! Object value = new TreeMap().computeIfPresent(KEYS[1], null); } @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") public void testComputeNulls(String description, Map<IntegerEnum, String> map) { assertTrue(map.containsKey(null), "null key absent"); --- 334,348 ---- }), null); assertFalse(map.containsKey(EXTRA_KEY)); assertSame(map.get(EXTRA_KEY), null); } ! @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") ! public void testComputeIfPresentNullFunction(String description, Map<IntegerEnum, String> map) { ! assertThrows( () -> { map.computeIfPresent(KEYS[1], null);}, ! NullPointerException.class, ! "Should throw NPE"); } @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") public void testComputeNulls(String description, Map<IntegerEnum, String> map) { assertTrue(map.containsKey(null), "null key absent");
*** 442,523 **** }), EXTRA_VALUE); assertTrue(map.containsKey(EXTRA_KEY)); assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); } ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testComputeNPEHashMap() { ! Object value = new HashMap().compute(KEYS[1], null); } ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testComputeNPEHashtable() { ! Object value = new Hashtable().compute(KEYS[1], null); ! } ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testComputeNPETreeMap() { ! Object value = new TreeMap().compute(KEYS[1], null); ! } ! @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") ! public void testMergeNulls(String description, Map<IntegerEnum, String> map) { ! assertTrue(map.containsKey(null), "null key absent"); ! assertNull(map.get(null), "value not null"); ! assertSame(map.merge(null, EXTRA_VALUE, (v, vv) -> { ! assertNull(v); ! assertSame(vv, EXTRA_VALUE); ! return vv; ! }), EXTRA_VALUE, description); ! assertTrue(map.containsKey(null)); ! assertSame(map.get(null), EXTRA_VALUE, description); } - - @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") - public void testMerge(String description, Map<IntegerEnum, String> map) { - assertTrue(map.containsKey(KEYS[1])); - Object value = map.get(KEYS[1]); - assertTrue(null == value || value == VALUES[1], description + String.valueOf(value)); - assertSame(map.merge(KEYS[1], EXTRA_VALUE, (v, vv) -> { - assertSame(v, value); - assertSame(vv, EXTRA_VALUE); - return vv; - }), EXTRA_VALUE, description); - assertSame(map.get(KEYS[1]), EXTRA_VALUE, description); - assertNull(map.merge(KEYS[1], EXTRA_VALUE, (v, vv) -> { - assertSame(v, EXTRA_VALUE); - assertSame(vv, EXTRA_VALUE); - return null; - }), description); - assertFalse(map.containsKey(KEYS[1])); - - assertFalse(map.containsKey(EXTRA_KEY)); - assertSame(map.merge(EXTRA_KEY, EXTRA_VALUE, (v, vv) -> { - assertNull(v); - assertSame(vv, EXTRA_VALUE); - return EXTRA_VALUE; - }), EXTRA_VALUE); - assertTrue(map.containsKey(EXTRA_KEY)); - assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); } ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testMergeNPEHashMap() { ! Object value = new HashMap().merge(KEYS[1], VALUES[1], null); ! } ! ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testMergeNPEHashtable() { ! Object value = new Hashtable().merge(KEYS[1], VALUES[1], null); ! } ! ! @Test(expectedExceptions = {NullPointerException.class}) ! public void testMergeNPETreeMap() { ! Object value = new TreeMap().merge(KEYS[1], VALUES[1], null); } ! enum IntegerEnum { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34, e35, e36, e37, e38, e39, --- 427,516 ---- }), EXTRA_VALUE); assertTrue(map.containsKey(EXTRA_KEY)); assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); } ! @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") ! public void testComputeNullFunction(String description, Map<IntegerEnum, String> map) { ! assertThrows( () -> { map.compute(KEYS[1], null);}, ! NullPointerException.class, ! "Should throw NPE"); } ! @Test(dataProvider = "MergeCases") ! private void testMerge(String description, Map<IntegerEnum, String> map, Merging.Value oldValue, Merging.Value newValue, Merging.Merger merger, Merging.Value put, Merging.Value result) { ! // add and check initial conditions. ! switch(oldValue) { ! case ABSENT : ! map.remove(EXTRA_KEY); ! assertFalse(map.containsKey(EXTRA_KEY), "key not absent"); ! break; ! case NULL : ! map.put(EXTRA_KEY, null); ! assertTrue(map.containsKey(EXTRA_KEY), "key absent"); ! assertNull(map.get(EXTRA_KEY), "wrong value"); ! break; ! case OLDVALUE : ! map.put(EXTRA_KEY, VALUES[1]); ! assertTrue(map.containsKey(EXTRA_KEY), "key absent"); ! assertSame(map.get(EXTRA_KEY), VALUES[1], "wrong value"); ! break; ! default: ! fail("unexpected old value"); ! } ! ! String returned = map.merge(EXTRA_KEY, ! newValue == Merging.Value.NULL ? (String) null : VALUES[2], ! merger ! ); ! // check result ! switch(result) { ! case NULL : ! assertNull(returned, "wrong value"); ! break; ! case NEWVALUE : ! assertSame(returned, VALUES[2], "wrong value"); ! break; ! case RESULT : ! assertSame(returned, VALUES[3], "wrong value"); ! break; ! default: ! fail("unexpected new value"); ! } ! ! // check map ! switch(put) { ! case ABSENT : ! assertFalse(map.containsKey(EXTRA_KEY), "key not absent"); ! break; ! case NULL : ! assertTrue(map.containsKey(EXTRA_KEY), "key absent"); ! assertNull(map.get(EXTRA_KEY), "wrong value"); ! break; ! case NEWVALUE : ! assertTrue(map.containsKey(EXTRA_KEY), "key absent"); ! assertSame(map.get(EXTRA_KEY), VALUES[2], "wrong value"); ! break; ! case RESULT : ! assertTrue(map.containsKey(EXTRA_KEY), "key absent"); ! assertSame(map.get(EXTRA_KEY), VALUES[3], "wrong value"); ! break; ! default: ! fail("unexpected new value"); } } ! @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") ! public void testMergeNullMerger(String description, Map<IntegerEnum, String> map) { ! assertThrows( () -> { map.merge(KEYS[1], VALUES[1], null);}, ! NullPointerException.class, ! "Should throw NPE"); } ! public enum IntegerEnum { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34, e35, e36, e37, e38, e39,
*** 713,722 **** --- 706,798 ---- } return result; } + static class Merging { + public enum Value { + ABSENT, + NULL, + OLDVALUE, + NEWVALUE, + RESULT + } + + public enum Merger implements BiFunction<String,String,String> { + UNUSED { + public String apply(String oldValue, String newValue) { + fail("should not be called"); + return null; + } + }, + NULL { + public String apply(String oldValue, String newValue) { + return null; + } + }, + RESULT { + public String apply(String oldValue, String newValue) { + return VALUES[3]; + } + }, + } + } + + @DataProvider(name = "MergeCases", parallel = true) + public Iterator<Object[]> mergeCasesProvider() { + Collection<Object[]> cases = new ArrayList<>(); + + cases.addAll(makeMergeTestCases()); + cases.addAll(makeMergeNullValueTestCases()); + + return cases.iterator(); + } + + static Collection<Object[]> makeMergeTestCases() { + Collection<Object[]> cases = new ArrayList<>(); + + for( Object[] mapParams : makeAllRWMaps() ) { + cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.ABSENT, Merging.Value.NEWVALUE, Merging.Merger.UNUSED, Merging.Value.NEWVALUE, Merging.Value.NEWVALUE }); + } + + for( Object[] mapParams : makeAllRWMaps() ) { + cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NEWVALUE, Merging.Merger.NULL, Merging.Value.ABSENT, Merging.Value.NULL }); + } + + for( Object[] mapParams : makeAllRWMaps() ) { + cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NEWVALUE, Merging.Merger.RESULT, Merging.Value.RESULT, Merging.Value.RESULT }); + } + + return cases; + } + + static Collection<Object[]> makeMergeNullValueTestCases() { + Collection<Object[]> cases = new ArrayList<>(); + + for( Object[] mapParams : makeAllRWMapsWithNulls() ) { + cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NULL, Merging.Merger.NULL, Merging.Value.ABSENT, Merging.Value.NULL }); + } + + for( Object[] mapParams : makeAllRWMapsWithNulls() ) { + cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NULL, Merging.Merger.RESULT, Merging.Value.RESULT, Merging.Value.RESULT }); + } + + for( Object[] mapParams : makeAllRWMapsWithNulls() ) { + cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.ABSENT, Merging.Value.NULL, Merging.Merger.UNUSED, Merging.Value.ABSENT, Merging.Value.NULL }); + } + + for( Object[] mapParams : makeAllRWMapsWithNulls() ) { + cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.NULL, Merging.Value.NULL, Merging.Merger.UNUSED, Merging.Value.ABSENT, Merging.Value.NULL }); + } + + for( Object[] mapParams : makeAllRWMapsWithNulls() ) { + cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.NULL, Merging.Value.NEWVALUE, Merging.Merger.UNUSED, Merging.Value.NEWVALUE, Merging.Value.NEWVALUE }); + } + + return cases; + } + public interface Thrower<T extends Throwable> { public void run() throws T; }