1 /*
   2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8010122 8004518
  27  * @summary Test Map default methods
  28  * @author Mike Duigou
  29  * @run testng Defaults
  30  */
  31 import java.util.AbstractMap;
  32 import java.util.AbstractSet;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.Collection;
  36 import java.util.Collections;
  37 import java.util.EnumMap;
  38 import java.util.HashMap;
  39 import java.util.HashSet;
  40 import java.util.Hashtable;
  41 import java.util.IdentityHashMap;
  42 import java.util.Iterator;
  43 import java.util.LinkedHashMap;
  44 import java.util.Map;
  45 import java.util.TreeMap;
  46 import java.util.Set;
  47 import java.util.WeakHashMap;
  48 import java.util.concurrent.ConcurrentMap;
  49 import java.util.concurrent.ConcurrentHashMap;
  50 import java.util.concurrent.ConcurrentSkipListMap;
  51 import java.util.function.BiFunction;
  52 import java.util.function.Supplier;
  53 
  54 import org.testng.annotations.Test;
  55 import org.testng.annotations.DataProvider;
  56 import static org.testng.Assert.fail;
  57 import static org.testng.Assert.assertEquals;
  58 import static org.testng.Assert.assertTrue;
  59 import static org.testng.Assert.assertFalse;
  60 import static org.testng.Assert.assertNull;
  61 import static org.testng.Assert.assertSame;
  62 
  63 public class Defaults {
  64 
  65     @Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=withNull values=withNull")
  66     public void testGetOrDefaultNulls(String description, Map<IntegerEnum, String> map) {
  67         assertTrue(map.containsKey(null), description + ": null key absent");
  68         assertNull(map.get(null), description + ": value not null");
  69         assertSame(map.get(null), map.getOrDefault(null, EXTRA_VALUE), description + ": values should match");
  70     }
  71 
  72     @Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=all values=all")
  73     public void testGetOrDefault(String description, Map<IntegerEnum, String> map) {
  74         assertTrue(map.containsKey(KEYS[1]), "expected key missing");
  75         assertSame(map.get(KEYS[1]), map.getOrDefault(KEYS[1], EXTRA_VALUE), "values should match");
  76         assertFalse(map.containsKey(EXTRA_KEY), "expected absent key");
  77         assertSame(map.getOrDefault(EXTRA_KEY, EXTRA_VALUE), EXTRA_VALUE, "value not returned as default");
  78         assertNull(map.getOrDefault(EXTRA_KEY, null), "null not returned as default");
  79     }
  80 
  81     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
  82     public void testPutIfAbsentNulls(String description, Map<IntegerEnum, String> map) {
  83         assertTrue(map.containsKey(null), "null key absent");
  84         assertNull(map.get(null), "value not null");
  85         assertNull(map.putIfAbsent(null, EXTRA_VALUE), "previous not null");
  86         assertTrue(map.containsKey(null), "null key absent");
  87         assertSame(map.get(null), EXTRA_VALUE, "unexpected value");
  88         assertSame(map.putIfAbsent(null, null), EXTRA_VALUE, "previous not expected value");
  89         assertTrue(map.containsKey(null), "null key absent");
  90         assertSame(map.get(null), EXTRA_VALUE, "unexpected value");
  91         assertSame(map.remove(null), EXTRA_VALUE, "removed unexpected value");
  92 
  93         assertFalse(map.containsKey(null), description + ": key present after remove");
  94         assertNull(map.putIfAbsent(null, null), "previous not null");
  95         assertTrue(map.containsKey(null), "null key absent");
  96         assertNull(map.get(null), "value not null");
  97         assertNull(map.putIfAbsent(null, EXTRA_VALUE), "previous not null");
  98         assertSame(map.get(null), EXTRA_VALUE, "value not expected");
  99     }
 100 
 101     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 102     public void testPutIfAbsent(String description, Map<IntegerEnum, String> map) {
 103         assertTrue(map.containsKey(KEYS[1]));
 104         Object expected = map.get(KEYS[1]);
 105         assertTrue(null == expected || expected == VALUES[1]);
 106         assertSame(map.putIfAbsent(KEYS[1], EXTRA_VALUE), expected);
 107         assertSame(map.get(KEYS[1]), expected);
 108 
 109         assertFalse(map.containsKey(EXTRA_KEY));
 110         assertSame(map.putIfAbsent(EXTRA_KEY, EXTRA_VALUE), null);
 111         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 112     }
 113 
 114     @Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=all values=all")
 115     public void testForEach(String description, Map<IntegerEnum, String> map) {
 116         IntegerEnum[] EACH_KEY = new IntegerEnum[map.size()];
 117 
 118         map.forEach((k, v) -> {
 119             int idx = (null == k) ? 0 : k.ordinal(); // substitute for index.
 120             assertNull(EACH_KEY[idx]);
 121             EACH_KEY[idx] = (idx == 0) ? KEYS[0] : k; // substitute for comparison.
 122             assertSame(v, map.get(k));
 123         });
 124 
 125         assertEquals(KEYS, EACH_KEY, description);
 126     }
 127 
 128     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 129     public static void testReplaceAll(String description, Map<IntegerEnum, String> map) {
 130         IntegerEnum[] EACH_KEY = new IntegerEnum[map.size()];
 131         Set<String> EACH_REPLACE = new HashSet<>(map.size());
 132 
 133         map.replaceAll((k,v) -> {
 134             int idx = (null == k) ? 0 : k.ordinal(); // substitute for index.
 135             assertNull(EACH_KEY[idx]);
 136             EACH_KEY[idx] = (idx == 0) ? KEYS[0] : k; // substitute for comparison.
 137             assertSame(v, map.get(k));
 138             String replacement = v + " replaced";
 139             EACH_REPLACE.add(replacement);
 140             return replacement;
 141         });
 142 
 143         assertEquals(KEYS, EACH_KEY, description);
 144         assertEquals(map.values().size(), EACH_REPLACE.size(), description + EACH_REPLACE);
 145         assertTrue(EACH_REPLACE.containsAll(map.values()), description + " : " + EACH_REPLACE + " != " + map.values());
 146         assertTrue(map.values().containsAll(EACH_REPLACE), description + " : " + EACH_REPLACE + " != " + map.values());
 147     }
 148 
 149     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")
 150     public static void testReplaceAllNoNullReplacement(String description, Map<IntegerEnum, String> map) {
 151         assertThrows(
 152             () -> { map.replaceAll(null); },
 153             NullPointerException.class,
 154             description);
 155         assertThrows(
 156             () -> { map.replaceAll((k,v) -> null); },
 157             NullPointerException.class,
 158             description + " should not allow replacement with null value");
 159     }
 160 
 161     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 162     public static void testRemoveNulls(String description, Map<IntegerEnum, String> map) {
 163         assertTrue(map.containsKey(null), "null key absent");
 164         assertNull(map.get(null), "value not null");
 165         assertFalse(map.remove(null, EXTRA_VALUE), description);
 166         assertTrue(map.containsKey(null));
 167         assertNull(map.get(null));
 168         assertTrue(map.remove(null, null));
 169         assertFalse(map.containsKey(null));
 170         assertNull(map.get(null));
 171         assertFalse(map.remove(null, null));
 172     }
 173 
 174     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 175     public static void testRemove(String description, Map<IntegerEnum, String> map) {
 176         assertTrue(map.containsKey(KEYS[1]));
 177         Object expected = map.get(KEYS[1]);
 178         assertTrue(null == expected || expected == VALUES[1]);
 179         assertFalse(map.remove(KEYS[1], EXTRA_VALUE), description);
 180         assertSame(map.get(KEYS[1]), expected);
 181         assertTrue(map.remove(KEYS[1], expected));
 182         assertNull(map.get(KEYS[1]));
 183         assertFalse(map.remove(KEYS[1], expected));
 184 
 185         assertFalse(map.containsKey(EXTRA_KEY));
 186         assertFalse(map.remove(EXTRA_KEY, EXTRA_VALUE));
 187     }
 188 
 189     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 190     public void testReplaceKVNulls(String description, Map<IntegerEnum, String> map) {
 191         assertTrue(map.containsKey(null), "null key absent");
 192         assertNull(map.get(null), "value not null");
 193         assertSame(map.replace(null, EXTRA_VALUE), null);
 194         assertSame(map.get(null), EXTRA_VALUE);
 195     }
 196 
 197     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")
 198     public void testReplaceKVNoNulls(String description, Map<IntegerEnum, String> map) {
 199         assertTrue(map.containsKey(FIRST_KEY), "expected key missing");
 200         assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value");
 201         assertThrows( () -> {map.replace(FIRST_KEY, null);}, NullPointerException.class, description + ": should throw NPE");
 202         assertSame(map.replace(FIRST_KEY, EXTRA_VALUE), FIRST_VALUE, description + ": replaced wrong value");
 203         assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value");
 204     }
 205 
 206     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 207     public void testReplaceKV(String description, Map<IntegerEnum, String> map) {
 208         assertTrue(map.containsKey(KEYS[1]));
 209         Object expected = map.get(KEYS[1]);
 210         assertTrue(null == expected || expected == VALUES[1]);
 211         assertSame(map.replace(KEYS[1], EXTRA_VALUE), expected);
 212         assertSame(map.get(KEYS[1]), EXTRA_VALUE);
 213 
 214         assertFalse(map.containsKey(EXTRA_KEY));
 215         assertNull(map.replace(EXTRA_KEY, EXTRA_VALUE));
 216         assertFalse(map.containsKey(EXTRA_KEY));
 217         assertNull(map.get(EXTRA_KEY));
 218         assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));
 219         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 220         assertSame(map.replace(EXTRA_KEY, (String)expected), EXTRA_VALUE);
 221         assertSame(map.get(EXTRA_KEY), expected);
 222     }
 223 
 224     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 225     public void testReplaceKVVNulls(String description, Map<IntegerEnum, String> map) {
 226         assertTrue(map.containsKey(null), "null key absent");
 227         assertNull(map.get(null), "value not null");
 228         assertFalse(map.replace(null, EXTRA_VALUE, EXTRA_VALUE));
 229         assertNull(map.get(null));
 230         assertTrue(map.replace(null, null, EXTRA_VALUE));
 231         assertSame(map.get(null), EXTRA_VALUE);
 232         assertTrue(map.replace(null, EXTRA_VALUE, EXTRA_VALUE));
 233         assertSame(map.get(null), EXTRA_VALUE);
 234     }
 235 
 236     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")
 237     public void testReplaceKVVNoNulls(String description, Map<IntegerEnum, String> map) {
 238         assertTrue(map.containsKey(FIRST_KEY), "expected key missing");
 239         assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value");
 240         assertThrows( () -> {map.replace(FIRST_KEY, FIRST_VALUE, null);}, NullPointerException.class, description + ": should throw NPE");
 241         assertThrows( () -> {if (!map.replace(FIRST_KEY, null, EXTRA_VALUE)) throw new NullPointerException("default returns false rather than throwing");}, NullPointerException.class,  description + ": should throw NPE");
 242         assertTrue(map.replace(FIRST_KEY, FIRST_VALUE, EXTRA_VALUE), description + ": replaced wrong value");
 243         assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value");
 244     }
 245 
 246     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 247     public void testReplaceKVV(String description, Map<IntegerEnum, String> map) {
 248         assertTrue(map.containsKey(KEYS[1]));
 249         Object expected = map.get(KEYS[1]);
 250         assertTrue(null == expected || expected == VALUES[1]);
 251         assertFalse(map.replace(KEYS[1], EXTRA_VALUE, EXTRA_VALUE));
 252         assertSame(map.get(KEYS[1]), expected);
 253         assertTrue(map.replace(KEYS[1], (String)expected, EXTRA_VALUE));
 254         assertSame(map.get(KEYS[1]), EXTRA_VALUE);
 255         assertTrue(map.replace(KEYS[1], EXTRA_VALUE, EXTRA_VALUE));
 256         assertSame(map.get(KEYS[1]), EXTRA_VALUE);
 257 
 258         assertFalse(map.containsKey(EXTRA_KEY));
 259         assertFalse(map.replace(EXTRA_KEY, EXTRA_VALUE, EXTRA_VALUE));
 260         assertFalse(map.containsKey(EXTRA_KEY));
 261         assertNull(map.get(EXTRA_KEY));
 262         assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));
 263         assertTrue(map.containsKey(EXTRA_KEY));
 264         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 265         assertTrue(map.replace(EXTRA_KEY, EXTRA_VALUE, EXTRA_VALUE));
 266         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 267     }
 268 
 269     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 270     public void testComputeIfAbsentNulls(String description, Map<IntegerEnum, String> map) {
 271         assertTrue(map.containsKey(null), "null key absent");
 272         assertNull(map.get(null), "value not null");
 273         assertSame(map.computeIfAbsent(null, (k) -> EXTRA_VALUE), EXTRA_VALUE, description);
 274         assertSame(map.get(null), EXTRA_VALUE, description);
 275     }
 276 
 277     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 278     public void testComputeIfAbsent(String description, Map<IntegerEnum, String> map) {
 279         assertTrue(map.containsKey(KEYS[1]));
 280         Object expected = map.get(KEYS[1]);
 281         assertTrue(null == expected || expected == VALUES[1], description + String.valueOf(expected));
 282         expected = (null == expected) ? EXTRA_VALUE : expected;
 283         assertSame(map.computeIfAbsent(KEYS[1], (k) -> EXTRA_VALUE), expected, description);
 284         assertSame(map.get(KEYS[1]), expected, description);
 285 
 286         assertFalse(map.containsKey(EXTRA_KEY));
 287         assertSame(map.computeIfAbsent(EXTRA_KEY, (k) -> EXTRA_VALUE), EXTRA_VALUE);
 288         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 289     }
 290 
 291     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 292     public void testComputeIfPresentNulls(String description, Map<IntegerEnum, String> map) {
 293         assertTrue(map.containsKey(null), description + ": null key absent");
 294         assertNull(map.get(null), description + ": value not null");
 295         assertSame(map.computeIfPresent(null, (k, v) -> {
 296             fail(description + ": null value is not deemed present");
 297             return EXTRA_VALUE;
 298         }), null, description);
 299         assertTrue(map.containsKey(null));
 300         assertNull(map.get(null), description);
 301         assertNull(map.remove(EXTRA_KEY), description + ": unexpected mapping");
 302         assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value");
 303         assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> {
 304             fail(description + ": null value is not deemed present");
 305             return EXTRA_VALUE;
 306         }), null, description);
 307         assertNull(map.get(EXTRA_KEY), description + ": null mapping gone");
 308     }
 309 
 310     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 311     public void testComputeIfPresent(String description, Map<IntegerEnum, String> map) {
 312         assertTrue(map.containsKey(KEYS[1]));
 313         Object value = map.get(KEYS[1]);
 314         assertTrue(null == value || value == VALUES[1], description + String.valueOf(value));
 315         Object expected = (null == value) ? null : EXTRA_VALUE;
 316         assertSame(map.computeIfPresent(KEYS[1], (k, v) -> {
 317             assertSame(v, value);
 318             return EXTRA_VALUE;
 319         }), expected, description);
 320         assertSame(map.get(KEYS[1]), expected, description);
 321 
 322         assertFalse(map.containsKey(EXTRA_KEY));
 323         assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> {
 324             fail();
 325             return EXTRA_VALUE;
 326         }), null);
 327         assertFalse(map.containsKey(EXTRA_KEY));
 328         assertSame(map.get(EXTRA_KEY), null);
 329     }
 330 
 331     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 332     public void testComputeNulls(String description, Map<IntegerEnum, String> map) {
 333         assertTrue(map.containsKey(null), "null key absent");
 334         assertNull(map.get(null), "value not null");
 335         assertSame(map.compute(null, (k, v) -> {
 336             assertNull(k);
 337             assertNull(v);
 338             return null;
 339         }), null, description);
 340         assertFalse(map.containsKey(null), description + ": null key present.");
 341         assertSame(map.compute(null, (k, v) -> {
 342             assertSame(k, null);
 343             assertNull(v);
 344             return EXTRA_VALUE;
 345         }), EXTRA_VALUE, description);
 346         assertTrue(map.containsKey(null));
 347         assertSame(map.get(null), EXTRA_VALUE, description);
 348         assertSame(map.remove(null), EXTRA_VALUE, description + ": removed value not expected");
 349         // no mapping before and after
 350         assertFalse(map.containsKey(null), description + ": null key present");
 351         assertSame(map.compute(null, (k, v) -> {
 352             assertNull(k);
 353             assertNull(v);
 354             return null;
 355         }), null, description + ": expected null result" );
 356         assertFalse(map.containsKey(null), description + ": null key present");
 357         // compute with map not containing value
 358         assertNull(map.remove(EXTRA_KEY),  description + ": unexpected mapping");
 359         assertFalse(map.containsKey(EXTRA_KEY),  description + ": key present");
 360         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 361             assertSame(k, EXTRA_KEY);
 362             assertNull(v);
 363             return null;
 364         }), null, description);
 365         assertFalse(map.containsKey(EXTRA_KEY),  description + ": null key present");
 366         // ensure removal.
 367         assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));
 368         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 369             assertSame(k, EXTRA_KEY);
 370             assertSame(v, EXTRA_VALUE);
 371             return null;
 372         }), null, description + ": null resulted expected");
 373         assertFalse(map.containsKey(EXTRA_KEY),  description + ": null key present");
 374        // compute with map containing null value
 375         assertNull(map.put(EXTRA_KEY, null),  description + ": unexpected value");
 376         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 377             assertSame(k, EXTRA_KEY);
 378             assertNull(v);
 379             return null;
 380         }), null, description);
 381         assertFalse(map.containsKey(EXTRA_KEY),  description + ": null key present");
 382         assertNull(map.put(EXTRA_KEY, null),  description + ": unexpected value");
 383         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 384             assertSame(k, EXTRA_KEY);
 385             assertNull(v);
 386             return EXTRA_VALUE;
 387         }), EXTRA_VALUE, description);
 388         assertTrue(map.containsKey(EXTRA_KEY), "null key present");
 389     }
 390 
 391     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 392     public void testCompute(String description, Map<IntegerEnum, String> map) {
 393         assertTrue(map.containsKey(KEYS[1]));
 394         Object value = map.get(KEYS[1]);
 395         assertTrue(null == value || value == VALUES[1], description + String.valueOf(value));
 396         assertSame(map.compute(KEYS[1], (k, v) -> {
 397             assertSame(k, KEYS[1]);
 398             assertSame(v, value);
 399             return EXTRA_VALUE;
 400         }), EXTRA_VALUE, description);
 401         assertSame(map.get(KEYS[1]), EXTRA_VALUE, description);
 402         assertNull(map.compute(KEYS[1], (k, v) -> {
 403             assertSame(v, EXTRA_VALUE);
 404             return null;
 405         }), description);
 406         assertFalse(map.containsKey(KEYS[1]));
 407 
 408         assertFalse(map.containsKey(EXTRA_KEY));
 409         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 410             assertNull(v);
 411             return EXTRA_VALUE;
 412         }), EXTRA_VALUE);
 413         assertTrue(map.containsKey(EXTRA_KEY));
 414         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 415     }
 416 
 417 
 418     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 419     public void testMergeNulls(String description, Map<IntegerEnum, String> map) {
 420         assertTrue(map.containsKey(null), "null key absent");
 421         assertNull(map.get(null), "value not null");
 422         assertSame(map.merge(null, EXTRA_VALUE, (v, vv) -> {
 423             assertNull(v);
 424             assertSame(vv, EXTRA_VALUE);
 425             return vv;
 426         }), EXTRA_VALUE, description);
 427         assertTrue(map.containsKey(null));
 428         assertSame(map.get(null), EXTRA_VALUE, description);
 429     }
 430 
 431     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 432     public void testMerge(String description, Map<IntegerEnum, String> map) {
 433         assertTrue(map.containsKey(KEYS[1]));
 434         Object value = map.get(KEYS[1]);
 435         assertTrue(null == value || value == VALUES[1], description + String.valueOf(value));
 436         assertSame(map.merge(KEYS[1], EXTRA_VALUE, (v, vv) -> {
 437             assertSame(v, value);
 438             assertSame(vv, EXTRA_VALUE);
 439             return vv;
 440         }), EXTRA_VALUE, description);
 441         assertSame(map.get(KEYS[1]), EXTRA_VALUE, description);
 442         assertNull(map.merge(KEYS[1], EXTRA_VALUE, (v, vv) -> {
 443             assertSame(v, EXTRA_VALUE);
 444             assertSame(vv, EXTRA_VALUE);
 445             return null;
 446         }), description);
 447         assertFalse(map.containsKey(KEYS[1]));
 448 
 449         assertFalse(map.containsKey(EXTRA_KEY));
 450         assertSame(map.merge(EXTRA_KEY, EXTRA_VALUE, (v, vv) -> {
 451             assertNull(v);
 452             assertSame(vv, EXTRA_VALUE);
 453             return EXTRA_VALUE;
 454         }), EXTRA_VALUE);
 455         assertTrue(map.containsKey(EXTRA_KEY));
 456         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 457     }
 458 
 459     enum IntegerEnum {
 460 
 461         e0, e1, e2, e3, e4, e5, e6, e7, e8, e9,
 462         e10, e11, e12, e13, e14, e15, e16, e17, e18, e19,
 463         e20, e21, e22, e23, e24, e25, e26, e27, e28, e29,
 464         e30, e31, e32, e33, e34, e35, e36, e37, e38, e39,
 465         e40, e41, e42, e43, e44, e45, e46, e47, e48, e49,
 466         e50, e51, e52, e53, e54, e55, e56, e57, e58, e59,
 467         e60, e61, e62, e63, e64, e65, e66, e67, e68, e69,
 468         e70, e71, e72, e73, e74, e75, e76, e77, e78, e79,
 469         e80, e81, e82, e83, e84, e85, e86, e87, e88, e89,
 470         e90, e91, e92, e93, e94, e95, e96, e97, e98, e99,
 471         EXTRA_KEY;
 472         public static final int SIZE = values().length;
 473     };
 474     private static final int TEST_SIZE = IntegerEnum.SIZE - 1;
 475     /**
 476      * Realized keys ensure that there is always a hard ref to all test objects.
 477      */
 478     private static final IntegerEnum[] KEYS = new IntegerEnum[TEST_SIZE];
 479     /**
 480      * Realized values ensure that there is always a hard ref to all test
 481      * objects.
 482      */
 483     private static final String[] VALUES = new String[TEST_SIZE];
 484 
 485     static {
 486         IntegerEnum[] keys = IntegerEnum.values();
 487         for (int each = 0; each < TEST_SIZE; each++) {
 488             KEYS[each] = keys[each];
 489             VALUES[each] = String.valueOf(each);
 490         }
 491     }
 492 
 493     private static final IntegerEnum FIRST_KEY = KEYS[0];
 494     private static final String FIRST_VALUE = VALUES[0];
 495     private static final IntegerEnum EXTRA_KEY = IntegerEnum.EXTRA_KEY;
 496     private static final String EXTRA_VALUE = String.valueOf(TEST_SIZE);
 497 
 498     @DataProvider(name = "Map<IntegerEnum,String> rw=all keys=all values=all", parallel = true)
 499     public static Iterator<Object[]> allMapProvider() {
 500         return makeAllMaps().iterator();
 501     }
 502 
 503     @DataProvider(name = "Map<IntegerEnum,String> rw=all keys=withNull values=withNull", parallel = true)
 504     public static Iterator<Object[]> allMapWithNullsProvider() {
 505         return makeAllMapsWithNulls().iterator();
 506     }
 507 
 508     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull", parallel = true)
 509     public static Iterator<Object[]> rwNonNullMapProvider() {
 510         return makeRWNoNullsMaps().iterator();
 511     }
 512 
 513     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=all", parallel = true)
 514     public static Iterator<Object[]> rwNonNullKeysMapProvider() {
 515         return makeRWMapsNoNulls().iterator();
 516     }
 517 
 518     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=all values=all", parallel = true)
 519     public static Iterator<Object[]> rwMapProvider() {
 520         return makeAllRWMaps().iterator();
 521     }
 522 
 523     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull", parallel = true)
 524     public static Iterator<Object[]> rwNullsMapProvider() {
 525         return makeAllRWMapsWithNulls().iterator();
 526     }
 527 
 528     private static Collection<Object[]> makeAllRWMapsWithNulls() {
 529         Collection<Object[]> all = new ArrayList<>();
 530 
 531         all.addAll(makeRWMaps(true, true));
 532 
 533         return all;
 534     }
 535 
 536 
 537     private static Collection<Object[]> makeRWMapsNoNulls() {
 538         Collection<Object[]> all = new ArrayList<>();
 539 
 540         all.addAll(makeRWNoNullKeysMaps(false));
 541         all.addAll(makeRWNoNullsMaps());
 542 
 543         return all;
 544     }
 545 
 546     private static Collection<Object[]> makeAllROMaps() {
 547         Collection<Object[]> all = new ArrayList<>();
 548 
 549         all.addAll(makeROMaps(false));
 550         all.addAll(makeROMaps(true));
 551 
 552         return all;
 553     }
 554 
 555     private static Collection<Object[]> makeAllRWMaps() {
 556         Collection<Object[]> all = new ArrayList<>();
 557 
 558         all.addAll(makeRWNoNullsMaps());
 559         all.addAll(makeRWMaps(false,true));
 560         all.addAll(makeRWMaps(true,true));
 561         all.addAll(makeRWNoNullKeysMaps(true));
 562         return all;
 563     }
 564 
 565     private static Collection<Object[]> makeAllMaps() {
 566         Collection<Object[]> all = new ArrayList<>();
 567 
 568         all.addAll(makeAllROMaps());
 569         all.addAll(makeAllRWMaps());
 570 
 571         return all;
 572     }
 573 
 574     private static Collection<Object[]> makeAllMapsWithNulls() {
 575         Collection<Object[]> all = new ArrayList<>();
 576 
 577         all.addAll(makeROMaps(true));
 578         all.addAll(makeRWMaps(true,true));
 579 
 580         return all;
 581     }
 582     /**
 583      *
 584      * @param nullKeys include null keys
 585      * @param nullValues include null values
 586      * @return
 587      */
 588     private static Collection<Object[]> makeRWMaps(boolean nullKeys, boolean nullValues) {
 589         return Arrays.asList(
 590             new Object[]{"HashMap", makeMap(HashMap::new, nullKeys, nullValues)},
 591             new Object[]{"IdentityHashMap", makeMap(IdentityHashMap::new, nullKeys, nullValues)},
 592             new Object[]{"LinkedHashMap", makeMap(LinkedHashMap::new, nullKeys, nullValues)},
 593             new Object[]{"WeakHashMap", makeMap(WeakHashMap::new, nullKeys, nullValues)},
 594             new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(makeMap(HashMap::new, nullKeys, nullValues), IntegerEnum.class, String.class)},
 595             new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(makeMap(HashMap::new, nullKeys, nullValues))},
 596             new Object[]{"ExtendsAbstractMap", makeMap(ExtendsAbstractMap::new, nullKeys, nullValues)});
 597     }
 598 
 599     /**
 600      *
 601      * @param nulls include null values
 602      * @return
 603      */
 604     private static Collection<Object[]> makeRWNoNullKeysMaps(boolean nulls) {
 605         return Arrays.asList(
 606                 // null key hostile
 607                 new Object[]{"EnumMap", makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls)},
 608                 new Object[]{"TreeMap", makeMap(TreeMap::new, false, nulls)},
 609                 new Object[]{"ExtendsAbstractMap(TreeMap)", makeMap(() -> {return new ExtendsAbstractMap(new TreeMap());}, false, nulls)},
 610                 new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls))}
 611                 );
 612     }
 613 
 614     private static Collection<Object[]> makeRWNoNullsMaps() {
 615         return Arrays.asList(
 616             // null key and value hostile
 617             new Object[]{"Hashtable", makeMap(Hashtable::new, false, false)},
 618             new Object[]{"ConcurrentHashMap", makeMap(ConcurrentHashMap::new, false, false)},
 619             new Object[]{"ConcurrentSkipListMap", makeMap(ConcurrentSkipListMap::new, false, false)},
 620             new Object[]{"Collections.synchronizedMap(ConcurrentHashMap)", Collections.synchronizedMap(makeMap(ConcurrentHashMap::new, false, false))},
 621             new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(makeMap(ConcurrentHashMap::new, false, false), IntegerEnum.class, String.class)},
 622             new Object[]{"ExtendsAbstractMap(ConcurrentHashMap)", makeMap(() -> {return new ExtendsAbstractMap(new ConcurrentHashMap());}, false, false)},
 623             new Object[]{"ImplementsConcurrentMap", makeMap(ImplementsConcurrentMap::new, false, false)}
 624             );
 625     }
 626 
 627     /**
 628      *
 629      * @param nulls include nulls
 630      * @return
 631      */
 632     private static Collection<Object[]> makeROMaps(boolean nulls) {
 633         return Arrays.asList(new Object[][]{
 634             new Object[]{"Collections.unmodifiableMap(HashMap)", Collections.unmodifiableMap(makeMap(HashMap::new, nulls, nulls))}
 635         });
 636     }
 637 
 638      /**
 639      *
 640      * @param supplier a supplier of mutable map instances.
 641      *
 642      * @param nullKeys   include null keys
 643      * @param nullValues include null values
 644      * @return
 645      */
 646     private static Map<IntegerEnum, String> makeMap(Supplier<Map<IntegerEnum, String>> supplier, boolean nullKeys, boolean nullValues) {
 647         Map<IntegerEnum, String> result = supplier.get();
 648 
 649         for (int each = 0; each < TEST_SIZE; each++) {
 650             IntegerEnum key = nullKeys ? (each == 0) ? null : KEYS[each] : KEYS[each];
 651             String value = nullValues ? (each == 0) ? null : VALUES[each] : VALUES[each];
 652 
 653             result.put(key, value);
 654         }
 655 
 656         return result;
 657     }
 658 
 659     public interface Thrower<T extends Throwable> {
 660 
 661         public void run() throws T;
 662     }
 663 
 664     public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable) {
 665         assertThrows(thrower, throwable, null);
 666     }
 667 
 668     public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable, String message) {
 669         Throwable thrown;
 670         try {
 671             thrower.run();
 672             thrown = null;
 673         } catch (Throwable caught) {
 674             thrown = caught;
 675         }
 676 
 677         assertInstance(thrown, throwable,
 678             ((null != message) ? message : "") +
 679             " Failed to throw " + throwable.getCanonicalName());
 680     }
 681 
 682     public static <T extends Throwable> void assertThrows(Class<T> throwable, String message, Thrower<T>... throwers) {
 683         for(Thrower<T> thrower : throwers) {
 684             assertThrows(thrower, throwable, message);
 685         }
 686     }
 687 
 688     public static void assertInstance(Object actual, Class<?> expected) {
 689         assertInstance(expected.isInstance(actual), null);
 690     }
 691 
 692     public static void assertInstance(Object actual, Class<?> expected, String message) {
 693         assertTrue(expected.isInstance(actual), message);
 694     }
 695 
 696     /**
 697      * A simple mutable map implementation that provides only default
 698      * implementations of all methods. ie. none of the Map interface default
 699      * methods have overridden implementations.
 700      *
 701      * @param <K> Type of keys
 702      * @param <V> Type of values
 703      */
 704     public static class ExtendsAbstractMap<M extends Map<K,V>, K, V> extends AbstractMap<K, V> {
 705 
 706         protected final M map;
 707 
 708         public ExtendsAbstractMap() { this( (M) new HashMap<K,V>()); }
 709 
 710         protected ExtendsAbstractMap(M map) { this.map = map; }
 711 
 712         public Set<Map.Entry<K, V>> entrySet() {
 713             return new AbstractSet<Map.Entry<K, V>>() {
 714                 public int size() {
 715                     return map.size();
 716                 }
 717 
 718                 public Iterator<Map.Entry<K,V>> iterator() {
 719                     final Iterator<Map.Entry<K,V>> source = map.entrySet().iterator();
 720                     return new Iterator<Map.Entry<K,V>>() {
 721                        public boolean hasNext() { return source.hasNext(); }
 722                        public Map.Entry<K,V> next() { return source.next(); }
 723                        public void remove() { source.remove(); }
 724                     };
 725                 }
 726 
 727                 public boolean add(Map.Entry<K,V> e) {
 728                     return map.entrySet().add(e);
 729                 }
 730             };
 731         }
 732 
 733         public V put(K key, V value) {
 734             return map.put(key, value);
 735         }
 736     }
 737 
 738     /**
 739      * A simple mutable concurrent map implementation that provides only default
 740      * implementations of all methods. ie. none of the ConcurrentMap interface
 741      * default methods have overridden implementations.
 742      *
 743      * @param <K> Type of keys
 744      * @param <V> Type of values
 745      */
 746     public static class ImplementsConcurrentMap<K, V> extends ExtendsAbstractMap<ConcurrentMap<K,V>, K, V> implements ConcurrentMap<K,V> {
 747         public ImplementsConcurrentMap() { super(new ConcurrentHashMap<K,V>()); }
 748 
 749         // ConcurrentMap reabstracts these methods
 750 
 751         public V replace(K k, V v) { return map.replace(k, v); };
 752 
 753         public boolean replace(K k, V v, V vv) { return map.replace(k, v, vv); };
 754 
 755         public boolean remove(Object k, Object v) { return map.remove(k, v); }
 756 
 757         public V putIfAbsent(K k, V v) { return map.putIfAbsent(k, v); }
 758     }
 759 }