Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/java/util/Map/Defaults.java
38812 views
/*1* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 8010122 8004518 8024331 802468826* @summary Test Map default methods27* @author Mike Duigou28* @run testng Defaults29*/30import java.util.AbstractMap;31import java.util.AbstractSet;32import java.util.ArrayList;33import java.util.Arrays;34import java.util.Collection;35import java.util.Collections;36import java.util.EnumMap;37import java.util.HashMap;38import java.util.Hashtable;39import java.util.HashSet;40import java.util.IdentityHashMap;41import java.util.Iterator;42import java.util.LinkedHashMap;43import java.util.Map;44import java.util.TreeMap;45import java.util.Set;46import java.util.WeakHashMap;47import java.util.concurrent.ConcurrentMap;48import java.util.concurrent.ConcurrentHashMap;49import java.util.concurrent.ConcurrentSkipListMap;50import java.util.function.BiFunction;51import java.util.function.Supplier;5253import org.testng.annotations.Test;54import org.testng.annotations.DataProvider;55import static org.testng.Assert.fail;56import static org.testng.Assert.assertEquals;57import static org.testng.Assert.assertTrue;58import static org.testng.Assert.assertFalse;59import static org.testng.Assert.assertNull;60import static org.testng.Assert.assertSame;6162public class Defaults {6364@Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=withNull values=withNull")65public void testGetOrDefaultNulls(String description, Map<IntegerEnum, String> map) {66assertTrue(map.containsKey(null), description + ": null key absent");67assertNull(map.get(null), description + ": value not null");68assertSame(map.get(null), map.getOrDefault(null, EXTRA_VALUE), description + ": values should match");69}7071@Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=all values=all")72public void testGetOrDefault(String description, Map<IntegerEnum, String> map) {73assertTrue(map.containsKey(KEYS[1]), "expected key missing");74assertSame(map.get(KEYS[1]), map.getOrDefault(KEYS[1], EXTRA_VALUE), "values should match");75assertFalse(map.containsKey(EXTRA_KEY), "expected absent key");76assertSame(map.getOrDefault(EXTRA_KEY, EXTRA_VALUE), EXTRA_VALUE, "value not returned as default");77assertNull(map.getOrDefault(EXTRA_KEY, null), "null not returned as default");78}7980@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")81public void testPutIfAbsentNulls(String description, Map<IntegerEnum, String> map) {82// null -> null83assertTrue(map.containsKey(null), "null key absent");84assertNull(map.get(null), "value not null");85assertNull(map.putIfAbsent(null, EXTRA_VALUE), "previous not null");86// null -> EXTRA_VALUE87assertTrue(map.containsKey(null), "null key absent");88assertSame(map.get(null), EXTRA_VALUE, "unexpected value");89assertSame(map.putIfAbsent(null, null), EXTRA_VALUE, "previous not expected value");90assertTrue(map.containsKey(null), "null key absent");91assertSame(map.get(null), EXTRA_VALUE, "unexpected value");92assertSame(map.remove(null), EXTRA_VALUE, "removed unexpected value");93// null -> <absent>9495assertFalse(map.containsKey(null), description + ": key present after remove");96assertNull(map.putIfAbsent(null, null), "previous not null");97// null -> null98assertTrue(map.containsKey(null), "null key absent");99assertNull(map.get(null), "value not null");100assertNull(map.putIfAbsent(null, EXTRA_VALUE), "previous not null");101assertSame(map.get(null), EXTRA_VALUE, "value not expected");102}103104@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")105public void testPutIfAbsent(String description, Map<IntegerEnum, String> map) {106// 1 -> 1107assertTrue(map.containsKey(KEYS[1]));108Object expected = map.get(KEYS[1]);109assertTrue(null == expected || expected == VALUES[1]);110assertSame(map.putIfAbsent(KEYS[1], EXTRA_VALUE), expected);111assertSame(map.get(KEYS[1]), expected);112113// EXTRA_KEY -> <absent>114assertFalse(map.containsKey(EXTRA_KEY));115assertSame(map.putIfAbsent(EXTRA_KEY, EXTRA_VALUE), null);116assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);117assertSame(map.putIfAbsent(EXTRA_KEY, VALUES[2]), EXTRA_VALUE);118assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);119}120121@Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=all values=all")122public void testForEach(String description, Map<IntegerEnum, String> map) {123IntegerEnum[] EACH_KEY = new IntegerEnum[map.size()];124125map.forEach((k, v) -> {126int idx = (null == k) ? 0 : k.ordinal(); // substitute for index.127assertNull(EACH_KEY[idx]);128EACH_KEY[idx] = (idx == 0) ? KEYS[0] : k; // substitute for comparison.129assertSame(v, map.get(k));130});131132assertEquals(KEYS, EACH_KEY, description);133}134135@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")136public static void testReplaceAll(String description, Map<IntegerEnum, String> map) {137IntegerEnum[] EACH_KEY = new IntegerEnum[map.size()];138Set<String> EACH_REPLACE = new HashSet<>(map.size());139140map.replaceAll((k,v) -> {141int idx = (null == k) ? 0 : k.ordinal(); // substitute for index.142assertNull(EACH_KEY[idx]);143EACH_KEY[idx] = (idx == 0) ? KEYS[0] : k; // substitute for comparison.144assertSame(v, map.get(k));145String replacement = v + " replaced";146EACH_REPLACE.add(replacement);147return replacement;148});149150assertEquals(KEYS, EACH_KEY, description);151assertEquals(map.values().size(), EACH_REPLACE.size(), description + EACH_REPLACE);152assertTrue(EACH_REPLACE.containsAll(map.values()), description + " : " + EACH_REPLACE + " != " + map.values());153assertTrue(map.values().containsAll(EACH_REPLACE), description + " : " + EACH_REPLACE + " != " + map.values());154}155156@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")157public static void testReplaceAllNoNullReplacement(String description, Map<IntegerEnum, String> map) {158assertThrows(159() -> { map.replaceAll(null); },160NullPointerException.class,161description);162assertThrows(163() -> { map.replaceAll((k,v) -> null); },164NullPointerException.class,165description + " should not allow replacement with null value");166}167168@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")169public static void testRemoveNulls(String description, Map<IntegerEnum, String> map) {170assertTrue(map.containsKey(null), "null key absent");171assertNull(map.get(null), "value not null");172assertFalse(map.remove(null, EXTRA_VALUE), description);173assertTrue(map.containsKey(null));174assertNull(map.get(null));175assertTrue(map.remove(null, null));176assertFalse(map.containsKey(null));177assertNull(map.get(null));178assertFalse(map.remove(null, null));179}180181@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")182public static void testRemove(String description, Map<IntegerEnum, String> map) {183assertTrue(map.containsKey(KEYS[1]));184Object expected = map.get(KEYS[1]);185assertTrue(null == expected || expected == VALUES[1]);186assertFalse(map.remove(KEYS[1], EXTRA_VALUE), description);187assertSame(map.get(KEYS[1]), expected);188assertTrue(map.remove(KEYS[1], expected));189assertNull(map.get(KEYS[1]));190assertFalse(map.remove(KEYS[1], expected));191192assertFalse(map.containsKey(EXTRA_KEY));193assertFalse(map.remove(EXTRA_KEY, EXTRA_VALUE));194}195196@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")197public void testReplaceKVNulls(String description, Map<IntegerEnum, String> map) {198assertTrue(map.containsKey(null), "null key absent");199assertNull(map.get(null), "value not null");200assertSame(map.replace(null, EXTRA_VALUE), null);201assertSame(map.get(null), EXTRA_VALUE);202}203204@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")205public void testReplaceKVNoNulls(String description, Map<IntegerEnum, String> map) {206assertTrue(map.containsKey(FIRST_KEY), "expected key missing");207assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value");208assertThrows( () -> {map.replace(FIRST_KEY, null);}, NullPointerException.class, description + ": should throw NPE");209assertSame(map.replace(FIRST_KEY, EXTRA_VALUE), FIRST_VALUE, description + ": replaced wrong value");210assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value");211}212213@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")214public void testReplaceKV(String description, Map<IntegerEnum, String> map) {215assertTrue(map.containsKey(KEYS[1]));216Object expected = map.get(KEYS[1]);217assertTrue(null == expected || expected == VALUES[1]);218assertSame(map.replace(KEYS[1], EXTRA_VALUE), expected);219assertSame(map.get(KEYS[1]), EXTRA_VALUE);220221assertFalse(map.containsKey(EXTRA_KEY));222assertNull(map.replace(EXTRA_KEY, EXTRA_VALUE));223assertFalse(map.containsKey(EXTRA_KEY));224assertNull(map.get(EXTRA_KEY));225assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));226assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);227assertSame(map.replace(EXTRA_KEY, (String)expected), EXTRA_VALUE);228assertSame(map.get(EXTRA_KEY), expected);229}230231@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")232public void testReplaceKVVNulls(String description, Map<IntegerEnum, String> map) {233assertTrue(map.containsKey(null), "null key absent");234assertNull(map.get(null), "value not null");235assertFalse(map.replace(null, EXTRA_VALUE, EXTRA_VALUE));236assertNull(map.get(null));237assertTrue(map.replace(null, null, EXTRA_VALUE));238assertSame(map.get(null), EXTRA_VALUE);239assertTrue(map.replace(null, EXTRA_VALUE, EXTRA_VALUE));240assertSame(map.get(null), EXTRA_VALUE);241}242243@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")244public void testReplaceKVVNoNulls(String description, Map<IntegerEnum, String> map) {245assertTrue(map.containsKey(FIRST_KEY), "expected key missing");246assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value");247assertThrows( () -> {map.replace(FIRST_KEY, FIRST_VALUE, null);}, NullPointerException.class, description + ": should throw NPE");248assertThrows( () -> {if (!map.replace(FIRST_KEY, null, EXTRA_VALUE)) throw new NullPointerException("default returns false rather than throwing");}, NullPointerException.class, description + ": should throw NPE");249assertTrue(map.replace(FIRST_KEY, FIRST_VALUE, EXTRA_VALUE), description + ": replaced wrong value");250assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value");251}252253@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")254public void testReplaceKVV(String description, Map<IntegerEnum, String> map) {255assertTrue(map.containsKey(KEYS[1]));256Object expected = map.get(KEYS[1]);257assertTrue(null == expected || expected == VALUES[1]);258assertFalse(map.replace(KEYS[1], EXTRA_VALUE, EXTRA_VALUE));259assertSame(map.get(KEYS[1]), expected);260assertTrue(map.replace(KEYS[1], (String)expected, EXTRA_VALUE));261assertSame(map.get(KEYS[1]), EXTRA_VALUE);262assertTrue(map.replace(KEYS[1], EXTRA_VALUE, EXTRA_VALUE));263assertSame(map.get(KEYS[1]), EXTRA_VALUE);264265assertFalse(map.containsKey(EXTRA_KEY));266assertFalse(map.replace(EXTRA_KEY, EXTRA_VALUE, EXTRA_VALUE));267assertFalse(map.containsKey(EXTRA_KEY));268assertNull(map.get(EXTRA_KEY));269assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));270assertTrue(map.containsKey(EXTRA_KEY));271assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);272assertTrue(map.replace(EXTRA_KEY, EXTRA_VALUE, EXTRA_VALUE));273assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);274}275276@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")277public void testComputeIfAbsentNulls(String description, Map<IntegerEnum, String> map) {278// null -> null279assertTrue(map.containsKey(null), "null key absent");280assertNull(map.get(null), "value not null");281assertSame(map.computeIfAbsent(null, (k) -> null), null, "not expected result");282assertTrue(map.containsKey(null), "null key absent");283assertNull(map.get(null), "value not null");284assertSame(map.computeIfAbsent(null, (k) -> EXTRA_VALUE), EXTRA_VALUE, "not mapped to result");285// null -> EXTRA_VALUE286assertTrue(map.containsKey(null), "null key absent");287assertSame(map.get(null), EXTRA_VALUE, "not expected value");288assertSame(map.remove(null), EXTRA_VALUE, "removed unexpected value");289// null -> <absent>290assertFalse(map.containsKey(null), "null key present");291assertSame(map.computeIfAbsent(null, (k) -> EXTRA_VALUE), EXTRA_VALUE, "not mapped to result");292// null -> EXTRA_VALUE293assertTrue(map.containsKey(null), "null key absent");294assertSame(map.get(null), EXTRA_VALUE, "not expected value");295}296297@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")298public void testComputeIfAbsent(String description, Map<IntegerEnum, String> map) {299// 1 -> 1300assertTrue(map.containsKey(KEYS[1]));301Object expected = map.get(KEYS[1]);302assertTrue(null == expected || expected == VALUES[1], description + String.valueOf(expected));303expected = (null == expected) ? EXTRA_VALUE : expected;304assertSame(map.computeIfAbsent(KEYS[1], (k) -> EXTRA_VALUE), expected, description);305assertSame(map.get(KEYS[1]), expected, description);306307// EXTRA_KEY -> <absent>308assertFalse(map.containsKey(EXTRA_KEY));309assertNull(map.computeIfAbsent(EXTRA_KEY, (k) -> null));310assertFalse(map.containsKey(EXTRA_KEY));311assertSame(map.computeIfAbsent(EXTRA_KEY, (k) -> EXTRA_VALUE), EXTRA_VALUE);312// EXTRA_KEY -> EXTRA_VALUE313assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);314}315316@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")317public void testComputeIfAbsentNullFunction(String description, Map<IntegerEnum, String> map) {318assertThrows( () -> { map.computeIfAbsent(KEYS[1], null);},319NullPointerException.class,320"Should throw NPE");321}322323@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")324public void testComputeIfPresentNulls(String description, Map<IntegerEnum, String> map) {325assertTrue(map.containsKey(null), description + ": null key absent");326assertNull(map.get(null), description + ": value not null");327assertSame(map.computeIfPresent(null, (k, v) -> {328fail(description + ": null value is not deemed present");329return EXTRA_VALUE;330}), null, description);331assertTrue(map.containsKey(null));332assertNull(map.get(null), description);333assertNull(map.remove(EXTRA_KEY), description + ": unexpected mapping");334assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value");335assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> {336fail(description + ": null value is not deemed present");337return EXTRA_VALUE;338}), null, description);339assertNull(map.get(EXTRA_KEY), description + ": null mapping gone");340}341342@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")343public void testComputeIfPresent(String description, Map<IntegerEnum, String> map) {344assertTrue(map.containsKey(KEYS[1]));345Object value = map.get(KEYS[1]);346assertTrue(null == value || value == VALUES[1], description + String.valueOf(value));347Object expected = (null == value) ? null : EXTRA_VALUE;348assertSame(map.computeIfPresent(KEYS[1], (k, v) -> {349assertSame(v, value);350return EXTRA_VALUE;351}), expected, description);352assertSame(map.get(KEYS[1]), expected, description);353354assertFalse(map.containsKey(EXTRA_KEY));355assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> {356fail();357return EXTRA_VALUE;358}), null);359assertFalse(map.containsKey(EXTRA_KEY));360assertSame(map.get(EXTRA_KEY), null);361}362363@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")364public void testComputeIfPresentNullFunction(String description, Map<IntegerEnum, String> map) {365assertThrows( () -> { map.computeIfPresent(KEYS[1], null);},366NullPointerException.class,367"Should throw NPE");368}369370@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")371public void testComputeNulls(String description, Map<IntegerEnum, String> map) {372assertTrue(map.containsKey(null), "null key absent");373assertNull(map.get(null), "value not null");374assertSame(map.compute(null, (k, v) -> {375assertNull(k);376assertNull(v);377return null;378}), null, description);379assertFalse(map.containsKey(null), description + ": null key present.");380assertSame(map.compute(null, (k, v) -> {381assertSame(k, null);382assertNull(v);383return EXTRA_VALUE;384}), EXTRA_VALUE, description);385assertTrue(map.containsKey(null));386assertSame(map.get(null), EXTRA_VALUE, description);387assertSame(map.remove(null), EXTRA_VALUE, description + ": removed value not expected");388// no mapping before and after389assertFalse(map.containsKey(null), description + ": null key present");390assertSame(map.compute(null, (k, v) -> {391assertNull(k);392assertNull(v);393return null;394}), null, description + ": expected null result" );395assertFalse(map.containsKey(null), description + ": null key present");396// compute with map not containing value397assertNull(map.remove(EXTRA_KEY), description + ": unexpected mapping");398assertFalse(map.containsKey(EXTRA_KEY), description + ": key present");399assertSame(map.compute(EXTRA_KEY, (k, v) -> {400assertSame(k, EXTRA_KEY);401assertNull(v);402return null;403}), null, description);404assertFalse(map.containsKey(EXTRA_KEY), description + ": null key present");405// ensure removal.406assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));407assertSame(map.compute(EXTRA_KEY, (k, v) -> {408assertSame(k, EXTRA_KEY);409assertSame(v, EXTRA_VALUE);410return null;411}), null, description + ": null resulted expected");412assertFalse(map.containsKey(EXTRA_KEY), description + ": null key present");413// compute with map containing null value414assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value");415assertSame(map.compute(EXTRA_KEY, (k, v) -> {416assertSame(k, EXTRA_KEY);417assertNull(v);418return null;419}), null, description);420assertFalse(map.containsKey(EXTRA_KEY), description + ": null key present");421assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value");422assertSame(map.compute(EXTRA_KEY, (k, v) -> {423assertSame(k, EXTRA_KEY);424assertNull(v);425return EXTRA_VALUE;426}), EXTRA_VALUE, description);427assertTrue(map.containsKey(EXTRA_KEY), "null key present");428}429430@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")431public void testCompute(String description, Map<IntegerEnum, String> map) {432assertTrue(map.containsKey(KEYS[1]));433Object value = map.get(KEYS[1]);434assertTrue(null == value || value == VALUES[1], description + String.valueOf(value));435assertSame(map.compute(KEYS[1], (k, v) -> {436assertSame(k, KEYS[1]);437assertSame(v, value);438return EXTRA_VALUE;439}), EXTRA_VALUE, description);440assertSame(map.get(KEYS[1]), EXTRA_VALUE, description);441assertNull(map.compute(KEYS[1], (k, v) -> {442assertSame(v, EXTRA_VALUE);443return null;444}), description);445assertFalse(map.containsKey(KEYS[1]));446447assertFalse(map.containsKey(EXTRA_KEY));448assertSame(map.compute(EXTRA_KEY, (k, v) -> {449assertNull(v);450return EXTRA_VALUE;451}), EXTRA_VALUE);452assertTrue(map.containsKey(EXTRA_KEY));453assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);454}455456@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")457public void testComputeNullFunction(String description, Map<IntegerEnum, String> map) {458assertThrows( () -> { map.compute(KEYS[1], null);},459NullPointerException.class,460"Should throw NPE");461}462463@Test(dataProvider = "MergeCases")464private void testMerge(String description, Map<IntegerEnum, String> map, Merging.Value oldValue, Merging.Value newValue, Merging.Merger merger, Merging.Value put, Merging.Value result) {465// add and check initial conditions.466switch(oldValue) {467case ABSENT :468map.remove(EXTRA_KEY);469assertFalse(map.containsKey(EXTRA_KEY), "key not absent");470break;471case NULL :472map.put(EXTRA_KEY, null);473assertTrue(map.containsKey(EXTRA_KEY), "key absent");474assertNull(map.get(EXTRA_KEY), "wrong value");475break;476case OLDVALUE :477map.put(EXTRA_KEY, VALUES[1]);478assertTrue(map.containsKey(EXTRA_KEY), "key absent");479assertSame(map.get(EXTRA_KEY), VALUES[1], "wrong value");480break;481default:482fail("unexpected old value");483}484485String returned = map.merge(EXTRA_KEY,486newValue == Merging.Value.NULL ? (String) null : VALUES[2],487merger488);489490// check result491492switch(result) {493case NULL :494assertNull(returned, "wrong value");495break;496case NEWVALUE :497assertSame(returned, VALUES[2], "wrong value");498break;499case RESULT :500assertSame(returned, VALUES[3], "wrong value");501break;502default:503fail("unexpected new value");504}505506// check map507switch(put) {508case ABSENT :509assertFalse(map.containsKey(EXTRA_KEY), "key not absent");510break;511case NULL :512assertTrue(map.containsKey(EXTRA_KEY), "key absent");513assertNull(map.get(EXTRA_KEY), "wrong value");514break;515case NEWVALUE :516assertTrue(map.containsKey(EXTRA_KEY), "key absent");517assertSame(map.get(EXTRA_KEY), VALUES[2], "wrong value");518break;519case RESULT :520assertTrue(map.containsKey(EXTRA_KEY), "key absent");521assertSame(map.get(EXTRA_KEY), VALUES[3], "wrong value");522break;523default:524fail("unexpected new value");525}526}527528@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")529public void testMergeNullMerger(String description, Map<IntegerEnum, String> map) {530assertThrows( () -> { map.merge(KEYS[1], VALUES[1], null);},531NullPointerException.class,532"Should throw NPE");533}534535public enum IntegerEnum {536537e0, e1, e2, e3, e4, e5, e6, e7, e8, e9,538e10, e11, e12, e13, e14, e15, e16, e17, e18, e19,539e20, e21, e22, e23, e24, e25, e26, e27, e28, e29,540e30, e31, e32, e33, e34, e35, e36, e37, e38, e39,541e40, e41, e42, e43, e44, e45, e46, e47, e48, e49,542e50, e51, e52, e53, e54, e55, e56, e57, e58, e59,543e60, e61, e62, e63, e64, e65, e66, e67, e68, e69,544e70, e71, e72, e73, e74, e75, e76, e77, e78, e79,545e80, e81, e82, e83, e84, e85, e86, e87, e88, e89,546e90, e91, e92, e93, e94, e95, e96, e97, e98, e99,547EXTRA_KEY;548public static final int SIZE = values().length;549};550private static final int TEST_SIZE = IntegerEnum.SIZE - 1;551/**552* Realized keys ensure that there is always a hard ref to all test objects.553*/554private static final IntegerEnum[] KEYS = new IntegerEnum[TEST_SIZE];555/**556* Realized values ensure that there is always a hard ref to all test557* objects.558*/559private static final String[] VALUES = new String[TEST_SIZE];560561static {562IntegerEnum[] keys = IntegerEnum.values();563for (int each = 0; each < TEST_SIZE; each++) {564KEYS[each] = keys[each];565VALUES[each] = String.valueOf(each);566}567}568569private static final IntegerEnum FIRST_KEY = KEYS[0];570private static final String FIRST_VALUE = VALUES[0];571private static final IntegerEnum EXTRA_KEY = IntegerEnum.EXTRA_KEY;572private static final String EXTRA_VALUE = String.valueOf(TEST_SIZE);573574@DataProvider(name = "Map<IntegerEnum,String> rw=all keys=all values=all", parallel = true)575public static Iterator<Object[]> allMapProvider() {576return makeAllMaps().iterator();577}578579@DataProvider(name = "Map<IntegerEnum,String> rw=all keys=withNull values=withNull", parallel = true)580public static Iterator<Object[]> allMapWithNullsProvider() {581return makeAllMapsWithNulls().iterator();582}583584@DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull", parallel = true)585public static Iterator<Object[]> rwNonNullMapProvider() {586return makeRWNoNullsMaps().iterator();587}588589@DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=all", parallel = true)590public static Iterator<Object[]> rwNonNullKeysMapProvider() {591return makeRWMapsNoNulls().iterator();592}593594@DataProvider(name = "Map<IntegerEnum,String> rw=true keys=all values=all", parallel = true)595public static Iterator<Object[]> rwMapProvider() {596return makeAllRWMaps().iterator();597}598599@DataProvider(name = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull", parallel = true)600public static Iterator<Object[]> rwNullsMapProvider() {601return makeAllRWMapsWithNulls().iterator();602}603604private static Collection<Object[]> makeAllRWMapsWithNulls() {605Collection<Object[]> all = new ArrayList<>();606607all.addAll(makeRWMaps(true, true));608609return all;610}611612613private static Collection<Object[]> makeRWMapsNoNulls() {614Collection<Object[]> all = new ArrayList<>();615616all.addAll(makeRWNoNullKeysMaps(false));617all.addAll(makeRWNoNullsMaps());618619return all;620}621622private static Collection<Object[]> makeAllROMaps() {623Collection<Object[]> all = new ArrayList<>();624625all.addAll(makeROMaps(false));626all.addAll(makeROMaps(true));627628return all;629}630631private static Collection<Object[]> makeAllRWMaps() {632Collection<Object[]> all = new ArrayList<>();633634all.addAll(makeRWNoNullsMaps());635all.addAll(makeRWMaps(false,true));636all.addAll(makeRWMaps(true,true));637all.addAll(makeRWNoNullKeysMaps(true));638return all;639}640641private static Collection<Object[]> makeAllMaps() {642Collection<Object[]> all = new ArrayList<>();643644all.addAll(makeAllROMaps());645all.addAll(makeAllRWMaps());646647return all;648}649650private static Collection<Object[]> makeAllMapsWithNulls() {651Collection<Object[]> all = new ArrayList<>();652653all.addAll(makeROMaps(true));654all.addAll(makeRWMaps(true,true));655656return all;657}658/**659*660* @param nullKeys include null keys661* @param nullValues include null values662* @return663*/664private static Collection<Object[]> makeRWMaps(boolean nullKeys, boolean nullValues) {665return Arrays.asList(666new Object[]{"HashMap", makeMap(HashMap::new, nullKeys, nullValues)},667new Object[]{"IdentityHashMap", makeMap(IdentityHashMap::new, nullKeys, nullValues)},668new Object[]{"LinkedHashMap", makeMap(LinkedHashMap::new, nullKeys, nullValues)},669new Object[]{"WeakHashMap", makeMap(WeakHashMap::new, nullKeys, nullValues)},670new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(makeMap(HashMap::new, nullKeys, nullValues), IntegerEnum.class, String.class)},671new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(makeMap(HashMap::new, nullKeys, nullValues))},672new Object[]{"ExtendsAbstractMap", makeMap(ExtendsAbstractMap::new, nullKeys, nullValues)});673}674675/**676*677* @param nulls include null values678* @return679*/680private static Collection<Object[]> makeRWNoNullKeysMaps(boolean nulls) {681return Arrays.asList(682// null key hostile683new Object[]{"EnumMap", makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls)},684new Object[]{"TreeMap", makeMap(TreeMap::new, false, nulls)},685new Object[]{"ExtendsAbstractMap(TreeMap)", makeMap(() -> {return new ExtendsAbstractMap(new TreeMap());}, false, nulls)},686new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls))}687);688}689690private static Collection<Object[]> makeRWNoNullsMaps() {691return Arrays.asList(692// null key and value hostile693new Object[]{"Hashtable", makeMap(Hashtable::new, false, false)},694new Object[]{"ConcurrentHashMap", makeMap(ConcurrentHashMap::new, false, false)},695new Object[]{"ConcurrentSkipListMap", makeMap(ConcurrentSkipListMap::new, false, false)},696new Object[]{"Collections.synchronizedMap(ConcurrentHashMap)", Collections.synchronizedMap(makeMap(ConcurrentHashMap::new, false, false))},697new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(makeMap(ConcurrentHashMap::new, false, false), IntegerEnum.class, String.class)},698new Object[]{"ExtendsAbstractMap(ConcurrentHashMap)", makeMap(() -> {return new ExtendsAbstractMap(new ConcurrentHashMap());}, false, false)},699new Object[]{"ImplementsConcurrentMap", makeMap(ImplementsConcurrentMap::new, false, false)}700);701}702703/**704*705* @param nulls include nulls706* @return707*/708private static Collection<Object[]> makeROMaps(boolean nulls) {709return Arrays.asList(new Object[][]{710new Object[]{"Collections.unmodifiableMap(HashMap)", Collections.unmodifiableMap(makeMap(HashMap::new, nulls, nulls))}711});712}713714/**715*716* @param supplier a supplier of mutable map instances.717*718* @param nullKeys include null keys719* @param nullValues include null values720* @return721*/722private static Map<IntegerEnum, String> makeMap(Supplier<Map<IntegerEnum, String>> supplier, boolean nullKeys, boolean nullValues) {723Map<IntegerEnum, String> result = supplier.get();724725for (int each = 0; each < TEST_SIZE; each++) {726IntegerEnum key = nullKeys ? (each == 0) ? null : KEYS[each] : KEYS[each];727String value = nullValues ? (each == 0) ? null : VALUES[each] : VALUES[each];728729result.put(key, value);730}731732return result;733}734735static class Merging {736public enum Value {737ABSENT,738NULL,739OLDVALUE,740NEWVALUE,741RESULT742}743744public enum Merger implements BiFunction<String,String,String> {745UNUSED {746public String apply(String oldValue, String newValue) {747fail("should not be called");748return null;749}750},751NULL {752public String apply(String oldValue, String newValue) {753return null;754}755},756RESULT {757public String apply(String oldValue, String newValue) {758return VALUES[3];759}760},761}762}763764@DataProvider(name = "MergeCases", parallel = true)765public Iterator<Object[]> mergeCasesProvider() {766Collection<Object[]> cases = new ArrayList<>();767768cases.addAll(makeMergeTestCases());769770return cases.iterator();771}772773static Collection<Object[]> makeMergeTestCases() {774Collection<Object[]> cases = new ArrayList<>();775776for( Object[] mapParams : makeAllRWMaps() ) {777cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.ABSENT, Merging.Value.NEWVALUE, Merging.Merger.UNUSED, Merging.Value.NEWVALUE, Merging.Value.NEWVALUE });778}779780for( Object[] mapParams : makeAllRWMaps() ) {781cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NEWVALUE, Merging.Merger.NULL, Merging.Value.ABSENT, Merging.Value.NULL });782}783784for( Object[] mapParams : makeAllRWMaps() ) {785cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NEWVALUE, Merging.Merger.RESULT, Merging.Value.RESULT, Merging.Value.RESULT });786}787788return cases;789}790791public interface Thrower<T extends Throwable> {792793public void run() throws T;794}795796public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable) {797assertThrows(thrower, throwable, null);798}799800public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable, String message) {801Throwable thrown;802try {803thrower.run();804thrown = null;805} catch (Throwable caught) {806thrown = caught;807}808809assertInstance(thrown, throwable,810((null != message) ? message : "") +811" Failed to throw " + throwable.getCanonicalName());812}813814public static <T extends Throwable> void assertThrows(Class<T> throwable, String message, Thrower<T>... throwers) {815for(Thrower<T> thrower : throwers) {816assertThrows(thrower, throwable, message);817}818}819820public static void assertInstance(Object actual, Class<?> expected) {821assertInstance(expected.isInstance(actual), null);822}823824public static void assertInstance(Object actual, Class<?> expected, String message) {825assertTrue(expected.isInstance(actual), message);826}827828/**829* A simple mutable map implementation that provides only default830* implementations of all methods. ie. none of the Map interface default831* methods have overridden implementations.832*833* @param <K> Type of keys834* @param <V> Type of values835*/836public static class ExtendsAbstractMap<M extends Map<K,V>, K, V> extends AbstractMap<K, V> {837838protected final M map;839840public ExtendsAbstractMap() { this( (M) new HashMap<K,V>()); }841842protected ExtendsAbstractMap(M map) { this.map = map; }843844public Set<Map.Entry<K, V>> entrySet() {845return new AbstractSet<Map.Entry<K, V>>() {846public int size() {847return map.size();848}849850public Iterator<Map.Entry<K,V>> iterator() {851final Iterator<Map.Entry<K,V>> source = map.entrySet().iterator();852return new Iterator<Map.Entry<K,V>>() {853public boolean hasNext() { return source.hasNext(); }854public Map.Entry<K,V> next() { return source.next(); }855public void remove() { source.remove(); }856};857}858859public boolean add(Map.Entry<K,V> e) {860return map.entrySet().add(e);861}862};863}864865public V put(K key, V value) {866return map.put(key, value);867}868}869870/**871* A simple mutable concurrent map implementation that provides only default872* implementations of all methods. ie. none of the ConcurrentMap interface873* default methods have overridden implementations.874*875* @param <K> Type of keys876* @param <V> Type of values877*/878public static class ImplementsConcurrentMap<K, V> extends ExtendsAbstractMap<ConcurrentMap<K,V>, K, V> implements ConcurrentMap<K,V> {879public ImplementsConcurrentMap() { super(new ConcurrentHashMap<K,V>()); }880881// ConcurrentMap reabstracts these methods882883public V replace(K k, V v) { return map.replace(k, v); };884885public boolean replace(K k, V v, V vv) { return map.replace(k, v, vv); };886887public boolean remove(Object k, Object v) { return map.remove(k, v); }888889public V putIfAbsent(K k, V v) { return map.putIfAbsent(k, v); }890}891}892893894